Skip to content

API, Core, Spark, Flink: extract SnapshotFile abstraction - #17523

Open
stevenzwu wants to merge 1 commit into
apache:mainfrom
stevenzwu:snapshot_file_extract
Open

API, Core, Spark, Flink: extract SnapshotFile abstraction#17523
stevenzwu wants to merge 1 commit into
apache:mainfrom
stevenzwu:snapshot_file_extract

Conversation

@stevenzwu

@stevenzwu stevenzwu commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Introduce SnapshotFile as the version-agnostic name for the top-level file a snapshot points at, so later v4 work can address a root manifest through the same type that v3 uses for a manifest list. Snapshot.snapshotFileLocation() defaults to manifestListLocation(), which is now deprecated; FileIO, EncryptingFileIO, EncryptionUtil, and ReachableFileUtil grow SnapshotFile overloads with the manifest-list variants deprecated as delegating wrappers.

Evolves the SnapshotsTable metadata schema to expose the location under a version-agnostic name: adds an optional snapshot_file column (field ID 9) populated for every snapshot. For tables at v3 and earlier, manifest_list is still populated with the same value so existing consumers are unaffected; for tables at v4+, manifest_list is null, dispatched via the new package-private TableMetadata.MIN_FORMAT_VERSION_ADAPTIVE_MANIFEST_TREE constant.

Alternative considered: a separate Snapshot#rootManifestLocation()

A more literal encoding would keep manifestListLocation() for v3 and add a sibling rootManifestLocation() for v4+. That was rejected for a few reasons:

  • Every caller that just wants "where is this snapshot's top-level file" would have to branch on format version and call one accessor or the other. snapshotFileLocation() collapses that decision into the interface — v3 snapshots return their manifest list, v4+ snapshots return their root manifest, and callers stop caring about the difference.
  • The abstraction has to extend past SnapshotFileIO.newInputFile(...), EncryptingFileIO, and EncryptionUtil.decryptSnapshotFileKeyMetadata(...) all need to accept "the file this snapshot points at" without knowing the format version. A single SnapshotFile type lets those signatures stay uniform; twin ManifestListFile/RootManifestFile types would fork every such API.
  • The manifest list (v3) and the root manifest (v4+) play the same structural role in a snapshot — they are the one file the snapshot addresses directly, and everything else is reached transitively. A single name reflects that shared role and keeps Snapshot from carrying two mutually-exclusive accessors long-term.

@stevenzwu
stevenzwu force-pushed the snapshot_file_extract branch 3 times, most recently from 51e06e3 to ee6b97b Compare August 5, 2026 04:12
}

@Override
public String snapshotFileLocation() {

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.

Maybe we should call this rootLocation? It feels weird to have to do snapshot.snapshotFileLocation() because the method name sounds duplicative in the context of already being within a snapshot. and I think rootLocation also generalizes to v4+ and older format version as the manifest list can still be considered a root of the metadata tree.

Comment on lines +25 to +26
* The top-level file that a {@link Snapshot} points at. For v3 and earlier this is a manifest list
* (see {@link ManifestListFile}); for v4+ it is a root manifest carrying a mix of data-file entries

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.

I guess I'm not following, why do we need this interface? Regardless of V4 or earlier we have a root file but we just need the location and all the other things like encryption key etc should all just work as they do today.

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.

Ah okay, for decryption we have a bunch of code that takes in ManifestListFile

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.

I think same principle as my other naming comment, maybe we want to just call this RootFile? It doesn't attach this interface too hard to the "Snapshot" though of course for the forsseable future Snapshot will always have a pointer to a single root. Not as opinionated on this one though

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I prefer something like ContentRoot - this is self explanatory and also applies to previous manifest list files also.

*
* @return the location of the snapshot file for this Snapshot
*/
default String snapshotFileLocation() {

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.

same as my other comment, I feel like we should just call it rootLocation? It generalizes to both v3 and v4 imo and feels less awkawrd than snapshot.snapshotFileLocation()?

Comment on lines +25 to +26
* The top-level file that a {@link Snapshot} points at. For v3 and earlier this is a manifest list
* (see {@link ManifestListFile}); for v4+ it is a root manifest carrying a mix of data-file entries

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I prefer something like ContentRoot - this is self explanatory and also applies to previous manifest list files also.

import org.apache.iceberg.encryption.EncryptionManager;
import org.apache.iceberg.encryption.EncryptionUtil;

class BaseSnapshotFile implements SnapshotFile, Serializable {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is not used anywhere yet?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah. this will be used in the v4 code path, which is not integrated yet. I can remove this for now and introduce it when we actually use it.

snap.operation(),
snap.manifestListLocation(),
snap.summary());
adaptive ? null : location,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We might be missing tests for the adaptive being true branch?


private static StaticDataTask.Row snapshotToRow(Snapshot snap) {
private static StaticDataTask.Row snapshotToRow(Snapshot snap, int formatVersion) {
boolean adaptive = formatVersion >= TableMetadata.MIN_FORMAT_VERSION_ADAPTIVE_MANIFEST_TREE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A tree upgraded from v3 to v4 would have adaptive true at the table level, but there could be snapshots that are v3 produced snapshots. How do we handle it correctly? It seems a bit tricky.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

good catch. this is a bug. I have some change locally that is not extracted properly in this PR. I intended to add this new API to the Snapshot interface.

  default int formatVersion() {
    return ManifestFile.LEGACY_FORMAT_VERSION;
  }

I should have checked snap.formatVersion() != ManifestFile.LEGACY_FORMAT_VERSION. I should also add a test coverage for this scenario.

Introduce SnapshotFile as the version-agnostic name for the top-level file a
snapshot points at, so later v4 work can address a root manifest through the
same type that v3 uses for a manifest list. Snapshot.snapshotFileLocation()
defaults to manifestListLocation(), which is now deprecated; FileIO,
EncryptingFileIO, EncryptionUtil, and ReachableFileUtil grow SnapshotFile
overloads with the manifest-list variants deprecated as delegating wrappers.

Evolve the SnapshotsTable metadata schema to expose the location under a
version-agnostic name: add an optional snapshot_file column (field ID 9)
populated for every snapshot. Whether the row also populates manifest_list is
dispatched per-snapshot via a new default Snapshot.formatVersion() (returning
the new ManifestFile.LEGACY_FORMAT_VERSION sentinel); v3-and-earlier snapshots
retain their manifest_list value, and adaptive v4+ snapshots — which report a
real format version — set it to null. Per-snapshot dispatch is required so
v3-produced snapshots on a v3->v4 upgraded table keep reporting manifest_list
correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@stevenzwu
stevenzwu force-pushed the snapshot_file_extract branch from ee6b97b to 0073109 Compare August 6, 2026 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants