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.
Mutation execution (exec/mutation.rs)
Resolves expression values to literals, converts to typed Arrow arrays (literal_to_typed_array(lit, DataType, num_rows)), then writes via Lance's two-phase distributed-write API at end-of-query:
insert(no@key, edges) → accumulate intoMutationStaging.pending(Append mode); finalize callsstage_appendonce per touched table.insert(@keynode) → accumulate intopending(Merge mode); finalize callsstage_merge_insertonce per touched table.update→ scan committed via Lance + pending via DataFusionMemTable(read-your-writes), apply assignments, accumulate intopending(Merge mode).delete→ still inline-commits viadelete_where(Lance v6.0.1 has no public two-phase delete;DeleteBuilder::execute_uncommittedfirst ships in v7.0.0-beta.10 — tracked as MR-A in docs/dev/lance.md); recorded intoMutationStaging.inline_committed.
D₂ parse-time rule. A single mutation query is either insert/update-only or delete-only. Mixed → reject before any I/O. The check fires in enforce_no_mixed_destructive_constructive(&ir) inside execute_named_mutation.
Multi-statement mutations are atomic at the publisher commit boundary: every insert/update batch lives in memory until end-of-query, then exactly one stage_* + commit_staged runs per touched table, then ManifestBatchPublisher::publish commits the manifest atomically with per-table expected_table_versions CAS.