Skip to content

Core, Flink: Refactor InputFilesDecryptor and use EncryptingFileIO.bulkDecrypt() - #18198

Open
gaborkaszab wants to merge 1 commit into
apache:mainfrom
gaborkaszab:main_InputFileDecryptor_refactor
Open

gaborkaszab wants to merge 1 commit into
apache:mainfrom
gaborkaszab:main_InputFileDecryptor_refactor

Conversation

@gaborkaszab

Copy link
Copy Markdown
Contributor

Changes this contains:

  • More generic interface with parameters: - Iterable - EncryptingFileIO
  • Contructs InputFiles lazily when first requested
  • EncryptionManager is wrapped into EncryptingFileIO input parameter
  • Use EncryptingFileIO.bulkDecrypt() instead of EncryptionManager.decrypt()
  • Throw if file location is not found
  • Removed usage of old contructor and getInputFile(FileScanTask)

With this design, this class is generic enough to be wired into other readers as a common implementation for creating InputFiles with bulk decryption taken into account. Later, GenericReader and Spark's readers can swap to use this.

One hidden improvement: Previously this class created InputFiles using location + key metadata without the size. While this works, when it comes to reading, it requires an extra RPC to get the size of the file. Now, with bulkDecrypt(), the location + size + key metadata are used together saving an extra RPC per file.


private Map<String, InputFile> inputFiles() {
if (lazyInputFiles == null) {
this.lazyInputFiles = encryptingIO.bulkDecrypt(referencedFiles);

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.

Do we need to synchronize this?

public InputFile getInputFile(String location) {
return decryptedInputFiles.get(location);
InputFile inputFile = inputFiles().get(location);
Preconditions.checkArgument(

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.

It is a behavioural change. Seems acceptable, but I would like to make sure that we are ok with this.

@pvary pvary changed the title Core, Flink: Refactor InputFileDecryptor and use EncryptingFileIO.bulkDecrypt() Core, Flink: Refactor InputFilesDecryptor and use EncryptingFileIO.bulkDecrypt() Sep 21, 2026
void before() {
this.dataInputFile = Mockito.mock(InputFile.class);
this.deleteInputFile = Mockito.mock(InputFile.class);
this.encryptingIO = Mockito.mock(EncryptingFileIO.class);

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 use InMemoryFileIO?

this.encryptingIO = Mockito.spy(EncryptingFileIO.combine(new InMemoryFileIO(), PlaintextEncryptionManager.instance()));

EncryptedFiles.encryptedInput(
io.newInputFile(entry.getKey()), entry.getValue()));
this(
() -> referencedFiles(combinedTask.files()).iterator(),

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.

do we need this lambda? Shall we just call referencedFiles when lazyInputFiles is initialized?

…kDecrypt()

Changes this contains:
  - More generic interface with parameters:
      - Iterable<FileScanTask>
      - EncryptingFileIO
  - Contructs InputFiles lazily when first requested
  - EncryptionManager is wrapped into EncryptingFileIO input parameter
  - Use EncryptingFileIO.bulkDecrypt() instead of EncryptionManager.decrypt()
  - Throw if file location is not found
  - Removed usage of old contructor and getInputFile(FileScanTask)

With this design, this class is generic enough to be wired into other
readers as a common implementation for creating InputFiles with bulk
decryption taken into account. Later, GenericReader and Spark's readers can
swap to use this.

One hidden improvement: Previously this class created InputFiles using
location + key metadata without the size. While this works, when it comes
to reading, it requires an extra RPC to get the size of the file.
Now, with bulkDecrypt(), the location + size + key metadata are used
together saving an extra RPC per file.
@gaborkaszab
gaborkaszab force-pushed the main_InputFileDecryptor_refactor branch from 55ba635 to 4e54fc1 Compare September 21, 2026 18:49
.getValue()
.getMessage())
.contains("File does not exist: ");
.contains("Failed to open input stream for file: ");

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.

This is an error scenario test that verifies that reading from missing files is handled gracefully. The error text changes because we create InputFile differently: via the newInputFile variant that accepts a length.

Originally the test failed when doing an extra RPC to get the length before reading the file:

NotFoundException: File does not exist: /tmp/.../generic-appender-partition-test-...
  at HadoopInputFile.lazyStat(HadoopInputFile.java:166)
  at HadoopInputFile.getLength(HadoopInputFile.java:177)
  at Parquet$ReadBuilder.<init>(Parquet.java:1334)      <-- reads length up front
  at Parquet.read(Parquet.java:1254)
  at ParquetFormatModel.readBuilder(ParquetFormatModel.java:140)
  at RowDataFileScanTaskReader.newIterable(RowDataFileScanTaskReader.java:108)

While now the extra RPC is gone, this fails when trying to actually open the file:

NotFoundException: Failed to open input stream for file: /tmp/.../generic-appender-partition-test-...
  at HadoopInputFile.newStream(HadoopInputFile.java:187)
  at EagerInputFile.newStream(EagerInputFile.java:76)
  at ParquetIO$ParquetInputFile.newStream(ParquetIO.java:254)
  at ParquetFileReader.<init>(ParquetFileReader.java:961)
  at ReadConf.newReader(ReadConf.java:194)

I think this is acceptable. WDYT @pvary ?

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.

2 participants