symfonic.memory.backends.postgres_memory_restore¶
postgres_memory_restore ¶
The restore procedure for the memory erase generation table (CORE-171).
A restored database holds content and generations as they were at its
backup: any forget since is undone in both, so neither a stale turn's token
nor a fresh turn's capture can see that it happened. Every logical restore
-- a dump restored into this cluster or another, or pg_upgrade -- leaves
a witness that no longer binds (postgres_memory_generation_witness), so
every generation-table reader -- Agent turn admission and the erase gates --
refuses, and the install script refuses to re-mint it. A direct backend read
outside a turn (get_node, query_subtree) does not consult the witness
yet: its coverage is pending, not excluded. Only
this procedure re-opens such a table:
- :func:
seal_and_exporton the source, asmemory_nodes' owner: its witness must still bind, so the source is the live table and not itself a restore. Under an EXCLUSIVE lock it reads every generation row, then seals the table: no reader binds it again and the forward function refuses every write, including a forget already waiting on the lock. The export names every erasure the source ever recorded, and nothing can add one. - Restore the database, content and generations from one consistent
snapshot, then :func:
validate_restoreon it, as itsmemory_nodes' owner. Its generation rows must equal the export's exactly. Fewer or lower rows mean the backup predates a forget, whose content it restored: that refuses, and the table stays closed. Its row trigger must also carry this export's seal, or its source's witness (a snapshot from before the seal): an export of another lineage refuses, however self-consistent. The digest detects an edited export; it authenticates nothing, and nothing here proves the export is the source's latest -- that freshness needs proof kept outside the database. Equal rows then mean every forget the export recorded is in the restored data; it mints a new witness.
Since 11.3.0 both steps also take the eraser's erase anchor proof
(postgres_erase_anchor), which supplies the freshness and single use the
export cannot. The seal refuses unless the anchor holds this source at its
log head (every forget acknowledged); the export records that holder and
seq. The validation refuses unless the restored log carries row seq
under the anchored digest, and, in the transaction that mints, rebinds the
anchor from the source to the restored database: the sealed source, a
second restore of the same export and an export the anchor has moved past
all refuse. A physical copy keeps the holder; the anchor's lineage check
refuses one that lacks an acknowledged forget, at every connection checkout.
GenerationExport
dataclass
¶
GenerationExport(source_system: str, source_witness: int, rows: tuple[tuple[str, str, int], ...], digest: str, holder: str = '', log_seq: int = -1)
Every generation row of a sealed source, and the witness it sealed.
seal_and_export
async
¶
Seal the live source table and return every generation it recorded.
Source code in src/symfonic/memory/backends/postgres_memory_restore.py
validate_restore
async
¶
Re-open a restored table whose rows equal export's; the new witness.