Syncopate — a production-quality ragtime adaptor for Datalevin.
Public surface:
store construct the ragtime DataStore (re-exported from
syncopate.store).load-resources build migrations from EDN/CLJ files on the classpath.->migration build a single migration from a spec map.pending / status observability without running anything.Migrations are EDN-first. A migration spec is a map:
{:id "0002-split-names" ; optional; defaults to the filename :up <step | [step ...]> :down <step | [step ...]> ; optional if :up is purely additive :transaction? true ; optional, default true :irreversible? false} ; optional
A step is one of:
transact! dataSugar:
:up/:down may be a single step instead of a vector.:down and it is derived automatically iff every :up step is a
:schema/create (adds → removes). Otherwise supply :down, or set
:irreversible? true to opt out of rollback with a clear error.The obsolete bare :schema step (ambiguous between create and alter) is
rejected with an actionable error — see ->migration.
Syncopate — a production-quality ragtime adaptor for Datalevin.
Public surface:
- `store` construct the ragtime DataStore (re-exported from
`syncopate.store`).
- `load-resources` build migrations from EDN/CLJ files on the classpath.
- `->migration` build a single migration from a spec map.
- `pending` / `status` observability without running anything.
Migrations are EDN-first. A migration spec is a map:
{:id "0002-split-names" ; optional; defaults to the filename
:up <step | [step ...]>
:down <step | [step ...]> ; optional if :up is purely additive
:transaction? true ; optional, default true
:irreversible? false} ; optional
A *step* is one of:
- {:schema/create {attr def ...}} add NEW attributes (auto-invertible)
- {:schema/alter {attr def ...}} modify existing attrs (needs :down)
- {:schema/remove [attr ...]} retract + drop attrs (needs :down)
- {:tx [tx-data ...]} raw `transact!` data
- a fully-qualified symbol resolved to (fn [conn] ...) and called
- (in .clj files) an actual fn called as (fn [conn] ...)
Sugar:
- `:up`/`:down` may be a single step instead of a vector.
- Omit `:down` and it is derived automatically iff every `:up` step is a
`:schema/create` (adds → removes). Otherwise supply `:down`, or set
`:irreversible? true` to opt out of rollback with a clear error.
The obsolete bare `:schema` step (ambiguous between create and alter) is
rejected with an actionable error — see `->migration`.Declarative schema operations for Datalevin migrations.
A schema step carries exactly one operation, along two orthogonal axes:
existence axis (does the attribute exist at all):
:schema/create - map of attr -> definition to add NEW attributes.
Auto-invertible (its :down drops them again), which powers
the auto-:down sugar in syncopate.core.
:schema/remove - collection of attrs to drop. Datalevin won't drop an attr
that still has datoms, so we retract them first. NOT
auto-invertible (the data, and prior definitions, are gone)
— supply an explicit :down or set :irreversible?.
definition axis (change an existing attribute's property keys):
:schema/alter - map of attr -> definition, merged (patched) into the
existing definition via update-schema. The attribute
keeps existing. NOT auto-invertible (the prior definition
is unknown) — supply an explicit :down (itself a
:schema/alter describing the inverse change).
:schema/create and :schema/alter apply identically at run time
(update-schema conn <map>); they differ only in declared reversibility. We
never inspect the live schema to guess intent — the declared key IS the intent.
Declarative schema operations for Datalevin migrations.
A schema step carries exactly one operation, along two orthogonal axes:
existence axis (does the attribute exist at all):
:schema/create - map of attr -> definition to add NEW attributes.
Auto-invertible (its :down drops them again), which powers
the auto-`:down` sugar in `syncopate.core`.
:schema/remove - collection of attrs to drop. Datalevin won't drop an attr
that still has datoms, so we retract them first. NOT
auto-invertible (the data, and prior definitions, are gone)
— supply an explicit :down or set :irreversible?.
definition axis (change an existing attribute's property keys):
:schema/alter - map of attr -> definition, merged (patched) into the
existing definition via `update-schema`. The attribute
keeps existing. NOT auto-invertible (the prior definition
is unknown) — supply an explicit :down (itself a
:schema/alter describing the inverse change).
`:schema/create` and `:schema/alter` apply identically at run time
(`update-schema conn <map>`); they differ only in declared reversibility. We
never inspect the live schema to guess intent — the declared key IS the intent.The ragtime DataStore half of Syncopate.
Applied-migration state lives in a dedicated key-value DBI on the same environment the app's Datalog connection already holds — never as datoms. This holds for both connection kinds:
datalevin.storage.Store's lmdb handle (Datalevin forbids a
second LMDB connection to the same directory in one process, so we can't just
open-kv).dtlv:// URI): the remote Datalog store does not expose
the KV surface, so open a KV client to the SAME server database via
open-kv. It shares the datalog connection's server environment (its DBIs sit
alongside datalevin/eav etc.), so the promise holds — a KV DBI on the same
env, never datoms. (Because that KV client is a separate session, it can't join
the datalog with-transaction, so client/server migrations record with the
two-transaction seam rather than the single-transaction fold.)The ragtime DataStore half of Syncopate. Applied-migration state lives in a dedicated key-value DBI on the *same* environment the app's Datalog connection already holds — never as datoms. This holds for both connection kinds: - **embedded** (a local LMDB directory): reuse the connection's own environment via the backing `datalevin.storage.Store`'s `lmdb` handle (Datalevin forbids a second LMDB connection to the same directory in one process, so we can't just `open-kv`). - **client/server** (a `dtlv://` URI): the remote Datalog store does not expose the KV surface, so open a KV *client* to the SAME server database via `open-kv`. It shares the datalog connection's server environment (its DBIs sit alongside `datalevin/eav` etc.), so the promise holds — a KV DBI on the same env, never datoms. (Because that KV client is a separate session, it can't join the datalog `with-transaction`, so client/server migrations record with the two-transaction seam rather than the single-transaction fold.)
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |