Authoritative MutationBatch commit protocol¶
MutationBatch is the engine's durable mutation currency. A successful RPC is
acknowledged only after the authoritative state, result, version/fence, durable
status, audit entry, and projection outbox have crossed one commit point. The
in-memory GraphCore is a serving projection and can be reconstructed from that
authority.
Commit sequence¶
sequenceDiagram
participant C as Client
participant G as Commit gateway
participant S as Isolated staging graph
participant D as Authoritative redb
participant P as Serving projections
C->>G: verified request + mutation
G->>G: authz, placement, policy, idempotency, version/fence
G->>S: execute against authoritative snapshot
S-->>G: state image + deterministic result
G->>D: batch + state digest/image + result + outbox
D-->>G: durable commit / durable replay result
G->>P: publish committed snapshot
G-->>C: acknowledge stored result
The four concrete process boundaries in this sequence are exercised against the exact promoted executable, not an in-process substitute. See Exact-binary fault and restart certification for the 60-case mutation-domain matrix, deterministic abort control, restart observations, and privacy-safe evidence format.
Compact row operations (AddNode, RemoveNode, AddEdge, RemoveEdge, and
ClearGraph) are validated and committed directly as canonical methods before
their RAM projection is changed. Runtime-result graph, Cypher, ordinary GraphQL,
and RDF mutations execute against an isolated snapshot. Their bounded affected-row
delta is SHA-256 bound to adjacent source and target graph versions and atomically
updates the durable rows before publication. A complete snapshot uses the same
version contract when a coordinator explicitly supplies one.
RDF multi-valued literals are retained under the reserved
__rdf_multivalue_literals node property. This makes the lossless RDF dataset part
of the same authoritative graph image. The RDF view is a derived projection, never
a second write authority or an alternate reader.
SQL graph DML follows the staged graph path. SQL user-table/catalog statements use
the table store's native coordinator: the table/catalog rows, SQL-domain version and
fence, terminal batch/result, idempotency index, and immutable outbox are committed
in one owner-scoped catalog transaction. Each verified tenant+effective actor resolves
to an opaque redb filename under <persist-dir>/sql-catalog/; there is no global store,
path override, unsigned reader, or temporary fallback. Query text and bound parameters are represented
only by an operation digest in that metadata. GraphQL cross-modal begin/stage calls
have no durable effect and are keyed by verified owner scope. Staged reads combine an
RLS-projected committed snapshot with only that owner's overlay. A sole-root
commitTransaction is consumed by the facade, which revalidates graph and tenant
authority and lands graph, semantic/blob/time-series rows plus the universal
status/fence/idempotency/outbox in one authoritative shard transaction before
publishing RAM state. The lower GraphQL crate has no commit function. This avoids
wrapping either native domain in a second, non-atomic graph-snapshot commit.
Durable invariants¶
- The sole current persisted schema is
MutationBatchv2. Every operation carries an explicit durability domain; missing fields, unknown fields, and other schema versions are rejected rather than defaulted during replay. - A batch has one opaque
batch_idand deterministic idempotency key. - Verified principals are stored only as SHA-256 pseudonyms.
- State-backed operations store an opaque method digest, not query text, paths, document bodies, or caller-provided identifiers.
expected_graph_version, placement epoch, and fencing token are checked inside the same write transaction that advances the durable graph version.- A state descriptor advances exactly one checked graph-version step. Missing durable version state is accepted only for a true version-zero bootstrap; an advanced or mismatched serving projection cannot substitute its RAM version.
- A retry after durable commit returns the stored result and reconciles RAM from the authoritative snapshot; it does not execute the handler again.
- Each outbox record and projection cursor carries the current schema version, a
required
version_scope, and a requiredsource_graph_version. Graph-authoritative rows useversion_scope=graphand a strictly positive committed graph version. Native SQL/KV/blob/job/control stores useversion_scope=non_graphand the explicit value0; their independent owner-domain counter is never presented as a graph version. - Graph-domain delivery leases are consumer-specific, ordered by source graph version, epoch-fenced, and acknowledged in the same transaction that advances that projection's cursor. A cursor can advance within the same batch ordinal or to a higher graph version; regressions, a different batch at the same graph version, and retries after the cursor has moved past an event fail closed. The SQL native store exposes its committed batch and outbox for restart/rebuild consumers from the same database that owns the table rows.
- Durable methods fail closed when an authoritative backend is unavailable.
There is no online reader for the pre-v2 mutation/projection shape and no caller- supplied version seeding when a durable version row is absent. The earlier shape was never a promoted production format, so this cutover intentionally ships no permanent migration path. Any retained development data must be rebuilt or converted by a finite release-specific offline operation before it is opened by a current binary.
The staged image limit is automatically sized from available RAM (bounded between
16 MiB and 2 GiB). EPISTEMIC_GRAPH_MUTATION_SNAPSHOT_MAX_BYTES can set an explicit
byte limit; 0 disables the limit. This is a capacity guard only and never changes
the atomicity contract.
WorkItem authority¶
Work scheduling is a native MutationBatch state machine:
ClaimWorkItemsupports exact-ID delivery or tenant/queue/resource/fairness selection, priority/deadline ordering, admission quota, renewable leases, and monotonically increasing fencing tokens.max_tenant_in_flightis a required, validated 1..=4096 limit; zero and out-of-range values are rejected at the protocol boundary, so callers cannot disable server admission. An expired lease whose current attempt has already reachedmax_attemptsis atomically fenced and terminalized asdead_letterin that same claim transaction; it is never re-leased for attemptmax_attempts + 1.RenewWorkItemLeaserejects stale or expired ownership.CommitWorkItemResultatomically publishes result/error references, retries with bounded exponential backoff, dead-letters exhausted work, and releases dependent items after success.CancelWorkItemcancels submitted/ready work without manufacturing a lease and never steals an active lease.DeferWorkItemreleases a fenced lease untilnext_retry_atwithout consuming an attempt, which supports polling barriers without exhausting retry budgets.
Payloads, errors, and cancellation/deferral reasons are opaque references. The control plane does not retain free-form bodies or personal data.
Projection recovery¶
Projection consumers call the persistence claim API with a stable consumer name, process leased outbox rows in order, and acknowledge with the returned epoch. The ack and projection cursor update are atomic. An expired lease can be reclaimed; an old epoch cannot acknowledge it. Any ordering gap fails closed, allowing a consumer to restart from its durable cursor and rebuild text, vector, RDF, CDC, audit, or lineage projections deterministically.
The compact reasoning snapshot has its own required v2 schema marker. Its applied position always contains a positive graph version, rejects a lower watermark or a different batch at the same version, and is validated before load and before atomic replacement. Missing snapshots bootstrap from authoritative graph state; malformed or unsupported snapshots fail closed instead of being interpreted through defaults.
SQL table/catalog recovery follows the same durable-status rule within the SQL authority: a pre-commit crash reopens with neither table changes nor coordinator metadata, while an acknowledgement-lost crash reopens with both and returns the stored affected-row count without re-executing the statement. Cross-modal recovery likewise rehydrates the serving graph/semantic projection from the committed batch; its modality manifest contains counts and a payload digest, never raw embeddings, document bodies, query text, filesystem paths, or caller names.
The current-only persistence contract is guarded by
python scripts/check_persisted_mutation_contract.py. It checks the required schema,
version scopes, checked version advancement, strict watermarks, canonical wire names,
and the closed Python MutationBatch serializer without opening a database. The same
source-only gate parses the live ALL_METHODS policy ledger, the real durable
classifier and applier, the WorkItem and native-command owners, the gateway/native
partition, dispatch route order, and the clustered consensus inventory. These sets
must agree exactly: a stale Rust mirror, an unowned mutation, an open gateway entry,
or a method that bypasses the routed commit owner fails the source freeze.
Auxiliary write carriers are included in that proof. SPARQL HTTP updates are
signed ApplyMutation events planned on detached images. Complete preimages and
forward images are authenticated ciphertext attached to a digest-only durable
parent before any state change. Cross-shard graph slices use retained-decision
2PC; local slices use deterministic child batches. A durable compensation marker
fixes restart direction and drives preimage replay plus lifecycle rollback before
the parent is acknowledged.
Direct blob-artifact insertion, reference acquisition, and its native blob batch
share one redb transaction. Acknowledgement-lost acquisition and release replay
the same batch, and ordinary CAS sweep deterministically reclaims a zero-reference
direct chunk after restart while preserving any chunk still reachable from a live
manifest. External compute uses signed KnowledgeStream reads and the native
AnalyticsJob result-publication state machine rather than another mutation
carrier. ROS2 inbound writes reconstruct an exact signed request and call ordinary
dispatch. None of these adapters can call live GraphCore or backend mutation
primitives directly.