Replay
The causet snapshots rebuild, causet snapshots rebuild-all, and causet projections replay commands on this page are proposed — they have no equivalent in the shipped causet-cli today. The real entity-level mechanism is causet recovery replay --stream <stream> (sandbox-only) — see Rebuilding Memory and causet recovery replay.
Replay is how Causet recovers and rebuilds derived state from the immutable event ledger. There are two types:
- Entity snapshot replay — re-run rules from ledger events to rebuild an entity’s current state.
- Projection rebuild — reset the Kafka consumer offset to re-process events and rebuild projection tables.
Both are safe because Causet’s rules are deterministic: the same events always produce the same resulting state.
When to Replay
| Scenario | Replay Type |
|---|---|
| Entity snapshot is stale or corrupted | Entity snapshot replay |
| Projection bug was fixed in a new IR version | Projection rebuild |
| New projection added to existing application | Projection rebuild (historical events) |
| Projection handler error DLQ’d many events | Projection rebuild (partial) or DLQ re-consume |
| Schema change broke derive expressions | Projection rebuild after IR fix |
| Disaster recovery — projections DB lost | Full projection rebuild |
Type 1: Entity Snapshot Replay
Entity snapshots are derived from ledger_events. If a snapshot is stale, incorrect, or missing, re-derive it from the ledger:
- Load all
ledger_eventsfor(entity_id, fork_id)ordered bysequence_number. - Start from empty state (
{}). - For each event, apply the mutations declared for that event type in the active IR.
- Upsert the resulting state into
entity_snapshots.
The cursor lock for the entity must be held during this operation to prevent concurrent intent processing.
Proposed CLI:
causet snapshots rebuild \
--entity-id user-123 \
--fork-id production
# Rebuild all snapshots for a fork (long-running)
causet snapshots rebuild-all \
--fork-id production \
--stream userThis does not require stopping the runtime or any Kafka operations.
Type 2: Projection Rebuild
Projection tables are derived from causet.projection-events.v1 (or, for a full rebuild, from the ledger re-published as projection events). To rebuild a projection:
Procedure
- Stop the
projection-worker(or pause the consumer group for the affected fork). - Optionally truncate the target table:
SET LOCAL search_path = acme_concertapp_production; TRUNCATE artist_followers; - Reset the consumer group offset to the beginning of
causet.projection-events.v1(or a specific timestamp checkpoint):# Using Kafka CLI kafka-consumer-groups.sh \ --bootstrap-server localhost:9092 \ --group projection-worker \ --topic causet.projection-events.v1 \ --reset-offsets \ --to-earliest \ --execute - Restart the
projection-worker.
Events are re-consumed from the reset offset. Mutations are idempotent — re-processing events that were previously applied produces the same result.
Proposed CLI:
causet projections replay \
--projection artist_followers \
--fork-id production
# Replay from a specific timestamp
causet projections replay \
--projection artist_followers \
--fork-id production \
--from-timestamp 2024-01-01T00:00:00ZDeterminism Makes Replay Safe
Rules are deterministic:
same entity state + same intent → same eventsAnd projection derive expressions are pure functions of the event payload. This means:
- Replaying the same events always produces the same projection rows.
- A rebuilt projection will be byte-for-byte identical to the original (for the same event sequence).
Side Effects During Replay
Warning: Side effects declared in
side_effectsrules (external API calls, webhook submissions, downstreamsubmitops) are triggered at intent processing time on the write path — not during projection rebuild.
Replay of projections does not re-trigger:
op: submit— follow-up intentsop: schedule— delayed intents- Any external integrations triggered via side effect rules
If you need to replay side effects (e.g. re-send a missed notification), that requires a separate reconciliation process outside Causet.
When writing side-effects rules, guard against re-execution using entity state:
side_effects:
rules:
- name: send_welcome_email
when: { expr: "state.welcome_email_sent != true" }
then:
- op: submit
action: SEND_EMAIL
entity_id: intent.user_id
payload:
template: welcomeThis pattern ensures the side effect only fires once even if the intent is re-submitted.
Projection Rebuild vs Migration
| Operation | Purpose |
|---|---|
| Projection rebuild | Re-process events with the current (or corrected) projection definition. Same table schema, new data. |
| DDL migration | Apply schema changes to the projection table — add column, change type, create index. Happens at deploy time from IR. |
A rebuild re-populates data. A migration changes the table structure. Often you need both: deploy a new IR version (which applies DDL), then rebuild the projection to backfill the new column with historical data.
Replay from the Ledger (No Kafka)
If Kafka retention has expired and the projection events are no longer available, projections can be rebuilt by re-publishing ledger events as projection events:
- Query
ledger_eventsfor all events in the relevant time range and fork. - Re-publish each event as a projection event envelope to
causet.projection-events.v1. - The projection worker consumes and materializes them.
This is an operational procedure performed with the Causet admin CLI or a custom migration script. No data loss — the ledger always has the complete history.
Replay and Multiple Forks
Replay is always scoped to a specific fork. Rebuilding production projections does not affect staging projections.
Related Pages
- The Ledger — the source of truth for replay
- Entity Snapshots — snapshot rebuild detail
- Projection Pipeline — how events are materialized
- Dead Letter Queues — alternative recovery for DLQ events