← Recipes

阅读中文版本

Add Snapshots to an Event-Sourced Aggregate

Persist periodic snapshots so rebuilding an aggregate does not replay thousands of events, and version snapshots so a schema change can invalidate them safely.

~45 minevent-sourcingperformancedata

Task

Introduce snapshots to keep aggregate load time bounded as its event stream grows.

When to use this

An aggregate with a long history takes seconds to load because every event is replayed from the beginning on each command.

Prerequisites

  • An append-only event store
  • A snapshot store keyed by aggregate id

Steps

1. Write a snapshot every N events, storing the serialized state and the last event position.

Expected output: Loads start from the most recent snapshot.

2. On load, read the latest snapshot then replay only events after its position.

Expected output: Load time depends on events since the snapshot, not total history.

3. Version the snapshot schema and discard snapshots whose version is outdated.

Expected output: A format change does not corrupt state.

4. Keep snapshots optional: the aggregate can always be rebuilt from events alone.

psql -c "SELECT position FROM snapshots WHERE aggregate_id=$1 ORDER BY position DESC LIMIT 1;"

Expected output: Snapshots are a performance cache, not a source of truth.

Troubleshooting

Problem

Snapshot format changes

Solution

Embed a version in the snapshot and fall back to full replay for mismatched versions.

Problem

Snapshot written before events are durable

Solution

Write the snapshot only after the events it summarizes are committed, so it never gets ahead of the log.

Limitations

  • Snapshots add write amplification and storage; they only pay off for long-lived aggregates.
  • An incorrect snapshot silently poisons the aggregate until a full rebuild corrects it.

Context

Introduce snapshots to keep aggregate load time bounded as its event stream grows. An aggregate with a long history takes seconds to load because every event is replayed from the beginning on each command.

Steps

  1. Write a snapshot every N events, storing the serialized state and the last event position. Expected: Loads start from the most recent snapshot.
  2. On load, read the latest snapshot then replay only events after its position. Expected: Load time depends on events since the snapshot, not total history.
  3. Version the snapshot schema and discard snapshots whose version is outdated. Expected: A format change does not corrupt state.
  4. Keep snapshots optional: the aggregate can always be rebuilt from events alone. Expected: Snapshots are a performance cache, not a source of truth.
SELECT state, position FROM snapshots
WHERE aggregate_id = $1
ORDER BY position DESC
LIMIT 1;

Verify

  • Loads start from the most recent snapshot.
  • Load time depends on events since the snapshot, not total history.
  • A format change does not corrupt state.
  • Snapshots are a performance cache, not a source of truth.

Limits

  • Snapshots add write amplification and storage; they only pay off for long-lived aggregates.
  • An incorrect snapshot silently poisons the aggregate until a full rebuild corrects it.