Skip to content

Add ManageSnapshots.cherry_pick_snapshot - #3769

Open
1fanwang wants to merge 2 commits into
apache:mainfrom
1fanwang:cherry-pick-snapshot
Open

Add ManageSnapshots.cherry_pick_snapshot#3769
1fanwang wants to merge 2 commits into
apache:mainfrom
1fanwang:cherry-pick-snapshot

Conversation

@1fanwang

@1fanwang 1fanwang commented Aug 7, 2026

Copy link
Copy Markdown

Rationale for this change

pyiceberg can stage a write on a branch and validate it there, but cannot publish it. Write and Audit work; Publish does not, so the last step happens outside Python — typically a Spark session calling system.cherrypick_snapshot.

This adds ManageSnapshots.cherry_pick_snapshot(), mirroring Java's CherryPickOperation:

  • An append is replayed as a new snapshot on top of current, carrying the data files it added and recording source-snapshot-id, plus published-wap-id when staged with a wap.id.
  • Any other operation is fast-forwarded to when its parent is already current, and rejected otherwise with Java's message.
  • Picking an existing ancestor is a no-op, and a wap.id may only be published once.

Not covered: dynamic overwrite (OVERWRITE with replace-partitions), which Java also replays. It raises here rather than silently doing nothing. Worth a follow-up.

Prior art

#750 by @chinmay-bhat implemented this in 2024 and was closed by the stale bot with no review on merit; credit for getting there first is theirs. I rewrote rather than rebased, since ManageSnapshots has moved to pyiceberg/table/update/snapshot.py and the producer API changed underneath it. This one is narrower: a single method, no publish_changes(wap_id) and no dedicated producer class, since fast_append() already does the work.

@chinmay-bhat — glad to hand this back if you would rather carry it.
Touches the same class as #3649. Happy to rebase onto whichever lands first and reuse its helper and exception types.

Are these changes tested?

Integration, against the REST catalog and Hive metastore from dev/docker-compose-integration.yml. It stages a branch write with a wap.id, advances main so the pick is a replay not a fast-forward, publishes, then checks the summary properties and that a second publish is refused.

Red, with pyiceberg/table/update/snapshot.py at upstream/main
$ git checkout upstream/main -- pyiceberg/table/update/snapshot.py
$ pytest tests/integration/test_snapshot_operations.py -m integration -k cherry_pick -q

E       AttributeError: 'ManageSnapshots' object has no attribute 'cherry_pick_snapshot'
tests/integration/test_snapshot_operations.py:366: AttributeError
FAILED ...::test_cherry_pick_snapshot_publishes_staged_write[session_catalog_hive]
FAILED ...::test_cherry_pick_snapshot_publishes_staged_write[session_catalog]
2 failed, 23 deselected
Green, with the change restored
$ pytest tests/integration/test_snapshot_operations.py -m integration -k cherry_pick -v

test_cherry_pick_snapshot_publishes_staged_write[session_catalog_hive] PASSED
test_cherry_pick_snapshot_publishes_staged_write[session_catalog] PASSED

The table goes from [1, 9] to [1, 2, 3, 9], the published snapshot carries
source-snapshot-id and published-wap-id=etl-001, and re-publishing raises
Duplicate request to cherry pick wap id that was published already: etl-001.

Ran the docs example verbatim too: 1 row before publish, 3 after, wap id recorded.
Unit, across the memory, sql, and sql_without_rowcount catalogs: replay onto a moved-on main, wap id recorded, duplicate publish rejected, append replayed when its parent is current, non-append fast-forwarded, ancestor no-op, and both rejection paths. Same red on unpatched source.

Whole unit and integration suites pass; prek run -a clean.

Are there any user-facing changes?

ManageSnapshots.cherry_pick_snapshot(snapshot_id), plus a Write-Audit-Publish section in mkdocs/docs/api.md.

Publishing a Write-Audit-Publish staged write means replaying a snapshot's
changes onto the current table state. pyiceberg can stage the write on a branch
and validate it there, but has no way to publish it, so the last step of the
loop has to happen outside Python.

Add cherry_pick_snapshot(), mirroring Java's CherryPickOperation: an append
snapshot is replayed as a new snapshot on top of current carrying its added data
files, recording source-snapshot-id and published-wap-id. A snapshot with
another operation is fast-forwarded to when its parent is already current, and
rejected otherwise. Picking an existing ancestor is a no-op, and a wap.id may
only be published once.

Dynamic overwrite is not covered; it raises rather than silently doing nothing.

Signed-off-by: 1fanwang <1fannnw@gmail.com>
Copilot AI lite review requested due to automatic review settings August 7, 2026 22:58

Copilot AI 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.

Pull request overview

Adds a ManageSnapshots.cherry_pick_snapshot(snapshot_id) API to support Write-Audit-Publish (WAP) flows in PyIceberg by publishing a staged branch snapshot onto the current table state, mirroring Iceberg Java’s cherry-pick behavior.

Changes:

  • Added ManageSnapshots.cherry_pick_snapshot() implementation, including WAP summary properties (source-snapshot-id, published-wap-id) and duplicate WAP publish protection.
  • Added unit and integration tests covering replay vs fast-forward behavior, ancestor no-op, and error paths.
  • Documented the WAP workflow in the public API docs.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
pyiceberg/table/update/snapshot.py Implements ManageSnapshots.cherry_pick_snapshot() plus helpers for replaying appended files and validating WAP publish semantics.
tests/table/test_manage_snapshots.py Adds unit tests for replay, fast-forward, ancestor no-op, unknown snapshot, non-append rejection, and WAP id duplication.
tests/integration/test_snapshot_operations.py Adds an integration test validating WAP publish behavior against REST/Hive catalogs.
mkdocs/docs/api.md Adds a Write-Audit-Publish usage section for cherry_pick_snapshot.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread mkdocs/docs/api.md Outdated
Comment on lines +1514 to +1516
If the table has not changed since the branch was cut, the branch is fast-forwarded rather than
replayed. Picking a snapshot that is already an ancestor of the current state does nothing. Only
append snapshots can be replayed; anything else raises.
The Write-Audit-Publish section described the earlier draft, where a pick was
fast-forwarded when the table had not moved. Appends are always replayed, so the
wap trail is recorded either way; fast-forward is the fallback for non-appends
whose parent is current.

Signed-off-by: 1fanwang <1fannnw@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants