Skip to content

Commit 83d4197

Browse files
authored
Fix receiver write admission stalls and RPC buffer allocation failures (#18918)
* Fix receiver write admission and RPC buffer memory pressure * Preserve RPC frame buffers and avoid duplicate flush submissions * Revert fallback flush trigger in memory admission
1 parent 5a437f5 commit 83d4197

8 files changed

Lines changed: 636 additions & 34 deletions

File tree

‎iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/AutoResizingBuffer.java‎

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@
2323
import java.util.Arrays;
2424

2525
/**
26-
* Helper class that wraps a byte[] so that it can expand and be reused. Users should call
27-
* resizeIfNecessary to make sure the buffer has suitable capacity, and then use the array as
28-
* needed.
26+
* Helper class that wraps a byte[] so that it can expand and be reused. Incremental writes use
27+
* ensureCapacity; resizeIfNecessary also considers shrinking and must receive a complete frame's
28+
* size.
2929
*
3030
* <p>Resizing policies: Expanding: If the required size > current capacity * 1.5, expand to the
31-
* required size, otherwise expand to current capacity * 1.5. Shrinking: If initial size < the
32-
* required size < current capacity * 0.6, and such small requests last for more than 5 times,
33-
* shrink to the middle of the required size and current capacity.
31+
* required size, otherwise expand to current capacity * 1.5 when the memory budget allows it.
32+
* Shrinking: If the required size < current capacity * 0.6, and such small requests last for more
33+
* than 5 times, shrink to the middle of the required size and current capacity. The required size
34+
* is bounded below by the initial capacity.
3435
*/
3536
class AutoResizingBuffer implements AutoCloseable {
3637

@@ -56,23 +57,28 @@ public AutoResizingBuffer(int initialCapacity) throws IOException {
5657
}
5758

5859
public void resizeIfNecessary(int size) throws IOException {
59-
reserveCurrentCapacityIfReleased();
60+
ensureCapacity(size);
61+
size = Math.max(size, initialCapacity);
6062
final int currentCapacity = this.array.length;
6163
final double loadFactor = 0.6;
64+
if (currentCapacity * loadFactor > size
65+
&& bufTooLargeCounter-- <= 0
66+
&& System.currentTimeMillis() - lastShrinkTime > RpcUtils.MIN_SHRINK_INTERVAL) {
67+
resize(size + (currentCapacity - size) / 2, size);
68+
bufTooLargeCounter = RpcUtils.MAX_BUFFER_OVERSIZE_TIME;
69+
lastShrinkTime = System.currentTimeMillis();
70+
}
71+
}
72+
73+
void ensureCapacity(int size) throws IOException {
74+
reserveCurrentCapacityIfReleased();
75+
final int currentCapacity = this.array.length;
6276
if (currentCapacity < size) {
6377
// Increase by a factor of 1.5x
6478
int growCapacity = currentCapacity + (currentCapacity >> 1);
6579
int newCapacity = Math.max(growCapacity, size);
66-
resize(newCapacity);
80+
resize(newCapacity, size);
6781
bufTooLargeCounter = RpcUtils.MAX_BUFFER_OVERSIZE_TIME;
68-
} else if (size > initialCapacity
69-
&& currentCapacity * loadFactor > size
70-
&& bufTooLargeCounter-- <= 0
71-
&& System.currentTimeMillis() - lastShrinkTime > RpcUtils.MIN_SHRINK_INTERVAL) {
72-
// do not resize if it is reading the request size and do not shrink too often
73-
resize(size + (currentCapacity - size) / 2);
74-
bufTooLargeCounter = RpcUtils.MAX_BUFFER_OVERSIZE_TIME;
75-
lastShrinkTime = System.currentTimeMillis();
7682
}
7783
}
7884

@@ -89,11 +95,14 @@ public void close() {
8995
}
9096
}
9197

92-
private void resize(int newCapacity) throws IOException {
98+
private void resize(int newCapacity, int requiredCapacity) throws IOException {
9399
final int currentCapacity = array.length;
94100
if (newCapacity > currentCapacity) {
95-
final int delta = newCapacity - currentCapacity;
96-
memoryAccount.allocate(delta);
101+
final int delta =
102+
(int)
103+
memoryAccount.allocate(
104+
newCapacity - currentCapacity, requiredCapacity - currentCapacity);
105+
newCapacity = currentCapacity + delta;
97106
try {
98107
array = Arrays.copyOf(array, newCapacity);
99108
accountedCapacity += delta;

‎iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/AutoResizingBufferMemoryManager.java‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,18 @@ void allocate(long sizeInBytes) throws IOException {
124124
AutoResizingBufferMemoryManager.allocate(memoryControl, sizeInBytes);
125125
}
126126

127+
long allocate(long preferredSizeInBytes, long requiredSizeInBytes) throws IOException {
128+
// Growth headroom is optional. If it does not fit, only wait for the bytes actually needed
129+
// by the frame instead of rejecting a request that still fits within the memory budget.
130+
if (preferredSizeInBytes > requiredSizeInBytes
131+
&& memoryControl.allocate(preferredSizeInBytes)) {
132+
memoryAllocationCount.incrementAndGet();
133+
return preferredSizeInBytes;
134+
}
135+
allocate(requiredSizeInBytes);
136+
return requiredSizeInBytes;
137+
}
138+
127139
void release(long sizeInBytes) {
128140
AutoResizingBufferMemoryManager.release(memoryControl, sizeInBytes);
129141
}

‎iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/AutoScalingBufferWriteTransport.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ public int read(byte[] buf, int off, int len) {
4747
@Override
4848
public void write(byte[] toWrite, int off, int len) throws TTransportException {
4949
try {
50-
buf.resizeIfNecessary(pos + len);
50+
// A small prefix during serialization does not indicate a small frame. Only the complete
51+
// frame size, supplied after flushing, may trigger shrinking.
52+
buf.ensureCapacity(pos + len);
5153
} catch (IOException e) {
5254
throw new TTransportException(e);
5355
}

‎iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TCompressedElasticFramedTransport.java‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,10 @@ public void flush() throws TTransportException {
100100
}
101101

102102
writeBuffer.reset();
103-
if (thriftDefaultBufferSize < length) {
104-
try {
105-
writeBuffer.resizeIfNecessary(thriftDefaultBufferSize);
106-
} catch (IOException e) {
107-
throw new TTransportException(e);
108-
}
103+
try {
104+
writeBuffer.resizeIfNecessary(length);
105+
} catch (IOException e) {
106+
throw new TTransportException(e);
109107
}
110108
underlying.flush();
111109
}

‎iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TElasticFramedTransport.java‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,12 +317,10 @@ public void flush() throws TTransportException {
317317
underlying.write(i32buf, 0, 4);
318318
underlying.write(writeBuffer.getBuffer(), 0, length);
319319
writeBuffer.reset();
320-
if (length > thriftDefaultBufferSize) {
321-
try {
322-
writeBuffer.resizeIfNecessary(thriftDefaultBufferSize);
323-
} catch (IOException e) {
324-
throw new TTransportException(e);
325-
}
320+
try {
321+
writeBuffer.resizeIfNecessary(length);
322+
} catch (IOException e) {
323+
throw new TTransportException(e);
326324
}
327325
underlying.flush();
328326
}

0 commit comments

Comments
 (0)