Skip to content

feat: add CompositeOperation for multi-statement transactions#7737

Draft
jackye1995 wants to merge 3 commits into
lance-format:mainfrom
jackye1995:composite-operation
Draft

feat: add CompositeOperation for multi-statement transactions#7737
jackye1995 wants to merge 3 commits into
lance-format:mainfrom
jackye1995:composite-operation

Conversation

@jackye1995

@jackye1995 jackye1995 commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Adds CompositeOperation: an ordered list of existing operations applied sequentially as one atomic commit, enabling multi-statement transactions such as an atomic append + index update (with fragment IDs reserved via ReserveFragments so index metadata can reference them).

Design

  • Wire format: CompositeOperation { repeated Transaction transactions } as oneof field 116 in transaction.proto (field 115 is intentionally left for the in-flight feat(transaction): compound append + add-index via action transactions #7674). Only each sub-transaction's operation is meaningful; the outer transaction governs read_version/uuid/tag/transaction_properties.
  • Apply: build_manifest folds sub-operations sequentially through intermediate manifests, with the version pinned to the single committed version — row-level version metadata on stable-row-id datasets is stamped with the final version. Each sub-operation is validated against the intermediate manifest it actually applies to.
  • Conflict resolution: pure delegation, no new pairwise rules. An in-flight composite holds one TransactionRebase per sub-operation; a committed composite is decomposed at the top of check_txn. Two composites conflict iff any pair of their sub-operations conflicts.
  • Structural rules: no Restore, no Clone (both are applied outside build_manifest in the commit driver), no nesting, non-empty. Anything else composes, including Overwrite-first to atomically create a dataset with config.
  • Compatibility: manifests are unchanged and no feature flag is needed. Composite transactions are inlined into the manifest transaction section like any other operation. Old writers racing a composite fail their commit fail-safe — the same behavior as any previously added operation type — and succeed once they read past it. Readers eagerly decode the inline transaction section on dataset open and older releases fail on operation types they cannot decode; that is a pre-existing issue affecting every new operation type, fixed independently in fix: don't fail dataset open on an undecodable inline transaction #7740 (the fix is included here until that merges).
  • Python: LanceOperation.Composite(operations=[...]) through the existing LanceDataset.commit. Java binding will follow in a separate PR.

Adds a Composite operation that applies an ordered list of existing
operations as one atomic commit. Sub-operations reuse the existing
build_manifest apply logic (a sequential fold with the version pinned to
the single committed version) and the existing TransactionRebase
conflict rules by delegation on both sides of a conflict check.

Composite transactions are not inlined into the manifest so released
readers can still open post-composite versions; the eager inline
transaction decode in load_manifest is also made tolerant so future
operation types no longer prevent opening a dataset. Restore, Clone,
nested composites, and empty composites are rejected.
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bbe33f03-1f07-419f-8ca0-71ba1fec4c6d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Important

This PR touches the Lance format specification.

Substantive changes to the format specification — the .proto definitions
and the spec docs under docs/src/format/ — require a PMC vote before merge.
Minor edits such as typo fixes, wording, or formatting are excluded; use your
judgment.

If this is a meaningful format change:

  • Start a vote following the Lance community voting process.
    Format specification modifications need 3 binding +1 votes (excluding the
    proposer), held on GitHub Discussions, with a minimum voting period of 1 week.
  • Once the vote passes, link the completed vote in this PR. It should not be
    merged until the vote is linked.

@github-actions github-actions Bot added A-python Python bindings A-format On-disk format: protos and format spec docs enhancement New feature or request labels Jul 11, 2026
- Reject sub-operations that reference fragment ids assigned to
  fragments added earlier in the same composite: those ids shift when
  the commit is rebuilt on conflict retry and would silently retarget
  another writer's fragment. Explicitly reserved ids stay allowed.
- Apply the outer transaction's tag to the final manifest.
- Keep the inline transaction section when disable_transaction_file is
  set so a composite commit always leaves a transaction record.
- Stamp row-version metadata with the true previous version for
  non-first Update sub-operations on stable-row-id datasets.
- Advance the outer read_version after sub-operation rebases.
- Recognize overwrite-leading composites in Dataset::commit and the
  CommitBuilder storage-format check.
- Surface unsupported operations as errors instead of panics across the
  JNI boundary; warn when affected_rows is ignored for composites.
@github-actions github-actions Bot added the A-java Java bindings + JNI label Jul 11, 2026
Composites now write the inline manifest transaction section normally.
The inability of released readers to decode an unknown inline operation
is an independent robustness problem: the opportunistic decode on
dataset open now tolerates undecodable transactions (log and skip)
instead of failing the open, which protects against any future
operation type.
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 A-java Java bindings + JNI A-python Python bindings enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant