-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Core: ManifestListWriter cleanup around encryption keys #18150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,22 +36,21 @@ abstract class ManifestListWriter implements FileAppender<ManifestFile> { | |
| private final FileAppender<ManifestFile> writer; | ||
| private final StandardEncryptionManager standardEncryptionManager; | ||
| private final NativeEncryptionKeyMetadata manifestListKeyMetadata; | ||
| private final OutputFile outputFile; | ||
| private boolean closed = false; | ||
| private ManifestListFile manifestListFile; | ||
| private FileEncryptionKeys encryptionKeys; | ||
|
|
||
| private ManifestListWriter( | ||
| OutputFile file, EncryptionManager encryptionManager, Map<String, String> meta) { | ||
| OutputFile outputFile; | ||
| if (encryptionManager instanceof StandardEncryptionManager) { | ||
| // ability to encrypt the manifest list key is introduced for standard encryption. | ||
| this.standardEncryptionManager = (StandardEncryptionManager) encryptionManager; | ||
| EncryptedOutputFile encryptedFile = this.standardEncryptionManager.encrypt(file); | ||
| this.outputFile = encryptedFile.encryptingOutputFile(); | ||
| outputFile = encryptedFile.encryptingOutputFile(); | ||
| this.manifestListKeyMetadata = (NativeEncryptionKeyMetadata) encryptedFile.keyMetadata(); | ||
| } else { | ||
| this.standardEncryptionManager = null; | ||
| this.outputFile = file; | ||
| outputFile = file; | ||
| this.manifestListKeyMetadata = null; | ||
| } | ||
|
|
||
|
|
@@ -98,25 +97,16 @@ public Long nextRowId() { | |
| return null; | ||
| } | ||
|
|
||
| public ManifestListFile toManifestListFile() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that the dependency is weird, it's what I mentioned here already; we should clean that part up. But I think there's a good argument for having a In our case the consumer is just SnapshotProducer So what I was thinking was a good way to clean this up would be So we get rid of the bad dependency ordering and still keep (imo) a useful API. WDYT?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine as it is btw, we can always add functions later, but just wanted to point out that the toManifestListFile was something thought through and not rushed :) but agree on cleaning up the dependency/state, that's a good thing.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the feedback, @amogh-jahagirdar ! As you pointed out |
||
| Preconditions.checkState(closed, "Cannot build ManifestListFile, writer is not closed"); | ||
| if (manifestListFile == null) { | ||
| if (manifestListKeyMetadata != null && manifestListKeyMetadata.encryptionKey() != null) { | ||
| this.encryptionKeys = | ||
| standardEncryptionManager.registerKeyMetadata( | ||
| manifestListKeyMetadata.copyWithLength(writer.length())); | ||
| this.manifestListFile = | ||
| new BaseManifestListFile(outputFile.location(), encryptionKeys.fileKey().keyId()); | ||
| } else { | ||
| this.manifestListFile = new BaseManifestListFile(outputFile.location(), null); | ||
| } | ||
| FileEncryptionKeys encryptionKeys() { | ||
| Preconditions.checkState(closed, "Cannot build encryption keys, writer is not closed"); | ||
| if (encryptionKeys == null | ||
| && manifestListKeyMetadata != null | ||
| && manifestListKeyMetadata.encryptionKey() != null) { | ||
| this.encryptionKeys = | ||
| standardEncryptionManager.registerKeyMetadata( | ||
| manifestListKeyMetadata.copyWithLength(writer.length())); | ||
| } | ||
|
|
||
| return manifestListFile; | ||
| } | ||
|
|
||
| FileEncryptionKeys encryptionKeys() { | ||
| toManifestListFile(); | ||
| return encryptionKeys; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -356,7 +356,6 @@ public Snapshot apply() { | |
| replacedRecords); | ||
| } | ||
|
|
||
| ManifestListFile manifestListFile = writer.toManifestListFile(); | ||
| this.manifestListEncryptionKeys = writer.encryptionKeys(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we do what I mentioned above, I think we can effectively get rid of this state on SnapshotProducer, and just pass in manifestListFile.encryptionKeys(). So it trades off more state in ManifestListFile (OutputFile) for getting rid of state in SnapshotProducer, which I think is better as as it really shouldn't be SnapshotProducer's responsibility imo. |
||
|
|
||
| return new BaseSnapshot( | ||
|
|
@@ -370,7 +369,7 @@ public Snapshot apply() { | |
| manifestList.location(), | ||
| nextRowId, | ||
| assignedRows, | ||
| manifestListFile.encryptionKeyID()); | ||
| manifestListEncryptionKeys != null ? manifestListEncryptionKeys.fileKey().keyId() : null); | ||
| } | ||
|
|
||
| private void runValidations(Snapshot parentSnapshot) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -353,15 +353,12 @@ private ManifestFile writeAndReadEncryptedManifestList(EncryptionManager em) thr | |
| SEQ_NUM, | ||
| SNAPSHOT_FIRST_ROW_ID); | ||
| try (writer) { | ||
| assertThatThrownBy(writer::toManifestListFile) | ||
| assertThatThrownBy(writer::encryptionKeys) | ||
| .isInstanceOf(IllegalStateException.class) | ||
| .hasMessage("Cannot build ManifestListFile, writer is not closed"); | ||
| .hasMessage("Cannot build encryption keys, writer is not closed"); | ||
| writer.add(TEST_MANIFEST); | ||
| } | ||
|
|
||
| ManifestListFile manifestListFile = writer.toManifestListFile(); | ||
| assertThat(writer.toManifestListFile().encryptionKeyID()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One possible issue with removing this is that then we're not effectively testing the |
||
| .isEqualTo(manifestListFile.encryptionKeyID()); | ||
| FileEncryptionKeys encryptionKeys = writer.encryptionKeys(); | ||
|
|
||
| // First try to read without decryption | ||
|
|
@@ -375,7 +372,11 @@ private ManifestFile writeAndReadEncryptedManifestList(EncryptionManager em) thr | |
| io, | ||
| EncryptionTestHelpers.createEncryptionManager( | ||
| List.of(encryptionKeys.keyEncryptionKey(), encryptionKeys.fileKey())))) { | ||
| List<ManifestFile> manifests = ManifestLists.read(readingIO.newInputFile(manifestListFile)); | ||
| List<ManifestFile> manifests = | ||
| ManifestLists.read( | ||
| readingIO.newInputFile( | ||
| new BaseManifestListFile( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note, #17545 will get rid of |
||
| outputFile.location(), encryptionKeys.fileKey().keyId()))); | ||
| assertThat(manifests).hasSize(1); | ||
| return manifests.get(0); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only usage of the
outputFilemember outside of the constructor was to create aManifestListFileintoManifestListFile(), but then it was unused later on. Now that the functions are merged, no need for this memberThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, even if we do my proposed approach we can drop ManifestListFile. Though in my approach, we would need OutputFile, which I feel like is very reasonable and consistent with what we do for "regular" manifests
iceberg/core/src/main/java/org/apache/iceberg/ManifestWriter.java
Line 45 in f1c6206