B8.2 · dive 2 — replay equals live
State is the fold of the log.
The master invariant of the platform reads: one instrument's book is reconstructable as a
fold over an append-only event log. State is not a thing kept beside the log — state is the fold of the
log. Recovery is therefore replay, not a feature bolted on afterward. The as-built
EchoCache.Journal already proves the posture on the job lane, and the PROPOSED
Exchange.Book inherits a certified mechanic rather than inventing one.
Source: replay(j, conn) and apply_and_remember are real Elixir at
echo/apps/echo_cache/lib/echo_cache/journal.ex, taught in content/bcs4.4.md,
recorded in bcs_rung_4_4_check.out (PASS 6/6). The per-instrument
Exchange.Book is PROPOSED (docs/trading/).
The journal remembers what the cache forgot
bcs_rung_4_4_check.out · H3 · verbatimThe applied table lives in the file, so it survives the table, the node, and the bus. After a
full restart with an empty L1, a replayed old version must answer :remembered_stale from the
journal alone — no cache row consulted, none created — while a genuinely newer version passes through and
updates the memory. The committed gate:
derive (memory): the applied table lives in the file, so it survives the table, the node, and the bus; after a full restart with an empty L1, a replayed old version must answer :remembered_stale from the journal alone -- no cache row consulted, none created -- while a genuinely newer version passes through and updates the memory H3 memory ok -- the journal remembered v5 across a full stop of table and journal; the replayed old version answered :remembered_stale without touching the cache -- the table's verdict counters did not move and no row appeared -- and the genuinely newer v6 passed through and became the new last word PASS 6/6
Without this memory, an empty L1 makes every replayed message look fresh; with it, staleness
is a fact about the lane, not about whichever rows happen to be resident. The memory check runs before the
cache: apply_and_remember consults the journal first and answers :remembered_stale
without an ETS lookup, because the journal's word outlives the cache's rows — the gate proves the verdict
counters do not move and no row appears.
The bus dies; the lane replays
bcs_rung_4_4_check.out · H4 · verbatimThe bus is volatile by D-2, so a bus restart erases queued coherence jobs. The loss drill stages exactly that: 50 intents recorded and enqueued, 20 applied before the loss, the lane's queue keys flushed. Replay must re-enqueue exactly the 30 uncovered intents in seq order, and a consumer must drain them to a remembered count of 50.
derive (loss): D-2 keeps the bus volatile, so a bus restart erases queued coherence jobs; 50 intents recorded and enqueued, 20 applied before the loss, the lane's queue keys flushed -- replay must re-enqueue exactly the 30 uncovered intents in seq order, and a consumer must drain them to a remembered count of 50 H4 loss ok -- the bus restart erased the queue and the journal replayed the lane back: exactly 30 uncovered intents re-enqueued in seq order under their recorded job ids, the consumer drained them, and the applied memory closed at 50 of 50 names holding their final versions PASS 6/6
Replay selects by coverage — every intent whose name lacks an applied version at least as new
— so the 20 already-applied intents are not re-sent and the 30 lost ones are, with no acknowledgment
bookkeeping anywhere on the hot path. Replay reuses the recorded job ids, so the bus answers
:duplicate for whatever it still holds, which the record counts in the open.
Coverage, not acknowledgment
docs/trading/trading.specs.md · the master invariantThe same predicate drives replay and the dedup floor: an intent is uncovered when its name
carries no applied version at least as new. The hot path pays no per-intent completion write — there is no
acknowledgment row to maintain. This is the design's reach: the replay property — folding the log reproduces
live state — is the Chapter 4.4 posture re-gated per book (the master invariant's mechanics). What the journal
proves on the job lane, the PROPOSED Exchange.Book reuses for its instrument's log: kill the
book, restart, replay equals live.
# recovery beside the bus: re-enqueue every intent not covered by the applied memory, # in seq order, reusing recorded job ids so admission dedup absorbs whatever it holds. {:ok, %{replayed: n, deduplicated: m}} = Journal.replay(j, conn)
Recovery is replay — the alternatives weighed
content/bcs4.4.md · the design's reasoningThe posture is chosen against named alternatives. A WAL inside the bus — the obvious place to put durability, and the one the law forbids: the bus stays volatile by D-2, and durability lives in a file the bus has never heard of, which keeps the bus a fan-out primitive and not a log. A dual-write to a store, then the bus — a crash between the two writes loses the message or announces a write that never happened; the outbox closes both seams by recording the intent first and relaying after. A canonical store the book reads back on every command — a round trip on the hot path, where the fold of an in-memory log answers for free.
Recovery as replay costs a price this module measures in dive 3 — 524 µs against
the bare lane's committed 148 µs end to end — and that price buys an outbox, a last word per
name, and a replay that survives the bus. The fold that is live state is written in the functional
core the platform is built in (/elixir); the bus the log moves to at milestone B is the
stream lanes of /echomq. The compaction that keeps the outbox small and the regulated
ledger are B8.2.3 · The Double-Entry
Ledger.
References
sources · relatedSources
- Richardson — Pattern: Transactional outboxthe outbox closes the dual-write seams; the relay may deliver twice, absorbed by admission dedup
- SQLite — Write-Ahead Loggingthe WAL the journal's durability rides; replay reads from the same file
- Litestream — How it worksrestore by snapshot plus replay — the off-box recovery verb the node-death drill runs first
Related
- B8.2 — The Log and the Ledgerthe module hub; the full journal record in context
- B4.4 — The Lane That Remembersthe replay posture re-gated per book, in its home chapter
- B4 — EchoCachethe cache the journal's memory outlives
- B3 — The Busthe volatile bus the lane replays onto
- Functional Programming in Elixirthe functional fold replay is
- EchoMQ — the coursethe stream lanes the log moves to