Repository navigation
feat(transaction): record the Iceberg-Rust writer version information in snapshot summaries - #3363
RussellSpitzer wants to merge 13 commits into
Conversation
A commit from iceberg-rust was indistinguishable from any other client that omits writer identity. Stamp engine-name, engine-version, and iceberg-version on every snapshot so mixed-writer tables can attribute the commit. Generated-by: Cursor Grok 4.7
Generated-by: Cursor Grok 4.7
iceberg-version still identifies this library. engine-name and engine-version are filled only when the caller did not set them, so an embedding engine can record itself through snapshot properties. Generated-by: Cursor Grok 4.7
A data file and an unrelated property do not affect those keys. Also list the new summary constants alphabetically. Generated-by: Cursor Grok 4.7
| let tx = Transaction::new(&table); | ||
|
|
||
| let mut snapshot_properties = HashMap::new(); | ||
| snapshot_properties.insert("key".to_string(), "val".to_string()); |
There was a problem hiding this comment.
Exists to force a commit
| const ICEBERG_VERSION_PROP: &str = "iceberg-version"; | ||
|
|
||
| fn apply_writer_identity(properties: &mut HashMap<String, String>) { | ||
| let version = env!("CARGO_PKG_VERSION"); |
There was a problem hiding this comment.
I don't know the convention here, but the LLMs tell me that this could be a crate level constant but recommended not doing that if we are only using it here.
There was a problem hiding this comment.
This is correct, when release a new version we will bump versions for all crates and they should be consistent: https://github.com/apache/iceberg-rust/blob/main/Cargo.toml#L91-L98
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The focused implementation and tests match the requested metadata precedence, with no blocking issues identified.
Review effort: Balanced
Findings: None
What changed in this PR
Adds writer identity to snapshot summaries so clients can identify snapshots created by iceberg-rust.
Changes:
- Defaults engine identity while preserving caller overrides; always records the library version.
- Adds tests for default identity and override precedence.
| File | Description |
|---|---|
| crates/iceberg/src/transaction/snapshot.rs | Applies writer identity after summary aggregation. |
| crates/iceberg/src/transaction/append.rs | Tests default metadata and caller override handling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const ICEBERG_VERSION_PROP: &str = "iceberg-version"; | ||
|
|
||
| fn apply_writer_identity(properties: &mut HashMap<String, String>) { | ||
| let version = env!("CARGO_PKG_VERSION"); |
There was a problem hiding this comment.
This is correct, when release a new version we will bump versions for all crates and they should be consistent: https://github.com/apache/iceberg-rust/blob/main/Cargo.toml#L91-L98
engine-name and engine-version name the embedding engine, which this library does not know. Callers can still set them. iceberg-version remains the library identity, matching Java and Go. Generated-by: Cursor Grok 4.7
Those keys are ordinary snapshot properties now, so preserving a caller value is already covered by the existing property test. Generated-by: Cursor Grok 4.7
The snapshot summary no longer writes those keys, so the test only checks iceberg-version. Generated-by: Cursor Grok 4.7
mbutrovich
left a comment
There was a problem hiding this comment.
Thanks @RussellSpitzer, just minor stuff at this point from me.
| use crate::transaction::ActionCommit; | ||
| use crate::{Error, ErrorKind, TableRequirement, TableUpdate}; | ||
|
|
||
| const ICEBERG_VERSION_PROP: &str = "iceberg-version"; |
There was a problem hiding this comment.
Should ICEBERG_VERSION_PROP live in spec/snapshot_summary.rs with the other summary keys (snapshot_summary.rs#L27-L50)? That module defines every other key the library writes into additional_properties.
Co-authored-by: Matt Butrovich <mbutrovich@users.noreply.github.com>
The transaction path still writes the value. The property name now lives with the rest of the summary keys the library defines. Generated-by: Cursor Grok 4.7
The key stays private next to the other summary keys, and the commit path calls set_iceberg_version after the totals are computed. Generated-by: Cursor Grok 4.7
mbutrovich
left a comment
There was a problem hiding this comment.
Thanks @RussellSpitzer!
anoopj
left a comment
There was a problem hiding this comment.
LGTM. Left a couple of comments.
laskoviymishka
left a comment
There was a problem hiding this comment.
lgtm — nice that set_iceberg_version sits in SnapshotProducer::summary(), so every producer-based action picks it up for free. And overwriting a caller-supplied iceberg-version is the right call: Java (SnapshotProducer.summary applies EnvironmentContext.get() last) and Go (rebaseSummary) both apply environment context after user properties, so the library value wins.
A few notes, none blocking:
- The PR description says a value supplied through
set_snapshot_propertiesis kept, but the code (and the new test) overwrite it. The code's doing the right thing here — it's the description that's backwards, so I'd just fix the body, and maybe add a///on the helper noting it always overwrites. - The version string puts
Rustwhere Java/Go put the version number, which breaks anApache Iceberg <ver>prefix match across clients — left an inline with a suggestion. - One stray blank-line drop in the imports — minor, flagged inline.
mbutrovich
left a comment
There was a problem hiding this comment.
LGTM!
Could you restore the deleted blank like mentioned above? Would love to keep the format clean before merging
If a committer applies a suggestion that's one way to resolve it without the author. I think this would work.
Resolve the snapshot.rs conflict with the truncate_full_table() change, and commit the iceberg-version test through commit_with_fresh_state since TransactionAction is stateful after apache#3265. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
kevinjqliu
left a comment
There was a problem hiding this comment.
LGTM
i tested this locally by writing and it works!
{
"operation": "append",
"iceberg-version": "Apache Iceberg Rust 0.10.1",
"added-data-files": "1",
"added-records": "3",
"added-files-size": "890",
"total-data-files": "1",
"total-records": "3",
"total-files-size": "890",
"total-delete-files": "0",
"total-position-deletes": "0",
"total-equality-deletes": "0"
}
|
thanks for the reviews, everyone! |
Which issue does this PR close?
Closes #3365
What changes are included in this PR?
Snapshots committed by iceberg-rust now record the library that wrote them:
iceberg-version=Apache Iceberg Rust <crate version>, alwaysengine-nameandengine-versionare left to the caller. Those name the embedding engine, which this library does not know. A value supplied throughset_snapshot_propertiesis kept. This matches Java and Go, whose default environment context contains onlyiceberg-version. PyIceberg also writesengine-nameandengine-version(apache/iceberg-python#3441).Other caller properties are unchanged. Existing snapshots are left alone.
Are these changes tested?
Yes. A unit test on
FastAppendActionchecks that a plain append recordsiceberg-version.cargo test -p iceberg --lib writer_identityandcargo clippy -p iceberg --lib --tests -- -D warningspass.AI Disclosure
Grok 4.7 in Cursor wrote the implementation, tests, and this description. The change has not been reviewed by a human yet.