Backup and migration over any Storage adapter, built on the protocol's log-scan half: export the log as EDN (one entry per line), import it into fresh or continuing storage, copy between backends, and verify that two storages hold the same log.
Log entries are the raw storage shape {:t t :datoms [[e a v tx added] ...]} — entity ids and attr ids as plain longs, no ident resolution — so a restored store replays exactly like the original and entity ids stay stable. Value classes without a readable EDN literal ride tagged literals: #kala/float, #kala/bytes (base64), #kala/uri. Everything else (instants, uuids, bigints, keywords, ...) round-trips through native EDN; an unknown tag fails the import rather than silently corrupting a value.
The offline story stays true too: copying a quiesced file store's directory is a valid backup — see docs/backup-and-migration.md for both procedures.
Backup and migration over any Storage adapter, built on the protocol's
log-scan half: export the log as EDN (one entry per line), import it into
fresh or continuing storage, copy between backends, and verify that two
storages hold the same log.
Log entries are the raw storage shape {:t t :datoms [[e a v tx added] ...]}
— entity ids and attr ids as plain longs, no ident resolution — so a
restored store replays exactly like the original and entity ids stay
stable. Value classes without a readable EDN literal ride tagged
literals: #kala/float, #kala/bytes (base64), #kala/uri. Everything else
(instants, uuids, bigints, keywords, ...) round-trips through native EDN;
an unknown tag fails the import rather than silently corrupting a value.
The offline story stays true too: copying a quiesced file store's
directory is a valid backup — see docs/backup-and-migration.md for both
procedures.(copy-storage from to)Copy the complete log of one storage into another through a temporary export file — migration between backends, restore into fresh storage. The target continues its own t sequence: copying over a non-empty store whose last t reaches an entry's t raises :storage/non-monotonic-t. Returns the import report.
Copy the complete log of one storage into another through a temporary export file — migration between backends, restore into fresh storage. The target continues its own t sequence: copying over a non-empty store whose last t reaches an entry's t raises :storage/non-monotonic-t. Returns the import report.
(export-log storage out)(export-log storage
out
{:keys [from-t to-t] :or {from-t 0 to-t Long/MAX_VALUE}})Write the storage log as EDN, one entry per line, to out (a path, file, stream, or Writer). :from-t/:to-t bound the range; the default is the whole log. Returns {:entries n :from-t :to-t} (nil bounds for an empty log). The output is the migration format: import-log consumes it, and a re-export of an imported store is byte-identical.
Write the storage log as EDN, one entry per line, to out (a path, file,
stream, or Writer). :from-t/:to-t bound the range; the default is the
whole log. Returns {:entries n :from-t :to-t} (nil bounds for an empty
log). The output is the migration format: import-log consumes it, and a
re-export of an imported store is byte-identical.(import-log storage in)Append log entries read from in (a path, file, stream, or Reader) into storage. Entries append in file order and the backend's monotonicity guard holds the line: importing over a store whose last t reaches an entry's t raises :storage/non-monotonic-t, so an overlap can never silently overwrite. Returns {:entries n :from-t :to-t}.
Append log entries read from in (a path, file, stream, or Reader) into
storage. Entries append in file order and the backend's monotonicity
guard holds the line: importing over a store whose last t reaches an
entry's t raises :storage/non-monotonic-t, so an overlap can never
silently overwrite. Returns {:entries n :from-t :to-t}.(verify-storage a b)(verify-storage a b {:keys [from-t to-t] :or {from-t 0 to-t Long/MAX_VALUE}})Do two storages hold the same log (within :from-t/:to-t, default the whole log)? Returns {:match? true :entries n :from-t :to-t}, or {:match? false :reason :entry-count | :t-sequence | :entry-content} with the first divergence named — :at-t where the content or sequence splits, :expected/:actual counts for the count case. Verification reads both logs in full; it never mutates either side.
Do two storages hold the same log (within :from-t/:to-t, default the
whole log)? Returns {:match? true :entries n :from-t :to-t}, or
{:match? false :reason :entry-count | :t-sequence | :entry-content} with
the first divergence named — :at-t where the content or sequence splits,
:expected/:actual counts for the count case. Verification reads both
logs in full; it never mutates either side.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 |