Entity Snapshots
entity_snapshots is a PostgreSQL table in the causet database that stores the materialized current state of each entity. Snapshots are a performance optimization — they allow causet-runtime to evaluate rules against current state without replaying the full ledger on every intent.
What Snapshots Store
A snapshot is the current state of a single entity: the result of applying all ledger events for that entity in sequence.
CREATE TABLE entity_snapshots (
entity_id TEXT NOT NULL,
fork_id TEXT NOT NULL,
stream TEXT NOT NULL,
state JSONB NOT NULL,
version BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
PRIMARY KEY (entity_id, fork_id, stream)
);| Column | Description |
|---|---|
entity_id | The entity identifier |
fork_id | The fork this snapshot belongs to |
stream | The state stream name (entity type, e.g. user, artist) |
state | The current entity state as JSONB |
version | The sequence_number of the last ledger event applied to this snapshot |
updated_at | Timestamp of the last snapshot update |
How Snapshots Are Maintained
On every successful intent:
- Rules evaluate against the current snapshot (
stateJSONB). - Core rules produce mutations (set, increment, array_push, etc.).
- After ledger events are appended, the snapshot is updated by applying the mutations.
- The
UPSERTtoentity_snapshotshappens in the same R2DBC transaction as the ledger event inserts.
The snapshot update and ledger append are atomic — they either both succeed or both fail.
Snapshots Are Not the Source of Truth
The ledger is the source of truth. The snapshot is derived from it. This means:
- If a snapshot is lost or corrupted, it can be rebuilt by replaying ledger events.
- If a snapshot and the ledger disagree (e.g. after a rule bug), the ledger takes precedence.
- Snapshots can be deleted and rebuilt without data loss.
State Accessed in Rules
When causet-runtime evaluates rules for an intent, the snapshot’s state JSONB is the state context available in expressions:
preflight:
rules:
- name: reject_already_following
when: { expr: "state.following contains intent.artist_id" }
then:
- op: reject
code: ALREADY_FOLLOWINGstate.following refers to the following array field in the current entity snapshot.
Snapshot and Ledger Divergence
Divergence can occur if:
- A rule bug caused an incorrect mutation to be applied to the snapshot but the corresponding ledger event is correct.
- A deployment issue caused a snapshot update to succeed but the ledger write to fail (should not happen given the shared R2DBC transaction, but a catastrophic crash could theoretically cause this).
- A snapshot was manually modified for debugging.
If divergence is detected (e.g. the version on the snapshot does not match the expected sequence_number from the ledger), rebuild the snapshot:
# Proposed CLI — see Replay docs
causet snapshots rebuild --entity-id user-123 --fork-id productionRebuilding Snapshots
To rebuild a snapshot, the runtime replays all ledger events for the entity in order and re-applies each event’s mutations:
- Load all
ledger_eventsfor(entity_id, fork_id)ordered bysequence_number. - Start with an empty state (
{}). - For each event, apply the mutations declared in the IR for that event type.
- Upsert the resulting state into
entity_snapshots.
This process is safe to run while the runtime is handling other entities. The cursor lock for (entity_id, fork_id) must be acquired before rebuilding to prevent concurrent writes during the rebuild.
Schema Evolution and Snapshots
When the DSL changes — for example, a new field is added to an entity state — the snapshot does not automatically gain that field. The field appears on the next intent that triggers a set for it.
If an existing snapshot is missing a field that rules now expect, expressions against that field return null. Design rules defensively:
# Guard against null before comparing
when: { expr: "state.tier != null && state.tier == 'premium'" }If you need to backfill a field across all existing snapshots after a schema change, submit a backfill intent (a no-op action that sets the new field’s default value) to each affected entity.
Stale Snapshot Detection
The version column on a snapshot reflects the sequence_number of the last applied ledger event. If the latest sequence_number in ledger_events for an entity is higher than the snapshot’s version, the snapshot is stale.
This can happen during:
- A runtime crash after ledger write but before snapshot update (extremely rare given the shared transaction).
- Manual ledger manipulation (not recommended).
The runtime detects this on the next intent and can trigger an automatic snapshot rebuild before proceeding.
Related Pages
- The Ledger — the source of truth
- Rules Engine — how
state.*is used in expressions - Replay — full snapshot and projection rebuild procedures
- Runtime Architecture — snapshot update in the processing pipeline