Repository navigation
Conversation
Readers, the promoter and cleanup learn to handle a table pin that names a detached Lance version, without any writer producing one yet. On existing graphs the change is inert: no pin carries the new fields. - TableVersionMetadata carries staged_version and transaction_uuid in the manifest row and the namespace request metadata map. - open_pinned_dataset resolves a pin to its linear target, its staged version while the target is pending or foreign, or reclaimed once the linear head has passed an absent target; both dataset-open paths use it. - The Blob facade and the change-feed witness accept the transaction uuid as identity; the Blob version check accepts a pending pin's staged version. - A version's transaction identity is read from the transaction file name Lance records in the manifest, so no request beyond the manifest is needed; the existing twin fence pins the name. - TableStorage gains commit_staged_detached, promote_detached and transaction_identity; the registry pins their call sites. - db/omnigraph/promotion.rs promotes one pin: target first, chains through read-version links, tolerates reaped predecessors, reports foreign occupants as blocked. - Cleanup promotes pending pins inside its per-branch loop before any reclamation and deletes promoted pins' detached manifests. - Seam promotion.pre_replay for the race and crash matrix of step 2.
This was referenced Sep 16, 2026
Closed
Closed
… 0067 review found Four defects reproduced against the stack, plus one the server's concurrent insert test exposed once the writers landed: - Cleanup skipped nothing when a pin's promotion was blocked: HEAD equalled the pin's target numerically, so stock version GC ran and could delete the detached version's data files once older than Lance's unverified threshold. Cleanup now records a blocked pin's table, refuses version GC for it in that table's stats row, and protects every link of the blocked chain from the reaper. - Cleanup deleted only the current tip pin's detached manifest, so superseded pins, promoted chain links and attempts that never published accumulated forever. With `--older-than`, cleanup now lists each table location's `_versions/` and reaps every detached manifest older than the threshold that no pending chain protects; without an age policy it reaps none, since a recent manifest may belong to a write in flight. - The snapshot diff filtered `_row_last_updated_at_version` against the linear interval even when the `to` endpoint resolved to a pending pin's detached version, whose rows staged on a detached predecessor carry stamps outside that interval. The same-lineage diff now hands a detached endpoint to the exact ID/content comparison. - The pinned resolver reported reclaimed history when a promotion landed between its target probe and its HEAD read. It now re-reads the exact target once HEAD has reached it, and reports reclaimed history only when the target is still absent. - A writer whose captured snapshot predated another writer's promotion saw the linear HEAD one past its published version and was refused with the non-retryable "run `omnigraph repair`" conflict; concurrent `/change` inserts returned 409 in about half of the runs. The baseline check now reads the current manifest first and returns `ReadSetChanged` when it explains the HEAD, so the writer reprepares; genuinely foreign drift is still refused.
ragnorc
force-pushed
the
claude/rfc-0067-pins-and-promotion
branch
from
September 17, 2026 07:52
3268efd to
7c1b662
Compare
Contributor
Author
|
Consolidated into #744, which carries this stack's complete history merged with current main and revalidated as one tree. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 1 of 2 for RFC 0067 (detached table commits). Stacked on #725, which carries the RFCs and the Lance fences; this PR's base is that branch and retargets to
mainwhen it merges.What lands here
Everything a reader, a promoter and cleanup need to handle a table pin that names a detached Lance version, without any writer producing one yet. On every existing graph this change is inert: no pin carries the new fields, so every path below short-circuits to today's behaviour.
TableVersionMetadatagainsstaged_versionandtransaction_uuid, carried in the manifest row JSON and asomnigraph.staged_version/omnigraph.transaction_uuidin the namespace request metadata map. Absent on every pin published by the sidecar path.open_pinned_dataset, resolves a pin: the linear target when it carries the pin's transaction; the staged version while the target is absent and the linear head is still behind it, or while a foreign commit occupies it;HistoricalVersionReclaimedonce the head has passed an absent target. Both dataset-open paths (the manifest entry opener and the read-handle cache miss) route through it.{read_version}-{uuid}.txnin every manifest, so the resolver, both witnesses and the chain walk read a version's identity from the opened manifest. Only promotion reads the transaction it replays. The existing twin fence inlance_surface_guardsnow pins that name for linear, detached and chained commits.commit_staged_detachedandpromote_detached, plus the puretransaction_identity. Promotion replays the recorded transaction attarget - 1with zero retries, refuses a bareAppend, and reports a landed twin, a refused replay or an unsafe state. Registered inforbidden_apiswith exact call-site counts.db/omnigraph/promotion.rs: promote one pin, check the target first (any process may promote; the same pin is registered on every branch that inherits it), follow read-version links back to the linear base for a chain of detached commits, tolerate reaped predecessors when the base already exists, and report a foreign occupant as blocked while reads stay correct through the staged version.promotion.pre_replaybetween a promotion's existence check and its replay, for the race and crash matrix that step 2 ports.Tests
db/omnigraph/promotion.rs: a detached commit is private until promoted, resolves through its pin, promotes exactly once and refuses a replay over its twin; a chain promotes oldest first and survives a reaped predecessor; a foreign commit at the target blocks promotion, the pin keeps reading its staged effect, and a target pruned behind the head is reclaimed.db/manifest/metadata.rs: the namespace round trip carries both pin fields.lance_surface_guards.rs: the transaction file name assertions on the existing twin fence.forbidden_apis.rs: the new primitives and their single call sites.Run locally:
forbidden_apis,lance_surface_guards,failpoint_names_guard,maintenance,recovery,failpoints,lifecycle,end_to_end,changes,write_cost,warm_read_cost,branching,writes, the engine lib tests, both Clippy graphs,cargo fmt,check-docs.py,typos,check-agents-md.sh,check-workflow-action-pins.py.Step 2
Switches every writer (mutation and load, merge, ensure-indices, Optimize as one detached Rewrite with an exact-base publish) to detached staging, stamps schema v10 with the upgrade route, promotes from held handles, ports the failure-window matrix and the race harness onto seams, and deletes the sidecar machinery, classifier, Restore compensation, recovery modes and drift repair together with their docs.
Review fixes (17 September 2026)
The stack review reproduced four defects in this PR's code once the later writers exercised it; a fifth surfaced as the server's concurrent-insert test returning 409 in about half of the runs. All five are fixed in the commit
engine: close the pin, promotion, diff and cleanup boundaries the RFC 0067 review found, and the regression tests live on #734 where the writers that reach them exist:--older-than, cleanup lists each table location's_versions/and reaps every detached manifest older than the threshold that no pending chain protects: superseded pins, promoted chain links and attempts that never published. Without an age policy it reaps none, since a recent manifest may belong to a write in flight.toendpoint that resolves to a detached version to the exact ID/content comparison, because rows staged on a detached predecessor carry stamps outside the linear interval. A replayed promotion re-stamps, so promoted history keeps the fast path.ReadSetChangedand reprepares. Genuinely foreign drift is still refused with the repair conflict.Docs:
docs/dev/writes.mdanddocs/user/operations/maintenance.mddescribe the blocked-pin GC refusal and the age-gated reaper.