Repository navigation
fix(engine): read a commit of a deleted branch while a live branch retains it - #1240
Conversation
|
Revise. The reported stacked-diff case is fixed. Two readers of the same commit still use the old refusal. WhyIssue #1236 is one broken rule. Merge could open a commit after the branch that wrote it was deleted. Diff, commit changes, a snapshot read, and the change feed could not, while a live branch still reached that commit. What
Blocking
This is the same check the pull request describes as the race fence. It sees a deletion when the polled branch wrote the head. A head inherited from a writer that is still live does not show that the polled branch was deleted. Compare a fresh hash of the polled branch's Lance identifier with
The fast path runs before Send this path through the same decision. When the writer is retired, open the retired ref by exact commit and mark the snapshot retired. The captured head is retention only while this branch is still that incarnation. Add both shapes to Optional
LiabilityThis removes a split contract. The class is every read of a commit whose writing incarnation is gone, while a live branch's parent or merged-parent chain still reaches that commit id. The fast-forward stacked diff is one shape. A child with no local commit, and a handle still bound to the deleted writer, are the same class. The new pieces are a boolean on Live-commit cost, from the code and not from a benchmark: a main commit returns before any manifest read. A live named branch pays the same native-ref resolve that Other open pull requests#1235 and #1184 touch the catalog and the coordinator, and they auto-merge with this head. Recheck both if either lands first. #1184 changes lookup of a commit held on a branch. #1235 changes branch bases. #1241 shares no files with this change. #1204, #1229, and #1175 conflict with current ValidationMerge base On the merge base, with the new
Failed. Step 8 is the On the merge base, with the new integration test appended and the file restored afterward:
Failed at At this head, the same two commands passed. GQT: 1 passed, 2.28s. Integration: 1 passed, 0.24s. A temporary probe at this head, removed afterward, created
CI on this exact head is green, including GQT ordinary, DST, and served, Test Workspace, and clippy. Devin Review did not review the diff. LimitationsI did not measure the live-path or retired-path cost. I did not run a concurrent delete during a poll. I did not run the served GQT target locally. Lance 13.0.0 Inline:
|
…tains it After a stacked branch's parent is fast-forward merged and deleted, branch diff, two-point diff, commit changes, query --snapshot and the change feed failed with not_found on the merge base, while merge succeeded. A commit whose writing incarnation is gone now opens through one door when a live branch still reaches it through parents or merged parents and the retired native ref matches the exact commit; a reclaimed manifest stays a loud gap. The live-incarnation path is unchanged. A commit no live branch retains is the not-found of the caller's spelling, never the deleted branch. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… note Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s retired Review of ModernRelay#1240: the change feed of a live child with no commit of its own, and a handle that captured the deleted parent as its head, still refused the retained commit. admit_commit decides every such read. commit_state (its captured-head fast path included) asks whether any live branch reaches the commit; the change feed asks whether the polled branch is still the incarnation the cut's witness names, by a fresh identifier hash. The feed skips natives already admitted as retired, and a recovered live writer is no longer reported as not found.
|
Rebased on current
One function, Optional points:
#1235 and #1184 had not landed when I rebased. Neither has been rechecked against this change. |
9c4c32f to
b45df1a
Compare
|
Ready. The two blocking findings from the review of WhyIssue #1236 is one broken rule. Merge could open a commit after the branch that wrote it was deleted. Diff, commit changes, a snapshot read, and the change feed could not. A live branch still reached that commit. What
Previous findings
The four optional points from that review are in this head. The feed skips a native already in Where the reply is wider than the codeThe reply says a deleted or recreated polled branch still fails with the incarnation refusal. The witness compare runs only after A Optional
LiabilityThis removes a split contract. The class is every read of a settled commit whose writing incarnation is gone, while the caller's retention still reaches that commit id. Merge already opened that commit. Diff, commit changes, a snapshot read, and the feed now use the same decision. The new pieces are one function, a The live-writer path returns before any manifest walk. The unretained miss opens every live branch, main first. That cost is the right spend. The miss is the refusal path, and it fails closed. A hole in lineage returns not found rather than a partial page. The async function was large enough to break dependents. On This change spends efficiency on the unretained miss so the live path and the single door stay simple. That is the right spend for this contract. Other open pull requests
ValidationMerge base At the head:
2 passed, 0.97s.
1 passed, 2.46s. DST seeds 0 and 42 replayed. This command does not run the served environment. On the merge base, production sources unchanged, with the head's The engine command above, plus a temporary probe
The same GQT command on that base failed in 2.23s. Step 8 is the CI on this exact head: clippy pass, Test Workspace pass (both engine tests pass), Storage Upgrade pass, Azurite pass, CI Gate pass (run LimitationsI did not measure the lineage walk. I did not run a delete concurrent with a poll. Local GQT did not run the served target. CI did, on this head. Lance 13.0.0 Inline:
|
Fixes #1236
What changed
After a stacked branch's parent is fast-forward merged and deleted, the merge base of the child and
mainis a commit written by the deleted branch.branch diff, two-point diff,commit changes,query --snapshotand the change feed refused it withnot_found(ensure_incarnation_live), whilebranch mergeopened it throughpinned_graph_commit_with_session.Those readers now share one door (
GraphCoordinator::settled_commit_state, and the feed'sPollSnapshots::of):ensure_retired_incarnation_opens, built onpinned_graph_commit_with_session). A recreated branch name cannot satisfy the exact match.ReadTargetNotFoundfor the id the caller gave, notbranch 'x' not found. A recreated name keeps its incarnation-witness error.commitinstead ofmerge base.branch create --from-commitof such a commit staysbranch_deleted.docs/user/branching/changes.mdand a changelog note are updated.Not changed
The server's
commit changesroute still authorizesreadon the branch that wrote the commit. A policy that denies the deleted name answers 404 even when a readable live branch retains the commit. Branch diff already authorizes its two live sides.Testing
.gqtcaseissue_1236_stacked_diff_after_parent_delete(branch diff, two-point diff, read at the commit; engine, DST and served). It failed onmainat the post-deletebranch diff.changes::stacked_branch_reads_survive_its_parents_delete_issue_1236(commit changes, feed from the beginning and after the commit, snapshot read, recreated name, unretained commit). It failed onmainwithBranchNotFound { branch: "a" }.changes::history_reads_are_the_same_after_a_merged_branch_is_deleted_and_recreatedpinned the old refusal for a commit main merged; it now asserts the commit opens, and that an unmerged head is not found.changes::,branching::andmerge_integration tests, the engine failpoints suite (including the feed ABA cases), the catalog tests, engine lib tests,cargo dev-clippyandcargo fmt --all --check. These ran withRUST_MIN_STACK=16777216, becausebranching::branch_delete_retires_native_parent_and_cleanup_preserves_live_childoverflows the default stack onmainin this sandbox.