Skip to content

anagrapheus.identity.sqlite

anagrapheus.identity.sqlite

The identity adapter: the only module in this subpackage containing SQL.

It shares the timeline's connection and therefore its file and its transactions — which is what lets the projection's state and its position commit together, so a crash can never leave the registry ahead of or behind the position that guards it.

SQLiteIdentityStore

SQLiteIdentityStore(conn: Connection)

Implements IdentityStore over the same file the timeline uses.

Every recording of a surface form is kept as a row of its own, a claim, named by the form and the position that recorded it, and stands on one entity at a time. Each claim also keeps its path: the merges that carried it to where it stands, latest first. A merge moves every claim standing on the absorbed entity to the survivor and adds itself to their paths; an unmerge sends every claim whose path runs through its merge back to the absorbed entity, wherever later merges of the survivor carried it, and drops that merge and every later one from the path. That is the whole rule: what an unmerge withdraws is found by the specific rows its merge moved, never by a form's name. Each step of a path is kept as a row of its own, indexed by the merge, so an unmerge reads only the claims its merge carried, however many claims the projection holds. A path's order is not stored: decisions are applied in log order, so a later merge always has the higher position, and the path reads latest first by position alone.

An entity shows one row per form, the first of the claims standing on it: its own recordings first, newest first, then what merges brought, the earliest merge first and each merge's claims in the order they stood on the entity they came from. That is the order the claims would stand in had no withdrawn merge ever happened.

A split takes the row the original shows to the new entity, as a recording of the new entity's own; whatever else stood behind that row on the original is set aside there, shown by no one, until an unmerge of a merge that carried it sends it back.

Each decision is applied once. On a re-applied batch the state already reflects every decision in it, so a decision that finds its own record at its position has nothing left to do.

Source code in src/anagrapheus/identity/sqlite.py
def __init__(self, conn: sqlite3.Connection) -> None:
    self._conn = conn
    self._conn.row_factory = sqlite3.Row
    migrate(self._conn, COMPONENT, MIGRATIONS)