Skip to content

API, Core: Introduce a generic abstraction to replace ManifestListFile - #17545

Open
gaborkaszab wants to merge 4 commits into
apache:mainfrom
gaborkaszab:main_encryptable_file
Open

API, Core: Introduce a generic abstraction to replace ManifestListFile#17545
gaborkaszab wants to merge 4 commits into
apache:mainfrom
gaborkaszab:main_encryptable_file

Conversation

@gaborkaszab

Copy link
Copy Markdown
Contributor

ManifestListFile and its implementation contains nothing that is specific to manifest lists. It is more generally related to files that use TableMetadata.encryptionKeys to store encrypted encryption key metadata that are referred to by a key ID.

This PR introduces a more general interface that can be used accross multiple file types like manifest lists, V4 root manifests, table statistics and partition statistics. The less general functionality specific to manifest lists is deprecated or removed where possible.

* @deprecated will be removed in 2.0.0; use {@link #newInputFile(EncryptableFile)} instead.
*/
@Deprecated
default InputFile newInputFile(ManifestListFile manifestList) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I dropped BaseManifestListFile because that was package private. Now, there is no implementation in the library that can call this function, still I don't think we can drop this, because it'd break API for users that happen to implement their own ManifestListFile. Not likely, but technically feasible.

* A file that may be encrypted. If it is encrypted, its encrypted key metadata is tracked in the
* table metadata encryption keys and is referenced by a key ID.
*/
public interface EncryptableFile {

@stevenzwustevenzwuAug 7, 2026

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.

Naming: "Encryptable" describes a capability every file has, not the specific concept/state represented here. I would suggest EncryptedFile and let the Javadoc cover the "may or may not carry a keyId" case.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Changed to EncryptedFile, however, I'm still not comfortable with the vile name. The name suggests the file is encrypted, but it's may or may not. Maybe FileWithEncryptedKey ?

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.

let's see if others have a better name than EncryptedFile.

We do have PlaintextEncryptionManager for no encryption too.

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 feel FileWithEncryptedKey is more accurate.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Renamed the interface to FileWithEncryptedKey

* The file key metadata can be encrypted. Returns ID of encryption key or null if it's not
* encrypted.
*/
String encryptionKeyID();

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.

  • encryptionKeyID()keyId() for consistency with Snapshot.keyId() and EncryptedKey.keyId() (ID caps is out of style — even the local variables in EncryptionUtil are manifestListKeyId).
  • Javadoc suggestion: /** Returns the encryption key ID for this file, or null if the file is not encrypted. */ — the interface-level javadoc already covers the mapping to TableMetadata.encryptionKeys(), so the method sentence can stay short.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for the comment suggestion! Applied.

About the function name: I'm not sure we can change the function name, because that would break the API for ManifestListFile that is derived from this interface.
Alternatively, we can avoid deriving ManifestListFile from the new interface, but then we won't be able to cast it to the new interface and delegate to the new function signature like in FileIO or in EncryptionUtil:
newInputFile((EncryptableFile) manifestList)

@stevenzwustevenzwuAug 9, 2026

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.

if we want to, we can still rename it to a new method in the EncryptedFile interface and keep the old method in ManifestListFile with a default impl that just forwards.

but I am also ok to keep the old name

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Could there technically be custom implementations of ManifestListFile out there overriding encryptionKeyID to not delegate to the new function, that would break the design here, because internally we would only call the new version of the function?
I think it's cleaner to keep the same API that we had before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I went through this again, and I think I managed to come up with a solution that isn't a breaking change for custom implementations of ManifestListFile. See EncryptionUtil.decryptKeyMetadata functions where I introduced a variation that accepts the key ID from the various versions.

Comment threadapi/src/main/java/org/apache/iceberg/ManifestListFile.java Outdated
Comment threadcore/src/main/java/org/apache/iceberg/encryption/EncryptionUtil.java Outdated
@stevenzwustevenzwu changed the title API, Core: Refactor: Introduce a more general abstraction to replace ManifestListFileAPI, Core: Introduce a generic EncryptedFile abstraction to replace ManifestListFileAug 7, 2026
@gaborkaszab
gaborkaszabforce-pushed the main_encryptable_file branch from 0a3b903 to c08b3fdCompareAugust 8, 2026 10:50

@gaborkaszabgaborkaszab left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for taking a look, @stevenzwu !

* A file that may be encrypted. If it is encrypted, its encrypted key metadata is tracked in the
* table metadata encryption keys and is referenced by a key ID.
*/
public interface EncryptableFile {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Changed to EncryptedFile, however, I'm still not comfortable with the vile name. The name suggests the file is encrypted, but it's may or may not. Maybe FileWithEncryptedKey ?

* The file key metadata can be encrypted. Returns ID of encryption key or null if it's not
* encrypted.
*/
String encryptionKeyID();

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for the comment suggestion! Applied.

About the function name: I'm not sure we can change the function name, because that would break the API for ManifestListFile that is derived from this interface.
Alternatively, we can avoid deriving ManifestListFile from the new interface, but then we won't be able to cast it to the new interface and delegate to the new function signature like in FileIO or in EncryptionUtil:
newInputFile((EncryptableFile) manifestList)

Comment threadapi/src/main/java/org/apache/iceberg/ManifestListFile.java Outdated
Comment threadcore/src/main/java/org/apache/iceberg/encryption/EncryptionUtil.java Outdated

@varun-lakhyanivarun-lakhyani 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.

Overall LGTM.

  • Data types / return types are consistently migrated to EncryptedFile.
  • Deprecation javadocs are clear
  • ManifestListFile's own deprecation javadoc + extends EncryptedFile explains most of the seemingly-confusing type/return changes across the PR (e.g. toManifestListFile() returning EncryptedFile).

Comment threadcore/src/main/java/org/apache/iceberg/encryption/EncryptionUtil.java Outdated
@gaborkaszabgaborkaszab changed the title API, Core: Introduce a generic EncryptedFile abstraction to replace ManifestListFileAPI, Core: Introduce a generic abstraction to replace ManifestListFileAug 14, 2026
* @param manifestList a ManifestListFile
* @param em the table's EncryptionManager
* @return a decrypted key metadata buffer
* @deprecated since 1.12.0. Will be removed in 1.13.0; use {@link

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.

Shouldn't the removal version be the next major version (2.0.0) as you have done in the other deprecation tag in this PR?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

In core/ we can remove stuff in the next minor release, while in api/ it's stricter, the next major is the closest one for removal.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Giving this a second thought I think you're right. As long as we keep the ManifestListFile interface (until 2.0.0 as it's in api/) we have to keep the encryption/decryption methods for that class. Otherwise it might be a behavior change for custom implementation if we silently fall back to the method for EncryptedFile.

String encryptionKeyID();

/** Decrypt and return the file key metadata */
ByteBuffer decryptKeyMetadata(EncryptionManager em);

@anoopjanoopjAug 19, 2026

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.

Carry over from existing design, but this method feels like it belongs on the encryption service rather than on the file value type. The file only supplies the key ID. I don't think it can simply be dropped, though: the caller (EncryptingFileIO) is in api and EncryptionUtil is in core, so api can't call it directly.

If we want it off EncryptedFile, the clean version is to move the operation onto EncryptionManager (impl already in core, and EncryptingFileIO already holds one), keyed by encryptionKeyID().

It's fine if we want to do in a followup.

cc @rdblue in case he has any thoughts.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, with this the only purpose of putting this functionality into EncryptionManager is that its implementation in core/ can delegate to EncryptionUtil to get around the issue that EncryptingFileIO.newInputFile() is in api/ and can't call the util method in core/. I'm not entirely comfortable with that design, while I'm not comfortable with the current one either. At least we keep existing api with the current one.

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 likes Anoop suggestion here of dropping this method and let callers use the EncryptionUtil.decryptXXX directly. Since we are defining a new interface, it seems like a good opportunity to deprecate. Then this class becomes just a POJO envelope interface.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I gave this a try and in the latest version of this PR you can see how this can be implemented. I dropped the decryptKeyMetadata method from the new interface. However, to be able to use the underlying EncryptionUtil.decryptKeyMetadata() that is in core/, from EncryptingFileIO that is in api/, I had to introduce the same in EncryptionManager. Basically, this is inline with @anoopj 's suggestion. Let me know what you think!

@gaborkaszabgaborkaszab left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks for looking into this @stevenzwu@huaxingao@anoopj !
I renamed the new interface, also went for the simpler method keyId() while keeping compatibility with custom ManifestListFile implementations.

* @param manifestList a ManifestListFile
* @param em the table's EncryptionManager
* @return a decrypted key metadata buffer
* @deprecated since 1.12.0. Will be removed in 1.13.0; use {@link

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Giving this a second thought I think you're right. As long as we keep the ManifestListFile interface (until 2.0.0 as it's in api/) we have to keep the encryption/decryption methods for that class. Otherwise it might be a behavior change for custom implementation if we silently fall back to the method for EncryptedFile.

* The file key metadata can be encrypted. Returns ID of encryption key or null if it's not
* encrypted.
*/
String encryptionKeyID();

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I went through this again, and I think I managed to come up with a solution that isn't a breaking change for custom implementations of ManifestListFile. See EncryptionUtil.decryptKeyMetadata functions where I introduced a variation that accepts the key ID from the various versions.

String encryptionKeyID();

/** Decrypt and return the file key metadata */
ByteBuffer decryptKeyMetadata(EncryptionManager em);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hmm, with this the only purpose of putting this functionality into EncryptionManager is that its implementation in core/ can delegate to EncryptionUtil to get around the issue that EncryptingFileIO.newInputFile() is in api/ and can't call the util method in core/. I'm not entirely comfortable with that design, while I'm not comfortable with the current one either. At least we keep existing api with the current one.

Comment threadcore/src/main/java/org/apache/iceberg/encryption/EncryptionUtil.java Outdated
* A file that may be encrypted. If it is encrypted, its encrypted key metadata is tracked in the
* table metadata encryption keys and is referenced by a key ID.
*/
public interface EncryptableFile {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Renamed the interface to FileWithEncryptedKey

…ManifestListFile
ManifestListFile and its implementation contains nothing that is specific
to manifest lists. It is more generally related to files that use
TableMetadata.encryptionKeys to store encrypted encryption key metadata
that are referred to by a key ID.
This PR introduces a more general interface that can be used accross
multiple file types like manifest lists, V4 root manifests, table
statistics and partition statistics. The less general functionality
specific to manifest lists is deprecated or removed where possible.
* A file that may be encrypted. If it is encrypted, its encrypted key metadata is tracked in the
* table metadata encryption keys and is referenced by a key ID.
*/
public interface FileWithEncryptedKey {

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 know we have gone through a couple of iterations on naming (EncryptableFile, EncryptedFile), which is a bit tricky in this case.

Just throwing out a couple more for considerations: FileWithKeyId or FileWithEncryptionKeyId?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I like FileWithKeyId, renamed the new interface to that. Thanks for the suggestion @stevenzwu !

Also, one minor correction to ManifestListFile: We can't default
encryptionKeyId() to return keyId, because custom implementations
might not implement keyId(), hence would be broken.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

5 participants

@gaborkaszab@anoopj@stevenzwu@huaxingao@varun-lakhyani