Repository navigation
Support row lineage assignment on commit - #4102
Open
cayetanobv wants to merge 10 commits into
Open
cayetanobv wants to merge 10 commits into
cayetanobv wants to merge 10 commits into
Conversation
Add ManifestWriterV3 and ManifestListWriterV3. The manifest writer uses the V3 record layout so the V3-only data file fields (first_row_id, referenced_data_file, content_offset, content_size_in_bytes) are written, rebinding V2-layout records when needed. The manifest list writer implements the spec's First Row ID Assignment: preserving existing first_row_id values, never assigning one to delete manifests, and assigning a running value advanced by existing and added row counts to unassigned data manifests. It also exposes next_row_id for the commit path to update the table's next-row-id. Closes apache#3620
Carry an optional first_row_id through ManifestWriterV3 into the produced manifest file (used when rewriting manifests whose first_row_id is known), matching ManifestFiles.newWriter in Java. Expand tests for parity with TestManifestWriterVersions and TestManifestListVersions: reader compatibility of V3-written files, metrics field round-trips, null first_row_id when reading V2 manifest lists with the V3 layout, and preservation of writer-carried first_row_id without advancing next_row_id.
Assigning a first-row-id while treating unknown existing/added row counts as zero would let subsequent manifests overlap row ID ranges. Raise a clear error instead, matching the reference implementation which requires the counts. Also drop the always-None value from the first-row-id-required error message and document the record layout invariant behind the V3 rebinding.
Reading V2/V3 manifests and manifest lists used a fixed V2 read schema, so V3-only fields (first_row_id, referenced_data_file, content_offset, content_size_in_bytes) were silently dropped by Avro schema resolution, breaking V3 round trips. Read with the latest known schema instead, keeping the V2 default for constructing and writing records unchanged. Also fixes ManifestListWriterV3._wrap sharing _data with the source record via a shallow copy, and rebinding a record to the V3 layout against a fixed V2 field count, which raised on V1-bound records.
max() and list indexing on the version-keyed schema dicts widen the Literal[1, 2, 3] TableVersion back to int, so mypy flags the results as incompatible with the declared TableVersion return types.
test_rest_manifest.py has its own todict helper (separate from tests/avro/test_file.py's), which also enumerates every DataFile property via reflection. Adding V3-only properties to DataFile in the previous commit caused this helper to include them for a V2-shaped record too, diverging from the V2 dict fastavro writes and failing the REST integration round-trip test.
DataFile.from_args now rejects fields that are not in the target layout, so rebinding a V1 data file to V3 raised on block_size_in_bytes, which V3 does not have. Generated-by: Claude Code
V3 snapshots set first-row-id but not added-rows, so REST catalogs reject every V3 commit and next-row-id is lost after the first one. Set added-rows to the row IDs that the V3 manifest list writer assigns, as Java does, and remove the unused _calculate_added_rows. Closes apache#3621
cayetanobv
force-pushed
the
feat/row-lineage-on-commit
branch
from
October 13, 2026 07:02
39e7fd9 to
551c5f4
Compare
cayetanobv
marked this pull request as ready for review
October 13, 2026 07:05
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3621 (part of #1551). Stacked on #3624, which adds
ManifestListWriterV3. Only the last commit belongs to this PR; I will rebase once #3624 merges.Rationale for this change
Every commit to a V3 table fails on a REST catalog.
_SnapshotProducer._commitsetsfirst-row-idon the new snapshot but never setsadded-rows, and the spec requires both together. The REST fixture rejects the snapshot:Without a server check, the table metadata goes wrong instead.
add_snapshotcomputesnext-row-idfromadded-rows, sonext-row-idbecomesNoneafter the first commit and the next commit fails.This PR sets
added-rowstowriter.next_row_id - first_row_id, which is the number of row IDsManifestListWriterV3assigned. Java'sSnapshotProducercomputes it the same way. The count includes existing rows in manifests that had nofirst_row_id, sonext-row-idalways moves past every assigned ID.It also removes
_calculate_added_rows. Nothing called it, and it counted onlyadded_rows_countfrom the snapshot's own manifests, which would undercount the rows that the writer assigns.Out of scope: a V3 table created by
new_table_metadatahasnext-row-id = None, so a commit to it still fails. A REST server sets it to 0 on create, which hides this. That belongs with writing V3 metadata (#3551).Rewritten manifests do not keep row IDs on EXISTING entries yet, so a merge append or partial delete moves them. #3972 fixes this by inheriting
first_row_idon read.added-rowsalready matches Java in both cases.Are these changes tested?
Yes. Three new tests in
tests/table/test_snapshots.pycommit to an empty V3 table inside a transaction, because writing a V3metadata.jsonis not supported yet:(first_row_id, added_rows)is(0, 3), then(3, 3), andnext-row-idis 6add_files:(0, 3),next-row-idis 3(3, 2), andnext-row-idis 5All three fail without the change. The unit suite passes locally.
This PR also un-skips the integration test
test_v3_write_and_read_row_lineage. It now appends twice and reads_row_idand_last_updated_sequence_numberwith Spark (Iceberg 1.11.0), which must see row IDs 0 to 5 with sequence numbers 1 and 2. It passes with the change and fails without it on the sameInvalid added-rowserror.I also ran this script against
apache/iceberg-rest-fixture(sha256:db8de90b…) with the RustFS warehouse fromdev/docker-compose-integration.yml:Before, on #3624 (
c152089d):After:
Are there any user-facing changes?
No API changes. Commits to V3 tables through a REST catalog now succeed.
Written with Claude Code (Claude Opus 5.5) and reviewed by me. One path has no test:
added-rowsfor tables upgraded from V2, which follows Java but needs #3622 to exercise.