Snapshots and Checkpoints in Event Sourcing
A snapshot stores derived aggregate state at a known point in its history; a checkpoint stores how far a consumer processed the event log. Both speed recovery, but neither replaces source events. Snapshot validity depends on reducer compatibility, while checkpoint validity depends on consumer identity and delivery semantics.
Problem
Why this pattern exists
Long-lived aggregates can require expensive full replay, and consumers should not rescan old offsets after every restart. Teams often call both optimizations checkpoints, then discover that advancing delivery cursor did not persist materialized state or that an old snapshot cannot be decoded by a new reducer.
Safe recovery needs explicit coordinates. Snapshot should record entity identity, included event count or version, as-of time, state-schema version, and reducer version. Consumer checkpoint should record durable consumer ID and acknowledged log position. Operators need separate reset procedures for each artifact.
Design decisions
Make boundaries explicit
- 01
Snapshot by measured replay cost
Choose threshold from aggregate size and latency budget, not arbitrary event count. Small streams may never need snapshots; hot long histories may benefit from automatic periodic creation.
- 02
Checkpoint after durable effect
Acknowledge consumer position only after output is committed or safely deduplicated. Early acknowledgement converts process crash into permanent skipped work.
- 03
Version both artifacts
Snapshot state shape and consumer reducer evolve independently. Include versions and make reset or rebuild a routine operation rather than emergency procedure.
AllSource implementation
Apply pattern to durable Core history
AllSource Core supports automatic and manual entity snapshots, including as-of coordinates and event counts, then applies later events during reconstruction. Snapshot endpoints let operators create, list, and fetch latest entity snapshots. Source events remain in WAL and Parquet according to retention and compaction policy.
Core's durable-consumer protocol stores WAL cursor positions. The Rust SDK ProjectionWorker acknowledges positions at configured intervals; its reduced state is not automatically stored with cursor. Select checkpoint interval by replay tolerance and ingest overhead. For stateful custom projections, persist view state transactionally with an idempotency marker or rebuild it from acknowledged event range.
entity snapshot
entity_id: account-42
as_of_version: 12500
state_schema: 3
consumer checkpoint
consumer_id: fraud_alerts_v2
wal_position: 92418830
reduced_state: stored separatelyFailure modes
Detect weak implementations early
Consumer resumes after checkpoint but its local view state is empty.
Fix: Persist view state separately or reset cursor and rebuild from source events.
New reducer loads semantically incompatible old snapshot.
Fix: Version snapshot schema and invalidate or migrate incompatible snapshots.
Checkpoint advances before external side effect completes.
Fix: Commit effect first and make repeats idempotent before acknowledging position.
Production checklist
Ready when each statement is true
- Snapshot carries entity version, schema version, and reducer version.
- Checkpoint carries stable consumer identity and durable log position.
- Acknowledgement happens after durable, idempotent processing.
- Reset and full-rebuild paths are tested before production.
- Source-event retention outlives required recovery and audit windows.
Related patterns
Continue through adjacent decisions
Event replay
Event replay reads immutable events again in their original stream order and applies them to a new or reset consumer. Use replay to rebuild projections, reproduce historical state, test new reducers, or backfill derived outputs—never to re-trigger uncontrolled external side effects.
Read nextDurable subscriptions
A durable subscription gives a named consumer a server-tracked position in the event log. After reconnect, event store replays committed events after last acknowledged position, then switches consumer to live delivery. Processing remains at least once, so handlers must be idempotent.
Read nextProjections and read models
A projection folds ordered events into a query-specific read model. It is derived state, not a second source of truth: operators must be able to discard it, replay source events, and build a replacement without rewriting event history or stopping existing readers.
Read nextStore history once
