Repository navigation
Conversation
Row-identity change discovery pairs two table versions' live rows by `_rowid` and skips pairs with equal version stamps, which proves an unchanged row only when the `from` version is on the `to` version's read-version chain. The guard checked graph first-parent ancestry instead. A merge can switch a table's pin to the merged branch's pin, and a fast forward does so for every type the merged branch changed; when that branch had merged the target since the fork, its history never read the target's pin. Sibling first inserts share a row id and a stamp, so the fast forward's commit page, the feed across it, the two-point diff from its parent and a branch review with a fast forward above the base reported the target's own row as inserted and dropped the merged branch's (refused under `verify`). Pairing now requires that `to` descend from `from` through first parents with no merge commit after `from`: the commit page and the feed skip it for a merge commit, and the commit-graph walk behind the two diff forms stops at the first merge commit above `from`. A type with a one-transaction adjacency proof keeps the candidate path; a merge commit's other types take the exact merge, as before row identity.
WhyRow-identity change discovery pairs live rows by A merge can install the other branch's table pin. What this change doesPairing now requires linear first-parent descent, with no merge commit after RecommendationRevise. The first-page rule is the right repair for the reported bug. One later page of a branch diff still applies row identity to a frozen window that crosses a merge. The release-note check also fails on this commit. Blocking
OptionalThe sound condition is each opened table pin's Tradeoff and liabilityRemoving This change removes the silent mis-report on every door that decides the flag once, for the commits it opens. It adds a second decision on the continuation, taken from the live heads. That second decision is the remaining liability. Five more graph-shape exceptions would leave the cause in place. PR 1184 also edits ValidationHead Local, head, pass:
Result: Local, merge base, with those two case files copied in and not committed, fail for the predicted reason: same command. Result: Local, head, temporary test in CI on this commit: LimitationsGQT cannot express a page token. The continuation probe is not in the PR. Please add it under Inline comment on
Those commits differ once the target moves. After main fast-forwards to feature, the live base is the source head, so this call returns true. The next page pairs rows on the frozen window. That window still crosses the merge. A temporary test on this commit showed the wrong row. Page 1 used
The two-point arm above walks the live |
Summary
Change discovery by row identity (#1201) pairs two table versions' live rows by
_rowidand skips pairs whose_row_last_updated_at_versionstamps are equal. That is sound only when thefromtable version is on thetoversion's Lanceread_versionchain. The guard checked graph first-parent ancestry instead, and a merge commit separates the two: a merge can switch a table's pin to the merged branch's pin (classify_adopt's pointer switch, which a fast forward does for every type the merged branch changed), and that pin's history never read the target's pin when the merged branch had merged the target since the fork.Reproduced before the fix:
xand feature insertsy. Both are first commits after the fork, so Lance gives both rows one_rowidand one stamp.xunder a new_rowid.On the unfixed tree, both new GQT cases fail under
verify(the debug default) withrow-identity change discovery verify divergence for 'node:Person': exact merge yields insert 'y', row identity yields insert 'x'. Underon(the release default) the row-identity answer is the one returned, so the two-point diff from main's head before the fast forward reportsinsert xand silently dropsy. The commit page and the change feed take the same row-identity route for the fast-forward commit (ChangeDiscovery::linearsetfrom_is_ancestor: trueunconditionally). The branch form had the same gap: when a fast forward sits above a merge base that is on the source's first-parent chain,branch diff feature into maingave the same wrong answer.Fix
Row identity now requires that
todescend fromfromthrough first parents with no merge commit afterfrom, in all three forms:ChangeDiscovery::for_committurns pairing off when the commit has a merged parent;diff_descends_linearly(formerlydiff_from_is_ancestor);source_descends_linearly_from_base(formerlybase_on_source_chain).The commit-graph walk (
CommitGraph::descends_linearly_from, formerlyfirst_parent_chain_holds) stops at the first merge commit abovefrom. A heldfromwith a merge commit above it no longer answers early, so the coordinator'sholds_commitshortcut is gone; the walk still reads held commits from memory.The adjacency path is unchanged: a type that an interval reaches through one recorded transaction still takes it, whatever commit wrote it.
Cost
A merge commit's page and a window that crosses a merge commit now take the exact ordered merge for every type that has no single-transaction proof. That is what they took before #1201, so nothing regresses against 0.14.1. It does give back part of #1201's gain: three-way merge pages (a chain of insert, update and delete chunks) and clean fast forwards, where pairing was safe. Proving each type's own lineage (its pin's
read_versionchain back tofrom's pin) would bring that back. That proof is left to a follow-up, recorded in the RFC. Thegraph_diff_of_diverged_heads_takes_the_exact_mergecost owner now records the route a window across a fast forward takes.Tests
cases/graph_diff_across_a_fast_forward.gqt: the two-point diff across the fast forward under the default,onandoffsettings.cases/branch_review_across_a_fast_forward_on_the_source.gqt: the branch-form variant.integration/changes.rs::commit_changes_feed_and_diff_across_a_fast_forward_report_the_source_rows: the commit page, feed and two-point diff underoff,onandverify.integration/changes_cost.rs::graph_diff_of_diverged_heads_takes_the_exact_merge: route counters for a window across a fast forward.Docs:
docs/dev/lance.md(Row version stamps),docs/user/branching/changes.md, the RFC's ancestry restriction and decision log. The unreleasedgraph-diff-row-identity.changed.mdnote is narrowed to windows with no merge.