Why a Mirror?
KoldStore already uses PostgreSQL WAL as its change source (logical decoding
into a database worker). It still maintains a per-table latest-state mirror
(koldstore.<table>__cl) because WAL and the mirror solve different problems.
WAL is an excellent change transport. The mirror is an operational data structure for flush, change feeds, and primary-key lookups.
Compare
| Need | PostgreSQL WAL | Latest-state mirror (__cl) |
|---|---|---|
| Crash recovery / PITR / physical replica | Designed for this | Not its job |
| Decode every committed change | Native | Applied once by the worker |
| Latest state of one primary key | Replay + resolve | Indexed row (WHERE id = …) |
| Rows pending flush | Reconstruct from history | The table is the queue |
| Resume change feed from a cursor | Slot LSN + filter/replay | WHERE seq > $last |
| Per-table isolation | Shared stream; filter later | One __cl per managed table |
| Inspect / debug with SQL | Decoding tooling | Normal SELECT |
| Append-only history of every UPDATE | Yes | No — one row per PK |
Latest state, not another WAL
Four WAL records for the same key become one mirror row:
Flush and catch-up need “what is pending for this key now,” not a full rewrite of intermediate versions.
What the mirror makes cheap
Those are ordinary indexed queries. Doing the same against raw WAL means decoding, replaying, visibility, and reconstructing latest state on every use.
Complementary, not competing
Today that path is already live: heap commit → WAL → applier → __cl. A future
capture change would still target the same mirror and the same external APIs.
Summary
- WAL moves durable changes through PostgreSQL.
- Mirror materializes latest-per-PK state for flush queues, resume cursors, and PK lookups.
- They are layers in one design, not alternatives.
See also: Under the Hood, Change Feed.