symfonic.memory.backends.postgres_erase_anchor¶
postgres_erase_anchor ¶
The erase anchor: the memory database's erase-log head, kept outside it (CORE-171).
A physical copy, PITR or promoted replica of the memory cluster is
byte-identical to it at the copy point, so only state the copy did not carry
can show that it lacks an acknowledged forget. That state is one row per
memory database in an operator-provided PostgreSQL database in another
cluster: (memory_system, memory_schema) -> (holder, seq, digest). The key
is the operator's stable, unique name for the memory database
(PostgresEraseAnchor(pool, memory=...), required: two databases sharing
an anchor store and a schema name stay distinct lineages) and its schema;
holder is the system_identifier, database OID and name serving it;
digest is HMAC-SHA256(receipt key, seq || chained log digest). The
anchor never holds a readable prefix, and the receipt key is never printed.
A database whose identity is not the
holder refuses MOVED: a logical restore elsewhere stays closed until
rebind (the validated restore) moves the holder, once, at the export's
exact seq; the sealed source then refuses too. A physical copy keeps the
holder, so the lineage check decides.
verify proves the memory database contains the anchored lineage: its log
row anchor.seq exists and its chained digest matches. Every memory
connection checkout (postgres_erase_incarnation) and every forget before
its delete run it. acknowledge runs after an erase commits: in one anchor transaction
it locks the row, re-verifies the lineage and moves the anchor to the local
head. A forget returns its receipt only after that; otherwise it raises
NOT_ANCHORED: committed, not acknowledged. There is no equal-seq no-op: an
anchor at or past the head acknowledges only when the lineage check passed.
Operator duties, not provable here: the anchor lives in a different cluster
(verify refuses the same system_identifier), is never restored from a
backup, and its loss closes memory. Receipt-key rotation has no supported
procedure and refuses (a digest under another key reads as a foreign lineage).
EraseAnchorError ¶
Bases: MemoryContractError
The memory database cannot be proven to hold every acknowledged forget.
EraseProof ¶
One memory schema's anchor, keyed with the eraser's receipt key.
Source code in src/symfonic/memory/backends/postgres_erase_anchor.py
acknowledge
async
¶
Anchor the committed local head in-lineage, or raise NOT_ANCHORED.
Source code in src/symfonic/memory/backends/postgres_erase_anchor.py
genesis
async
¶
Owner operation, once per anchor key: anchor log row 0 to this database.
Refused over a non-empty log or any existing row for the key, whoever holds it: an anchor lost after genesis has no in-database re-genesis, and a restore can never start a fresh lineage under an anchored name.
Source code in src/symfonic/memory/backends/postgres_erase_anchor.py
holder
async
¶
rebind
async
¶
The validated restore: move the anchor from source to memory, once.
Only when the anchor still names source at exactly seq (the
sealed export's head, nothing acknowledged since) and memory's log
row seq carries the anchored digest. Afterwards source and any
other copy refuse MOVED, and a second use of the export elsewhere
refuses; re-running it on the same database (a failed commit) is idempotent.
Source code in src/symfonic/memory/backends/postgres_erase_anchor.py
seal ¶
The anchor's keyed digest of log row seq; never printed.
Source code in src/symfonic/memory/backends/postgres_erase_anchor.py
verify
async
¶
Refuse unless memory holds the anchor and its lineage; the anchored seq.
pinned: memory is the eraser's own login, read in its schema.
Source code in src/symfonic/memory/backends/postgres_erase_anchor.py
PostgresEraseAnchor ¶
The anchor row store on its own pool, in a cluster other than memory's.