Search before asking
Paimon version
master, 9c7deebbd (2.1-SNAPSHOT)
Compute Engine
Java API (paimon-common). MemorySliceOutput backs the SST block writer, the sorted-index footers and the global-index key serializers.
Minimal reproduce step
MemorySliceOutput.ensureSize grows the buffer by doubling, starting from the current segment size:
int newCapacity = segment.size();
int minNewCapacity = segment.size() + minWritableBytes;
while (newCapacity < minNewCapacity) {
newCapacity <<= 1;
}
Doubling never leaves zero, so with an empty segment the loop condition stays true forever:
MemorySliceOutput out = new MemorySliceOutput(0);
out.writeByte(5); // spins on the CPU and never returns
What doesn't meet your expectations?
The first write should either allocate a buffer or fail. Instead the thread burns a core with no exception, no log line and no progress, which in a Flink or Spark task looks like a hang rather than a bug: the stack trace shows a live thread inside ensureSize and nothing else.
Every construction site in the repository passes a positive size (the smallest is 2, in the global-index key serializer), so no current Paimon code path reaches this. MemorySliceOutput is public in paimon-common and takes the capacity from its caller, so a size computed at runtime that happens to be zero is enough.
Anything else?
Two related things I found while checking this, both older than the zero case and neither fixed here.
newCapacity <<= 1 overflows for a buffer above 2^30: the value walks through negative into 0 and the same loop hangs again. And when segment.size() + minWritableBytes itself overflows, the loop is skipped entirely, ensureSize allocates a same-sized array and returns as though it had grown, after which the caller runs MemorySegment.put, which is an unchecked UNSAFE.copyMemory. HeapBytesVector.calculateNewBytesCapacity in the same module already shows the shape that avoids both, computing in long and clamping against a maximum array size.
AbstractHeapVector.reserveDictionaryIds has the identical doubling-from-zero loop, reachable if a zero-row batch is followed by a non-empty one on the same reused vector.
Are you willing to submit a PR?
Search before asking
Paimon version
master,
9c7deebbd(2.1-SNAPSHOT)Compute Engine
Java API (
paimon-common).MemorySliceOutputbacks the SST block writer, the sorted-index footers and the global-index key serializers.Minimal reproduce step
MemorySliceOutput.ensureSizegrows the buffer by doubling, starting from the current segment size:Doubling never leaves zero, so with an empty segment the loop condition stays true forever:
What doesn't meet your expectations?
The first write should either allocate a buffer or fail. Instead the thread burns a core with no exception, no log line and no progress, which in a Flink or Spark task looks like a hang rather than a bug: the stack trace shows a live thread inside
ensureSizeand nothing else.Every construction site in the repository passes a positive size (the smallest is 2, in the global-index key serializer), so no current Paimon code path reaches this.
MemorySliceOutputis public in paimon-common and takes the capacity from its caller, so a size computed at runtime that happens to be zero is enough.Anything else?
Two related things I found while checking this, both older than the zero case and neither fixed here.
newCapacity <<= 1overflows for a buffer above 2^30: the value walks through negative into 0 and the same loop hangs again. And whensegment.size() + minWritableBytesitself overflows, the loop is skipped entirely,ensureSizeallocates a same-sized array and returns as though it had grown, after which the caller runsMemorySegment.put, which is an uncheckedUNSAFE.copyMemory.HeapBytesVector.calculateNewBytesCapacityin the same module already shows the shape that avoids both, computing inlongand clamping against a maximum array size.AbstractHeapVector.reserveDictionaryIdshas the identical doubling-from-zero loop, reachable if a zero-row batch is followed by a non-empty one on the same reused vector.Are you willing to submit a PR?