Skip to content

GH-1234: Release serialization buffers when compression fails - #1290

Merged
jbonofre merged 2 commits into
apache:mainfrom
pj-workspace:fix/vector-unloader-exception-cleanup
Oct 5, 2026
Merged

jbonofre merged 2 commits into
apache:mainfrom
pj-workspace:fix/vector-unloader-exception-cleanup

Conversation

@pj-workspace

@pj-workspace pj-workspace commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

What's Changed

When record batch serialization fails after retaining or compressing buffers, VectorUnloader can leave those buffers alive even after the source vectors are closed. Compression allocation failures can also leave an extra reference to the input buffer in AbstractCompressionCodec.

Release collected batch buffers if traversal or batch construction throws, while preserving the original exception. Use try-with-resources in AbstractCompressionCodec.compress so the codec releases the input it owns on both success and failure. Cleanup remains in the component that owns each buffer: the unloader does not release an input already handed to a custom codec.

Regression coverage checks allocator exhaustion with zero allocation headroom, runtime exceptions and errors injected on the second compression call, an empty input buffer, and a malformed later vector. The compression-only test stub is explicitly named SimulatedLz4Codec; it exercises allocation and ownership, not LZ4 round trips. The tests assert memory usage and reference counts return to their previous values and that the source data remains readable. Additional tests exercise allocation failure with the real LZ4 and ZSTD codecs.

The memory-leak report and initial outer-cleanup proposal are from #1234; this change also handles the codec's input ownership.

Validation

  • Before the fix: the initial regression run exposed retained references and unreleased allocations; the 5 existing unload/load tests passed.
  • After the review update: 10 vector cases pass on each of Netty and Unsafe; 53 compression cases pass, including LZ4/ZSTD allocation failures and compressed stream/file round trips (73 test executions, no failures/errors/skips).
  • JDK 17 compilation and Checkstyle passed. Spotless check passed on JDK 23; git diff --check passed.
mvn -B -ntp -pl compression -am test -Dtest=TestVectorUnloadLoad,TestVectorUnloaderFailure,TestCompressionCodec,TestArrowReaderWriterWithCompression,TestCompressionCodecServiceProvider -Dsurefire.failIfNoSpecifiedTests=false
  • No full repository test run, native dataset/Flight integration run or performance benchmark is claimed.

Closes #1234.

AI assistance: OpenAI Codex was used for implementation and local validation.

Copilot AI lite review requested due to automatic review settings September 10, 2026 03:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

This comment has been minimized.

@lidavidm lidavidm added the bug-fix PRs that fix a big. label Sep 10, 2026
@lidavidm
lidavidm requested a balanced review from Copilot September 10, 2026 04:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment on lines +38 to +55
@ParameterizedTest
@ValueSource(longs = {0, 16})
void compressionAllocationFailureReleasesBuffers(long availableBytes) {
try (BufferAllocator allocator = new RootAllocator();
IntVector vector = new IntVector("values", allocator);
VectorSchemaRoot root = VectorSchemaRoot.of(vector)) {
vector.allocateNew(1);
vector.set(0, 42);
root.setRowCount(1);
long allocatedBefore = allocator.getAllocatedMemory();
int referencesBefore = vector.getDataBuffer().getReferenceManager().getRefCount();
allocator.setLimit(allocatedBefore + availableBytes);
VectorUnloader unloader = new VectorUnloader(root, true, new CopyCodec(), true);

assertThrows(OutOfMemoryException.class, unloader::getRecordBatch);
assertEquals(allocatedBefore, allocator.getAllocatedMemory());
assertEquals(referencesBefore, vector.getDataBuffer().getReferenceManager().getRefCount());
assertEquals(42, vector.get(0));
Comment on lines +145 to +148
@Override
public CompressionUtil.CodecType getCodecType() {
return CompressionUtil.CodecType.LZ4_FRAME;
}

@jbonofre jbonofre left a comment

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.

Thanks for the fix and good test coverage @pj-workspace!

The separation between VectorUnloader and AbstractCompressionCodec is clean and strictly adheres to the CompressionCodec contract:

  • using try (uncompressedBuffer) in AbstractCompressionCodec.compress() ensures the input buffer reference on all exit paths (empty check, compression failure, packaging fallback) without double-freeing
  • the rollback cleanup via AutoCloseable.close(e, buffers) in VectorUnloader.getRecordBatch() reliably frees collected buffers and preserves the root exception
  • the regression test suite is great.

(nit and non blocker: we could apply similar try-with-resource semantics to AbstractCompressionCodec.decompress() and align StructVectorUnloader in the C module)

@jbonofre
jbonofre force-pushed the fix/vector-unloader-exception-cleanup branch from dc61ee7 to a5bf863 Compare October 5, 2026 11:11
@jbonofre
jbonofre merged commit eb733ad into apache:main Oct 5, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug-fix PRs that fix a big.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Java] VectorUnloader has multiple memory leak issues

4 participants