How a write becomes durable
Follow a mutation from the query executor through in-memory staging, the single manifest-publish CAS fence, and crash recovery — the heart of Omnigraph's atomicity guarantee.
L2 — Multi-dataset coordination via __manifest
OmniGraph is not a single Lance dataset; it is a graph of datasets coordinated through one append-only manifest table.
- Manifest table:
__manifest/Lance dataset. - Layout:
nodes/{fnv1a64-hex(type_name)}— one Lance dataset per node typeedges/{fnv1a64-hex(edge_type_name)}— one Lance dataset per edge type__manifest/— the catalog of all sub-tables and their published versions, and the graph commit lineage (RFC-013 Phase 7)_graph_commits.lance/_graph_commit_actors.lance— legacy / branch-ref carriers. Since RFC-013 Phase 7 the graph lineage lives in__manifest(graph_commit/graph_headrows, written in the publish CAS);_graph_commits.lanceno longer receives commit rows, but is retained to carry the Lance branch refs thatcreate_branch/list_branches/ thecleanuporphan reconciler operate on. A graph created before Phase 7 (internal schema v3) keeps its lineage here until its first read-write open, which migrates it into__manifestviamigrate_v3_to_v4.- (legacy
_graph_runs.lance/_graph_run_actors.lancefrom pre-v0.4.0 graphs are inert; the run state machine was removed. The internal schema migration sweeps stale__run__*branches on first write-open; the inert dataset bytes themselves remain until a prefix-delete storage primitive lands)
- Manifest row schema (
object_id, object_type, location, metadata, base_objects, table_key, table_version, table_branch, row_count):object_type∈table | table_version | table_tombstone | graph_commit | graph_headtable_key∈node:<TypeName> | edge:<EdgeName>(empty forgraph_commit/graph_headlineage rows)table_branchisnullfor the main lineage and the branch name otherwise- Graph lineage rows (RFC-013 Phase 7): one immutable
graph_commitrow per commit (object_id= the commit ULID;metadataJSON carries parent / merged-parent / actor / timestamp) plus one mutablegraph_head:<branch>pointer per branch (graph_head:mainfor main). The in-memory commit DAG is a projection of these rows.
- Snapshot reconstruction: latest visible
table_versionper(table_key, table_branch)minus tombstones — rows whereobject_type = table_tombstone, whose owntable_version(acting as the tombstone version) is>= the entry's table_version. - Atomic publish: multi-dataset commits publish so that a single write to
__manifestflips all the new sub-table versions visible at once. - Row-level CAS on the merge-insert join key:
object_idcarries an unenforced-primary-key annotation so Lance's bloom-filter conflict resolver rejects two concurrent commits that land the sameobject_idrow. Without this annotation, Lance's transparent rebase would admit silent duplicates from racing publishers. - Optimistic concurrency control on publish: a publish asserts the manifest's current latest non-tombstoned version for each touched table is exactly what the caller observed; mismatches surface as an
ExpectedVersionMismatchmanifest conflict naming the table and the expected/actual versions. Concurrent advances surface as a conflict rather than being silently rebased through.