EDR 0003 accepted
Point-in-time reconstruction (as-of reads) from the archive
TL;DR
The snapshotter archive (EDR-0001) is a base
snapshot plus a stream of diffs, each watermarked by source position. Cold-tier
replay (EDR-0002) added snapshotter.Reader to walk
that archive forward from a position. The same machinery answers a second,
generic question for free: "what did the table look like as of position P?"
We add Reader.ReconstructAsOf(ctx, position, keyFields, cmp) — it selects the
newest base snapshot at or before P, folds the diffs up to P into a keyed map,
and returns the materialized rows plus the effective position the result
reflects. This is point-in-time/time-travel reads for audit, debugging, and
offline point-in-time exports, built entirely from artifacts already written.
Context
EDR-0001 scoped reconstruction out of the binary ("the consumer rebuilds")
and EDR-0002 gave consumers Reader for the forward (resume) case, explicitly
naming as-of reads as "its own EDR." The pieces are all present:
- Artifacts carry positions: a snapshot at
ToPosition, a diff over(FromPosition, ToPosition]. Readeralready loads the manifest and decodes snapshots/diffs.- Diffs are not splittable (they are PK-collapsed over a range), so a read "as of P" resolves to the state at the latest artifact boundary at or before P — the effective position. That is the natural, honest semantics and needs no per-change positions.
Decision
Two additions to snapshotter.Reader, plus one shared helper:
PlanAsOf(manifest, position, cmp) (*ReplayPlan, error)— the newest base snapshot withToPosition ≤ position, plus the contiguous diffs after it whoseToPosition ≤ position. A diff that straddlespositionis excluded. Returnsnilwhen the archive cannot coverposition(empty manifest, or the oldest snapshot is already afterposition).ReconstructAsOf(ctx, position, keyFields, cmp) (*Reconstruction, error)— loads the manifest, plans, reads the base snapshot and diffs, and folds them into a keyed map (insert/update set, delete removes, truncate clears), returning{Rows, Position}wherePositionis the effective as-of position. Returnsnilwhen the archive cannot reachposition; the caller decides whether that is an error.RowKey(row, keyFields)— lifted from the writer's privatekeyOf, now shared so a reconstructed snapshot keys rows exactly as the diff stream does (no drift between write and read).keyFieldsdefaults to["id"].
It stays a library capability (EDR-0001's "reconstruction is the consumer's
job"): the snapshotter binary remains write-only, and the replication service is
untouched. Reader reuses its existing Destination / Format decode paths —
no new artifact format, storage backend, or wire change.
Scope — in
PlanAsOf,ReconstructAsOf,Reconstruction, and the sharedRowKey.- Unit tests over a real local destination (boundary, between-boundary, predates-archive, delete/insert/update folding, effective-position).
- Docs (snapshot-writer guide reconstruction section), changelog.
Scope — out
- No CLI / RPC surface. A
laredoCLI command or a read RPC over the archive is a natural follow-up but not required for the library capability. Noted inTODO.md. (Update: the CLI shipped —laredo archive reconstruct, reading the archive directly throughsnapshotter/destwire. A read RPC remains future work. See the Changelog.) - No sub-diff (intra-range) precision. As-of resolves to artifact boundaries; splitting a diff to land exactly on a non-boundary position is out of scope (and would need per-change positions the archive does not record).
- No new metric/threshold. Read-only over existing artifacts.
Consequences
Easier:
- Audit & debugging. "What did this table look like at LSN X / time T" is a direct call, from cheap object storage, with no live system involved.
- Point-in-time exports. A consumer can materialize a consistent historical snapshot for a data lake or a diff against "now".
- Reuse. Built on the
ReaderandFormatpaths cold-tier replay already uses;RowKeyremoves the last bit of write/read key-derivation duplication.
Harder / obligations:
keyFieldsmust match the write side. Reconstruction keys snapshot rows by the same fields the snapshotter wrote with; a mismatch silently mis-keys. Documented; the default["id"]matches the writer default.- Effective-vs-requested position. Callers must use the returned
Position, not assume the result is exactly at the requested position. Documented on the type.
References
- EDR-0001 — snapshot + diff artifacts
- EDR-0002 — cold-tier replay — introduced
Reader snapshotter/reader.go—Reader,Plan,PlanAsOf,ReconstructAsOf
Changelog
- 2026-06-11: Accepted; implemented alongside this EDR.
- 2026-06-11: CLI surface shipped —
laredo archive reconstruct --at <pos>materializes a table's state from the archive, building the reader throughsnapshotter/destwire(the same path laredo-server and the snapshotter use). It reads object storage directly, so it works offline. A read RPC remains future work.