This is an automated email from the ASF dual-hosted git repository.
guoweijie pushed a commit to branch release-1.19
in repository https://gitbox.apache.org/repos/asf/flink.git
The following commit(s) were added to refs/heads/release-1.19 by this push:
new 6045bd8bf93 [FLINK-35169][runtime] Recycle buffers to freeSegments
before releasing data buffer for sort accumulator
6045bd8bf93 is described below
commit 6045bd8bf935b12c8056e98945e054511da9c251
Author: Yuxin Tan <[email protected]>
AuthorDate: Fri Apr 19 16:40:55 2024 +0800
[FLINK-35169][runtime] Recycle buffers to freeSegments before releasing
data buffer for sort accumulator
---
.../tiered/storage/SortBufferAccumulator.java | 92 +++++++++++++++-------
.../tiered/storage/SortBufferAccumulatorTest.java | 44 +++++++++++
2 files changed, 108 insertions(+), 28 deletions(-)
diff --git
a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/storage/SortBufferAccumulator.java
b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/storage/SortBufferAccumulator.java
index 12e63aba55d..4c9d3cf7e79 100644
---
a/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/storage/SortBufferAccumulator.java
+++
b/flink-runtime/src/main/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/storage/SortBufferAccumulator.java
@@ -30,6 +30,7 @@ import
org.apache.flink.runtime.io.network.partition.hybrid.tiered.common.Tiered
import org.apache.flink.util.function.TriConsumer;
import javax.annotation.Nullable;
+import javax.annotation.concurrent.GuardedBy;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -65,6 +66,7 @@ public class SortBufferAccumulator implements
BufferAccumulator {
private final int bufferSizeBytes;
/** The empty buffers without storing data. */
+ @GuardedBy("lock")
private final LinkedList<MemorySegment> freeSegments = new LinkedList<>();
/** The memory manager of the tiered storage. */
@@ -78,7 +80,9 @@ public class SortBufferAccumulator implements
BufferAccumulator {
* data integrity. Note that this can be null before using it to store
records, and this {@link
* DataBuffer} will be released once flushed.
*/
- @Nullable private TieredStorageSortBuffer currentDataBuffer;
+ @GuardedBy("lock")
+ @Nullable
+ private TieredStorageSortBuffer currentDataBuffer;
/**
* The buffer recycler. Note that this can be null before requesting
buffers from the memory
@@ -97,6 +101,11 @@ public class SortBufferAccumulator implements
BufferAccumulator {
/** Whether the current {@link DataBuffer} is a broadcast sort buffer. */
private boolean isBroadcastDataBuffer;
+ @GuardedBy("lock")
+ private boolean isDataBufferReleased;
+
+ private final Object lock = new Object();
+
public SortBufferAccumulator(
int numSubpartitions,
int numBuffers,
@@ -123,38 +132,44 @@ public class SortBufferAccumulator implements
BufferAccumulator {
boolean isBroadcast)
throws IOException {
int targetSubpartition = subpartitionId.getSubpartitionId();
- switchCurrentDataBufferIfNeeded(isBroadcast);
- if (!checkNotNull(currentDataBuffer).append(record,
targetSubpartition, dataType)) {
- return;
- }
+ synchronized (lock) {
+ switchCurrentDataBufferIfNeeded(isBroadcast);
+ if (!checkNotNull(currentDataBuffer).append(record,
targetSubpartition, dataType)) {
+ return;
+ }
- // The sort buffer is empty, but we failed to write the record into
it, which indicates the
- // record is larger than the sort buffer can hold. So the record is
written into multiple
- // buffers directly.
- if (!currentDataBuffer.hasRemaining()) {
- currentDataBuffer.release();
- writeLargeRecord(record, targetSubpartition, dataType);
- return;
+ // The sort buffer is empty, but we failed to write the record
into it, which indicates
+ // the record is larger than the sort buffer can hold. So the
record is written into
+ // multiple buffers directly.
+ if (!currentDataBuffer.hasRemaining()) {
+ isDataBufferReleased = true;
+ currentDataBuffer.release();
+ writeLargeRecord(record, targetSubpartition, dataType);
+ return;
+ }
+ flushDataBuffer();
}
- flushDataBuffer();
checkState(record.hasRemaining(), "Empty record.");
receive(record, subpartitionId, dataType, isBroadcast);
}
@Override
public void close() {
- flushCurrentDataBuffer();
- releaseFreeBuffers();
- if (currentDataBuffer != null) {
- currentDataBuffer.release();
+ synchronized (lock) {
+ flushCurrentDataBuffer();
+ isDataBufferReleased = true;
+ releaseFreeBuffers();
+ if (currentDataBuffer != null) {
+ currentDataBuffer.release();
+ }
}
}
// ------------------------------------------------------------------------
// Internal Methods
// ------------------------------------------------------------------------
-
+ @GuardedBy("lock")
private void switchCurrentDataBufferIfNeeded(boolean isBroadcast) {
if (isBroadcast == isBroadcastDataBuffer
&& currentDataBuffer != null
@@ -164,9 +179,12 @@ public class SortBufferAccumulator implements
BufferAccumulator {
}
isBroadcastDataBuffer = isBroadcast;
flushCurrentDataBuffer();
+ isDataBufferReleased = true;
currentDataBuffer = createNewDataBuffer();
+ isDataBufferReleased = false;
}
+ @GuardedBy("lock")
private TieredStorageSortBuffer createNewDataBuffer() {
requestBuffers();
@@ -181,6 +199,7 @@ public class SortBufferAccumulator implements
BufferAccumulator {
isPartialRecordAllowed);
}
+ @GuardedBy("lock")
private void requestBuffers() {
while (freeSegments.size() < numBuffers) {
Buffer buffer = requestBuffer();
@@ -191,6 +210,7 @@ public class SortBufferAccumulator implements
BufferAccumulator {
}
}
+ @GuardedBy("lock")
private void flushDataBuffer() {
if (currentDataBuffer == null
|| currentDataBuffer.isReleased()
@@ -214,14 +234,17 @@ public class SortBufferAccumulator implements
BufferAccumulator {
flushBuffer(bufferWithSubpartition,
numRemainingConsecutiveBuffers);
} while (true);
+ isDataBufferReleased = true;
releaseFreeBuffers();
currentDataBuffer.release();
}
private void flushCurrentDataBuffer() {
- if (currentDataBuffer != null) {
- flushDataBuffer();
- currentDataBuffer = null;
+ synchronized (lock) {
+ if (currentDataBuffer != null) {
+ flushDataBuffer();
+ currentDataBuffer = null;
+ }
}
}
@@ -251,11 +274,13 @@ public class SortBufferAccumulator implements
BufferAccumulator {
}
private MemorySegment getFreeSegment() {
- MemorySegment freeSegment = freeSegments.poll();
- if (freeSegment == null) {
- freeSegment = requestBuffer().getMemorySegment();
+ synchronized (lock) {
+ MemorySegment freeSegment = freeSegments.poll();
+ if (freeSegment == null) {
+ freeSegment = requestBuffer().getMemorySegment();
+ }
+ return freeSegment;
}
- return freeSegment;
}
private void flushBuffer(
@@ -278,11 +303,22 @@ public class SortBufferAccumulator implements
BufferAccumulator {
}
private void releaseFreeBuffers() {
- freeSegments.forEach(this::recycleBuffer);
- freeSegments.clear();
+ synchronized (lock) {
+ isDataBufferReleased = true;
+ freeSegments.forEach(this::recycleBuffer);
+ freeSegments.clear();
+ }
}
private void recycleBuffer(MemorySegment memorySegment) {
- checkNotNull(bufferRecycler).recycle(memorySegment);
+ synchronized (lock) {
+ if (!isDataBufferReleased
+ && currentDataBuffer != null
+ && !currentDataBuffer.isReleased()) {
+ freeSegments.add(memorySegment);
+ } else {
+ checkNotNull(bufferRecycler).recycle(memorySegment);
+ }
+ }
}
}
diff --git
a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/storage/SortBufferAccumulatorTest.java
b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/storage/SortBufferAccumulatorTest.java
index 50b705730d1..ed093c2b9d7 100644
---
a/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/storage/SortBufferAccumulatorTest.java
+++
b/flink-runtime/src/test/java/org/apache/flink/runtime/io/network/partition/hybrid/tiered/storage/SortBufferAccumulatorTest.java
@@ -18,9 +18,12 @@
package org.apache.flink.runtime.io.network.partition.hybrid.tiered.storage;
+import org.apache.flink.core.memory.MemorySegment;
import org.apache.flink.runtime.io.network.buffer.Buffer;
+import org.apache.flink.runtime.io.network.buffer.BufferBuilder;
import org.apache.flink.runtime.io.network.buffer.BufferPool;
import org.apache.flink.runtime.io.network.buffer.NetworkBufferPool;
+import
org.apache.flink.runtime.io.network.partition.hybrid.tiered.TestingTieredStorageMemoryManager;
import
org.apache.flink.runtime.io.network.partition.hybrid.tiered.common.TieredStorageSubpartitionId;
import org.junit.jupiter.api.AfterEach;
@@ -202,6 +205,47 @@ class SortBufferAccumulatorTest {
assertThat(tieredStorageMemoryManager.numOwnerRequestedBuffer(bufferAccumulator)).isZero();
}
+ @Test
+ void testReuseRecycledBuffersWhenFlushDataBuffer() throws IOException {
+ int numBuffers = 10;
+ int numSubpartitions = 10;
+
+ AtomicInteger numRequestedBuffers = new AtomicInteger(0);
+ TestingTieredStorageMemoryManager memoryManager =
+ new TestingTieredStorageMemoryManager.Builder()
+ .setRequestBufferBlockingFunction(
+ owner -> {
+ numRequestedBuffers.incrementAndGet();
+ MemorySegment memorySegment =
+
globalPool.requestPooledMemorySegment();
+ // When flushing data from the data
buffer, the buffers should
+ // not be requested from here anymore
because the freeSegments
+ // can be reused before released.
+ assertThat(numRequestedBuffers.get() <=
numBuffers).isTrue();
+ return new BufferBuilder(
+ memorySegment,
+ segment ->
globalPool.requestPooledMemorySegment());
+ })
+ .build();
+
+ SortBufferAccumulator bufferAccumulator =
+ new SortBufferAccumulator(
+ numSubpartitions, numBuffers, BUFFER_SIZE_BYTES,
memoryManager, true);
+ bufferAccumulator.setup(
+ ((subpartition, buffer, numRemainingBuffers) ->
buffer.recycleBuffer()));
+
+ for (int i = 0; i < numSubpartitions; i++) {
+ bufferAccumulator.receive(
+ generateRandomData(10, new Random()),
+ new TieredStorageSubpartitionId(i % numSubpartitions),
+ Buffer.DataType.DATA_BUFFER,
+ false);
+ }
+
+ // Trigger flushing buffers from data buffer.
+ bufferAccumulator.close();
+ }
+
private TieredStorageMemoryManagerImpl createStorageMemoryManager(int
numBuffersInBufferPool)
throws IOException {
BufferPool bufferPool =