Skip to content

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

Open
pj-workspace wants to merge 2 commits into
apache:mainfrom
pj-workspace:fix/vector-unloader-exception-cleanup
Open

GH-1234: Release serialization buffers when compression fails#1290
pj-workspace wants to merge 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

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;
}
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

3 participants