All event-sourcing patterns
Production pattern423 words · verified 10 September 2026

Event Replay for Event-Sourced Systems

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.

Problem

Why this pattern exists

Replay is powerful because recorded facts become reusable input. It is dangerous when a handler mixes deterministic state reduction with sending email, charging cards, or calling mutable services. Reprocessing old events can duplicate real-world effects unless replay context and idempotency boundaries are explicit.

Production replay also competes with live traffic and may encounter schemas written years ago. A safe plan defines source range, event filters, target consumer version, rate limits, validation sample, catch-up behavior, and cutover. Historical payloads must be decoded through compatible upcasters rather than rewritten in place.

Design decisions

Make boundaries explicit

  1. 01

    Separate projection from effect

    Keep pure state fold independent from outbound effect dispatcher. Replays can rebuild state while effect handlers ignore replay frames or deduplicate by event ID.

  2. 02

    Replay into new target

    Build projection or export under versioned name. Avoid destroying current serving view until replacement catches up and passes comparison checks.

  3. 03

    Bound and observe workload

    Define start and end coordinates, throughput budget, lag target, error policy, and pause control. Replay is an operation with progress, not opaque background loop.

AllSource implementation

Apply pattern to durable Core history

AllSource stores ordered events in Core and exposes query, reconstruction, snapshot, and durable-consumer paths. For a projection rebuild, register new consumer identity, replay from its initial cursor, fold events deterministically, then continue into live WebSocket delivery. Existing view stays available during catch-up.

For incident analysis, query one entity and reconstruct state at timestamp before failure. Compare event IDs, versions, payloads, and metadata with current state; this answers what system knew then without editing history. For large backfills, filter to required event namespaces, throttle processing, and measure cursor lag against Core head before switching readers.

Controlled projection replay
target: account_balance_v3
source: tenant-scoped Core events
range: position 0 -> captured live head
effects: disabled
validation: totals, entity count, sampled state hashes
cutover: after v3 catches live stream and checks pass

Failure modes

Detect weak implementations early

Historical replay sends duplicate notifications or payments.

Fix: Separate pure folds from effects and deduplicate effects by event ID.

Rebuild overwrites working projection before validation.

Fix: Replay into versioned target and use explicit cutover.

Old payload cannot be read by current code.

Fix: Add deterministic schema upcaster; preserve original stored event.

Production checklist

Ready when each statement is true

  • Replay range, filters, target, and owner are explicit.
  • External side effects are disabled or idempotent.
  • Historical schemas have deterministic read compatibility.
  • Progress, throughput, lag, failures, pause, and resume are observable.
  • New output is verified before reader cutover.

Related patterns

Store history once

Rebuild every useful view from durable events.