feat(format): define a unified tagged fragment reuse history - #9136
feat(format): define a unified tagged fragment reuse history#9136LuQQiu wants to merge 10 commits into
Conversation
|
Important Format specification voteThis PR modifies the Lance format specification, so it requires 3 binding +1 votes from PMC members (excluding the proposer) and a minimum 72-hour voting period, weekends excluded, before it can merge. Vote by approving this PR (+1) or requesting changes (−1, a veto). See the voting process. Status: ❌ Blocked — 1 of 3 required approvals
Updated automatically by the format-spec vote gate, which re-checks every 15 minutes — just voted? Re-check now (press Run workflow; leave the input blank to re-check every open format PR). A PMC member may apply the |
Bit 9 is taken by the stable-row-id FRI compatibility flag (#9119), so the tagged-history capability moves to bit 1024. A rewrite and its transition may land in separate commits: transitions reference only committed fragments, and unmapped destinations are served by scanning, so correctness never depends on the mapping being present; atomic composition is deferred to a future composite transaction mechanism. Tables using stable row IDs do not support tagged histories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016RynpAxtwGB9Q9CL4JCvR4
A rewrite replaces the FRI entry with its mapping in the same commit; append_fri_transitions remains the separate-commit path for mappings produced apart from their rewrite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016RynpAxtwGB9Q9CL4JCvR4
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016RynpAxtwGB9Q9CL4JCvR4
…nsitions Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016RynpAxtwGB9Q9CL4JCvR4
The durable record is the FRI entry in the manifest; how a writer carries the mapping to its commit is implementation domain, as it has been since the FRI launched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016RynpAxtwGB9Q9CL4JCvR4
c0a98b3 to
c0f0159
Compare
|
Re the gate concern about durable transaction semantics for the rewrite mapping: This is how the FRI has operated since it launched, and this PR does not change it. A deferred rewrite has never carried its mapping in the serialized transaction message: the durable record is the FRI entry in the manifest's index section, replaced in the same commit ( Independent writers do not need to reconstruct another writer's delta from its transaction file, because that delta is already materialized in its committed manifest entry. The correctness requirement is on the writer side: assemble the entry against the current manifest at each commit attempt, never splice an entry pre-assembled from a stale snapshot. With that rule, a retrying writer reloads the latest entry (which contains the other writer's record) and appends its own in-memory delta, so both records survive. Double consumption is detectable from the rewrite groups' fragment sets, which are serialized. The reproducer is real, and it demonstrates an implementation defect in the current legacy writer (stale-snapshot splicing), not a format gap: the tagged-history writer in the implementation stack already reassembles at every commit attempt inside conflict resolution, and a matching fix for the legacy path can follow separately. The spec text has been adjusted to state only the durable fact (the entry replacement in the manifest) and leaves the transport to the implementation, where it has always lived. |
| } | ||
|
|
||
| // TODO: compose with Rewrite atomically when Transaction V2 supports composite operations. | ||
| message AppendFragmentReuseTransitions { |
There was a problem hiding this comment.
The standalone AppendFragmentReuseTransitions operation seems premature here, since Rewrite can already publish the FRI entry atomically. Could we defer it until there is a concrete caller for separate mapping publication or composite transactions? That would let us review the durable operation’s semantics alongside its actual use, while keeping this PR focused on the unified history format.
There was a problem hiding this comment.
This is pre-added for composite transaction V2 at the assumption that transaction V2 will be landed soon. In that case, having a separate AppendFragmentReuseTransitions can help make the transaction more clear. I can remove it now and add it back later when transaction V2 is ready
There was a problem hiding this comment.
Agreed, removed in 66232d4: the operation, its oneof entry, and the related doc sections (the Appending FRI Transitions section and the separate-commit wording). Rewrite remains the only publication path, atomic with its own commit. We can bring a durable append back together with a concrete caller or composite transactions.
Remove AppendFragmentReuseTransitions from the transaction proto and its documentation. A rewrite already publishes the FRI entry atomically in its own commit; a durable append operation can be reviewed alongside a concrete caller or composite transactions when one exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016RynpAxtwGB9Q9CL4JCvR4
There was a problem hiding this comment.
✅ Gate recommendation: approve.
Deferring the standalone append operation keeps this format change focused on the concrete atomic path: a rewrite publishes its FRI mapping in the same commit as fragment replacement. The unified tagged history, compatibility fence, and fail-closed adapter behavior remain coherent, and the removed field had no released compatibility obligation.
Please mark this PR with the breaking-change label.
|
+1 binding from me |
FRI keeps existing indices usable after fragment rewrites by translating old physical row addresses to new ones. Today it supports order-preserving compaction. This proposal extends the same system index to support stable partitioning, with both mapping types sharing one fragment-lineage history.
Following the unified FRI proposal, source/destination lineage stays in FRI details, while large mapping payloads remain in separate immutable files.
One history, multiple mappings
Continue storing FRI information in a single
__lance_frag_reusesystem-index entry. Keep the existingInlineContent/ExternalFileenvelope and the original field number for legacy versions. Add tagged transitions alongside them:Sources and destinations define the common rewrite graph. Each mapping defines how to translate row offsets. Legacy groups can be read as ordered-compaction transitions; mixed histories follow fragment lineage, not the order of records or dataset version numbers.
Lightweight metadata, external row maps
Ordered compaction retains its compact bitmap representation. Stable partition assigns each physical source row a nullable
uint16destination label, preserving source order within each destination. A null label means the row was deleted. A counts matrix lets readers reconstruct destination offsets without reading all preceding labels.Stable-partition metadata records
map_id,map_size_bytes, and optionalbase_id. The labels and counts are stored in_fri/<map_id>/stable_partition.lance. Mapping identity is independent of the FRI index UUID: updating the history rewrites its metadata, but does not rewrite existing row-map files. The history can be opened without loading labels; address translation reads the required blocks.Publication and compatibility
AppendFragmentReuseTransitionsexpresses a transition delta. Combined atomically with a fragment rewrite, it lets the commit apply the delta to the current history and publish destination fragments and their mappings together. The persisted FRI details remain a snapshot of that history.Scope and validation
This PR contains protobuf definitions, the corresponding format documentation, the proposed flag constant, and minimal compile adapters. It does not enable tagged-history reads or writes. Mapping implementations and reader integration follow in #9106 → #9064 → #9067 → #9068 → #9107.
Replaces #9065 as the standalone spec at the bottom of native stack #9137, based on main
31d78d170.cargo fmt --alland whitespace checks pass. Clippy and tests are blocked by dependency resolution: main requiresobject_store_opendal 0.60.1, while the crates.io index currently offers only up to 0.60.0.