Skip to content

feat(format): add cell preconditions to the transaction format - #9121

Open
lichuang wants to merge 1 commit into
lance-format:mainfrom
lichuang:issue-8976-proto
Open

feat(format): add cell preconditions to the transaction format#9121
lichuang wants to merge 1 commit into
lance-format:mainfrom
lichuang:issue-8976-proto

Conversation

@lichuang

Copy link
Copy Markdown
Contributor

feat(format): add cell preconditions to the transaction format

Part of #8976. Vote-only PR: proto change, format specification, and compile-required edits. The builder and commit-loop implementation is intentionally excluded and lands in the follow-up #9118, per the persisted-format change process.

Contract being accepted

A transaction may carry preconditions: declarations that specific cells must be unchanged relative to the transaction's read_version when the commit lands. A cell is a (field, physical row address) pair; each precondition declares a set of cells as the cartesian product of field ids and a serialized row-address set (RowAddrTreeMap).

Motivating case: read-compute-write workloads such as publishing a derived column y = f(x). The staged values are only valid if the cells of x they were computed from still hold the same contents. Conflict compatibility cannot guard this — a read-x-write-y transaction is compatible with a concurrent update of x, and rebasing over it silently publishes stale values.

Semantics fixed by this spec

The specification (docs/src/format/table/transaction.md, new "Preconditions" section) pins the points that must be decided language-agnostically, before any implementation fixes them by accident:

  1. Compositionpreconditions is repeated and conjunctive: every entry must hold. A violation rejects the commit; it is never rebased over the changed cells.
  2. Absent / empty / invalid — absent or empty means no conditions. Entries with empty field_ids or empty rows are degenerate (declare no cells); producers must not emit them. Consumers must reject undecodable rows or negative field ids as corrupt transactions.
  3. "Unchanged" — defined conservatively in manifest-structure terms, evaluated against the pinned read_version state: fragment removal, data-file or deletion-file replacement, or any overlay affecting declared cells added / modified / removed / narrowed counts as a change; an overlay already present at the read version and unchanged does not. Implementations may over-reject but must never under-reject.

The rows wire format is fully specified inline (u32 entry count; per fragment: u32 fragment id, u32 bitmap byte length, roaring bitmap of offsets; zero length = whole fragment selected). Stable-row-id preconditions are explicitly reserved for a future revision.

Compatibility

  • Proto: additive field (repeated Precondition preconditions = 5 on Transaction; field 5 previously unused). Old readers ignore it; new readers see it absent for old writers. No field numbers reused or retyped.
  • The feature is inert until a producer emits it: no behavior change for any existing transaction.
  • transaction.proto is a stable persisted contract (transaction logs and inline manifest transactions), so this change requires the format-spec vote.

@github-actions github-actions Bot added A-format On-disk format: protos and format spec docs format-change A change to the format spec, which requires a vote. Remove if minor (e.g. fixing typo). enhancement New feature or request labels Sep 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Important

Format specification vote

This 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 — 0 of 3 required approvals

Approvals (this commit) none (0/3)
Vetoes none
Voting period ends Tue 2026-09-15 05:43 UTC (Mon 22:43 PDT)

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 format-waived label to waive the vote for a trivial edit (typo, wording, formatting).

lance-gatekeeper[bot]

This comment was marked as outdated.

@lance-gatekeeper lance-gatekeeper Bot added the K-changes Latest Gatekeeper recommendation requests changes. label Sep 10, 2026
Defines the Transaction.Precondition message: a declared set of cells
(fields × physical row addresses) that must be unchanged relative to the
transaction's read_version for the commit to land. Rows are encoded as
per-fragment RowSelection entries (whole-fragment flag or portable
Roaring bitmap of offsets). The spec pins the composition semantics
(conjunctive), absent/empty behavior, invalid encoding handling, and the
conservative meaning of "unchanged" — declared fields must still exist
with unchanged data type and nullability, and over-rejection is
permitted while under-rejection is forbidden.

This is the format-vote half of lance-format#8976; it carries only the proto change,
the docs/src/format specification, and compile-required edits. The
builder and commit-loop implementation lands in a follow-up PR (lance-format#9118).
@lance-gatekeeper lance-gatekeeper Bot removed the K-changes Latest Gatekeeper recommendation requests changes. label Sep 10, 2026

@lance-gatekeeper lance-gatekeeper Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate recommendation: approve.

The revision closes both contract gaps: row selections are now structured in protobuf with portable Roaring payloads, and the contract requires protected-field existence, type, and nullability to remain unchanged relative to read_version. The additive format remains inert for existing transactions, with implementation correctly sequenced in #9118.

@lichuang

Copy link
Copy Markdown
Contributor Author

https://github.com/lance-format/lance/actions/runs/34446896028/job/102773541990?pr=9121 the failed case dataset::scanner::test::test_scan_limit_offset is not related with this pr.

relocates the declared rows counts as a change; transparent remapping through compaction is not
required.
- If the fragment's data files or deletion file differ from the read version, the declared cells
are changed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to consider deletion to be a change? If we declare some cells and unrelated cells in the fragment get deleted then ideally that would not be an issue. Even if the declared cells are deleted, ideally that shouldn't cause recomputation of non-deleted values. The deleted/needlessly-computed values will be masked by the deletion vector.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-format On-disk format: protos and format spec docs enhancement New feature or request format-change A change to the format spec, which requires a vote. Remove if minor (e.g. fixing typo). K-approved Latest Gatekeeper recommendation permits acceptance.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants