Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Engrams

Every mutation in patinaDB is recorded. A engram is one committed unit of change — a list of low-level delta operations (create/delete vertex, set property, set/remove label, create/delete edge) plus metadata: an id, a parent id, a timestamp, and an optional message. The chain of engrams is the source of truth for history, diffs, time travel, and — in the server — replication.

Think of it as a git-like commit log for your graph: an append-only history you can inspect, diff, travel through, tag, and compact.

How writes become engrams

Autocommit (the common case). Every write — a REST /cypher call or a Bolt RUN — is captured and committed as one engram, atomically. A single statement, however complex (MATCH … CREATE … SET …, or a bulk UNWIND … CREATE), is one engram.

Explicit transactions. Over Bolt, BEGIN … COMMIT groups several statements into one engram applied atomically at COMMIT (see Bolt). ROLLBACK discards them — no engram is written.

Chunked bulk writes. CALL { … } IN TRANSACTIONS OF n ROWS commits one engram per chunk, so a large load is versioned in bounded steps instead of one giant one — see Procedures → bulk write clause.

Listing history

CALL patinadb.engrams() YIELD id, message, timestamp
RETURN id, message, timestamp ORDER BY timestamp DESC

Snapshots & compaction

To keep time travel fast, patinaDB periodically captures a full-graph snapshot (by default every 50 commits, configurable). Reconstructing a past state loads the nearest snapshot and replays deltas forward from there, rather than replaying the whole history. Snapshots are an internal optimisation — you interact with history through engrams, diffs, tags, and time travel.

Determinism & replication

A engram is a pure, deterministic description of a change: replaying a committed op stream reproduces the exact same graph, and a created vertex’s generated UUID is baked into the op so replays are stable. This is what lets the server replicate — a write becomes a Raft log entry of delta ops, and every node applies the same ops to reach the same state.

The engram lifecycle

History is a managed asset, not just an append-only ledger. These operations let you name, protect, compact, branch, and promote points in it.

Pin — protect an engram from compaction

A pinned engram is never coalesced by squash, so the point-in-time it marks stays reachable.

CALL patinadb.pin('<engram-id>')
CALL patinadb.unpin('<engram-id>')

Tag — a named, snapshotted, pinned point

A tag is a stable name for an engram (like a git tag), so you can refer to a meaningful point without tracking raw ids. Creating a tag pins its engram so squash cannot remove it. On a Raft server, the full snapshot is built asynchronously after the tag commits. Until then, reads reconstruct the tagged state through the existing history path; snapshot completion is an acceleration, not a prerequisite for correct reads. The embedded Dataset::create_tag API still builds the snapshot synchronously.

CREATE TAG v1                                  -- tag the current HEAD
CREATE TAG release AS OF '<engram-id>'      -- tag a specific engram
SHOW TAGS                                       -- list name → engram
SHOW JOBS                                       -- admin: local snapshot job status
DROP TAG v1                                      -- remove (unpins if unreferenced)

Read a database as it was at a tag with AS OF TAG. Tags replicate across a cluster — every node names and pins the same engram, then builds its snapshot independently. History-incomplete nodes retain the existing history-availability restrictions. See maintenance jobs for status, retries and the distinction between tag commit and snapshot readiness.

For a historical tag based on an existing snapshot, the background exporter retains one private primary-data base and replays each tag’s changes in a separate overlay. It omits query indexes from that base because snapshot records contain primary data and index definitions. Ordinary historical queries still use fully indexed reconstruction; the export base is never a query store. A cached ancestor may also serve tags beyond a newer snapshot when its complete replay chain can be verified. Reuse checks the history floor, unchanged compound index definitions, and snapshot-only transitions without a replayable delta. It declines optional reuse beyond 4096 engrams or 64 MiB of decoded serialized delta work, and retains the normal nearest-snapshot path. These are work estimates, not total-memory bounds. Each delta is still loaded and applied through the ordinary checked history path. Both temporary stores use the disposable-cache cap described under configuration. This avoids building those unused indexes, but still reads and writes snapshot data and can compete with live I/O. It is not a latency guarantee or a cap on total process memory. Snapshot publication keeps its existing durable completion boundary.

Squash — compact old history

Coalesce a run of old engrams into a single synthetic genesis, keeping recent history intact. Pinned (and too-recent) engrams are boundaries squash stops at. The live graph is unchanged; only the log is compacted.

CALL patinadb.squash(10)               -- keep the 10 most recent, coalesce older
CALL patinadb.squash(10, 1719792000)   -- …only those older than a unix timestamp

Fork — branch a database at a point

Create a new database seeded with the state of another at a chosen engram (HEAD if omitted). The fork starts with a single genesis engram and its own independent history. Forking is subject to the same user-database limit as CREATE DATABASE; reaching that limit rejects the fork before replication.

FORK DATABASE prod AS OF '<engram-id>' INTO staging

Restore — promote a past state to HEAD

Bring the state at a past engram back to the live graph as a new engram. History is preserved (nothing is rewritten); the restore is an ordinary append-only write, so it replicates cleanly.

CALL patinadb.restore('<engram-id>')

This is the write-side counterpart to time travel (which is read-only): time travel reads the past, restore promotes it to the present.

Large restores are chunked. The restore delta (the creates needed to reach the target state, then the deletes for everything not in it) is proposed in batches of PATINADB_RESTORE_OPS_CHUNK operations — default 5000, and the same knob governs POST /mgmt/restore. A restore smaller than one chunk is a single atomic entry, exactly as before. A larger one commits as several entries, which has one consequence worth knowing:

A chunked restore is not atomic. Intermediate, partially-restored states are observable between chunks, and a failure part-way through leaves the graph in one of them. Restore is an offline administrative operation — do not run it against a database serving live traffic and expect a clean cut-over. All creates are ordered before all deletes, so the intermediate states are supersets of the target, never missing data the target has.

Subscribing to changes (CDC)

The engram log is also a live change source: the server’s GET /changes endpoint streams each committed engram as it applies, resumable from an engram cursor — so external systems can react to graph changes as they happen (cache invalidation, search-index sync, ETL).

In the cluster

All of the lifecycle operations are replicated control commands: every node re-derives the result from its own identical history (synthetic genesis ids are content-derived, so the ids agree on every node). SHOW TAGS / SHOW DATABASES are local reads; the mutating commands go through the leader and need admin.