Skip to content

Commit 1073715

Browse files
authored
Add RPC auto resizing buffer memory control (#17911)
* ver1 * ver2 * ver3 * ver4 * ver4 * update default config value * Disable when proportion <= 0 * fix review * disable mem-control in ITs * Replace auto_resizing_buffer_memory_proportion with datanode_memory_proportion * Add AutoResizingBuffer memory metrics * Fix it
1 parent ced3908 commit 1073715

54 files changed

Lines changed: 1071 additions & 46 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.db.it;
21+
22+
import org.apache.iotdb.commons.conf.CommonConfig;
23+
import org.apache.iotdb.commons.conf.IoTDBConstant;
24+
import org.apache.iotdb.isession.SessionConfig;
25+
import org.apache.iotdb.it.env.EnvFactory;
26+
import org.apache.iotdb.it.env.cluster.node.AbstractNodeWrapper;
27+
import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
28+
import org.apache.iotdb.it.framework.IoTDBTestRunner;
29+
import org.apache.iotdb.itbase.category.LocalStandaloneIT;
30+
import org.apache.iotdb.rpc.DeepCopyRpcTransportFactory;
31+
import org.apache.iotdb.rpc.RpcUtils;
32+
import org.apache.iotdb.service.rpc.thrift.IClientRPCService;
33+
import org.apache.iotdb.service.rpc.thrift.TSCloseOperationReq;
34+
import org.apache.iotdb.service.rpc.thrift.TSCloseSessionReq;
35+
import org.apache.iotdb.service.rpc.thrift.TSExecuteStatementReq;
36+
import org.apache.iotdb.service.rpc.thrift.TSExecuteStatementResp;
37+
import org.apache.iotdb.service.rpc.thrift.TSOpenSessionReq;
38+
import org.apache.iotdb.service.rpc.thrift.TSOpenSessionResp;
39+
40+
import org.apache.thrift.protocol.TBinaryProtocol;
41+
import org.apache.thrift.transport.TTransport;
42+
import org.junit.AfterClass;
43+
import org.junit.Assert;
44+
import org.junit.BeforeClass;
45+
import org.junit.Test;
46+
import org.junit.experimental.categories.Category;
47+
import org.junit.runner.RunWith;
48+
49+
import java.io.File;
50+
import java.nio.file.Files;
51+
import java.sql.Connection;
52+
import java.sql.Statement;
53+
import java.time.ZoneId;
54+
import java.util.ArrayList;
55+
import java.util.Arrays;
56+
import java.util.List;
57+
58+
@RunWith(IoTDBTestRunner.class)
59+
@Category({LocalStandaloneIT.class})
60+
public class IoTDBAutoResizingBufferMemoryIT {
61+
62+
private static final int DATANODE_MAX_HEAP_SIZE_IN_MB = 256;
63+
private static final int AUTO_RESIZING_BUFFER_COUNT_PER_CONNECTION = 2;
64+
private static final int CONNECTION_COUNT_OVERFLOW_MARGIN = 1;
65+
private static final int INITIAL_GROWING_REQUEST_PAYLOAD_SIZE = 16 * 1024;
66+
private static final int MAX_GROWING_REQUEST_PAYLOAD_SIZE =
67+
calculateNextPowerOfTwo(calculateAutoResizingBufferMemorySizeInBytes());
68+
69+
@BeforeClass
70+
public static void setUp() throws Exception {
71+
EnvFactory.getEnv()
72+
.getConfig()
73+
.getDataNodeJVMConfig()
74+
.setMaxHeapSize(DATANODE_MAX_HEAP_SIZE_IN_MB);
75+
EnvFactory.getEnv().initClusterEnvironment();
76+
}
77+
78+
@AfterClass
79+
public static void tearDown() throws Exception {
80+
EnvFactory.getEnv().cleanClusterEnvironment();
81+
}
82+
83+
@Test
84+
public void testNewConnectionsWithWritesAreRejectedWhenBufferMemoryIsExhausted()
85+
throws Exception {
86+
List<Connection> heldConnections = new ArrayList<>();
87+
boolean rejected = false;
88+
89+
try {
90+
try (Connection connection = EnvFactory.getEnv().getConnection();
91+
Statement statement = connection.createStatement()) {
92+
statement.execute("CREATE DATABASE root.auto_resizing_buffer_reject");
93+
statement.execute(
94+
"CREATE TIMESERIES root.auto_resizing_buffer_reject.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN");
95+
}
96+
97+
int connectionCountToExhaustBufferMemory =
98+
calculateConnectionCountToExhaustAutoResizingBufferMemory();
99+
for (int i = 0; i < connectionCountToExhaustBufferMemory; i++) {
100+
try {
101+
Connection connection = EnvFactory.getEnv().getConnection();
102+
heldConnections.add(connection);
103+
try (Statement statement = connection.createStatement()) {
104+
statement.execute(
105+
String.format(
106+
"INSERT INTO root.auto_resizing_buffer_reject.d1(time, s1) VALUES (%d, %d)",
107+
i + 1, i));
108+
}
109+
} catch (Exception e) {
110+
rejected = true;
111+
break;
112+
}
113+
}
114+
} finally {
115+
for (Connection connection : heldConnections) {
116+
closeQuietly(connection);
117+
}
118+
}
119+
120+
Assert.assertTrue(
121+
"Expected new connections with writes to be rejected after AutoResizingBuffer memory is exhausted",
122+
rejected);
123+
}
124+
125+
@Test
126+
public void testGrowingRequestsAreRejectedWhenBufferMemoryIsExhausted() throws Exception {
127+
boolean rejected = false;
128+
129+
try (Connection connection = EnvFactory.getEnv().getConnection();
130+
Statement statement = connection.createStatement()) {
131+
statement.execute("CREATE DATABASE root.auto_resizing_buffer_growing_request");
132+
statement.execute(
133+
"CREATE TIMESERIES root.auto_resizing_buffer_growing_request.d1.s1 WITH DATATYPE=TEXT, ENCODING=PLAIN");
134+
}
135+
136+
// do not use connection because its inevitable retry will slow down the test
137+
try (ThriftClientContext clientContext = ThriftClientContext.open()) {
138+
int payloadSize = INITIAL_GROWING_REQUEST_PAYLOAD_SIZE;
139+
while (payloadSize <= MAX_GROWING_REQUEST_PAYLOAD_SIZE) {
140+
try {
141+
clientContext.executeStatement(
142+
String.format(
143+
"INSERT INTO root.auto_resizing_buffer_growing_request.d1(time, s1) VALUES (%d, '%s')",
144+
payloadSize, repeat('a', payloadSize)));
145+
} catch (Exception e) {
146+
rejected = true;
147+
clientContext.markBroken();
148+
break;
149+
}
150+
payloadSize = payloadSize << 1;
151+
}
152+
}
153+
154+
Assert.assertTrue(
155+
"Expected a growing request to be rejected after AutoResizingBuffer memory is exhausted",
156+
rejected);
157+
}
158+
159+
private static void closeQuietly(AutoCloseable closeable) {
160+
if (closeable == null) {
161+
return;
162+
}
163+
try {
164+
closeable.close();
165+
} catch (Exception ignored) {
166+
// ignored
167+
}
168+
}
169+
170+
private static int calculateConnectionCountToExhaustAutoResizingBufferMemory() {
171+
int autoResizingBufferInitialSizePerConnection =
172+
AUTO_RESIZING_BUFFER_COUNT_PER_CONNECTION * RpcUtils.THRIFT_DEFAULT_BUF_CAPACITY;
173+
return (int)
174+
(calculateAutoResizingBufferMemorySizeInBytes() / autoResizingBufferInitialSizePerConnection
175+
+ CONNECTION_COUNT_OVERFLOW_MARGIN);
176+
}
177+
178+
private static long calculateAutoResizingBufferMemorySizeInBytes() {
179+
return (DATANODE_MAX_HEAP_SIZE_IN_MB * 1024L * 1024L * 5 / 100); // 5% of the max heap size;
180+
}
181+
182+
private static int calculateNextPowerOfTwo(long value) {
183+
int result = 1;
184+
while (result < value) {
185+
result <<= 1;
186+
}
187+
return result;
188+
}
189+
190+
private static String repeat(char character, int count) {
191+
char[] chars = new char[count];
192+
Arrays.fill(chars, character);
193+
return new String(chars);
194+
}
195+
196+
private static TSOpenSessionReq createOpenSessionReq() {
197+
TSOpenSessionReq req = new TSOpenSessionReq();
198+
req.setUsername(SessionConfig.DEFAULT_USER);
199+
req.setPassword(SessionConfig.DEFAULT_PASSWORD);
200+
req.setZoneId(ZoneId.systemDefault().toString());
201+
req.putToConfiguration("version", IoTDBConstant.ClientVersion.V_1_0.toString());
202+
req.putToConfiguration("sql_dialect", "tree");
203+
return req;
204+
}
205+
206+
private static class ThriftClientContext implements AutoCloseable {
207+
208+
private final TTransport transport;
209+
private final IClientRPCService.Client client;
210+
private final long sessionId;
211+
private final long statementId;
212+
private boolean broken;
213+
214+
private ThriftClientContext(
215+
TTransport transport, IClientRPCService.Client client, long sessionId, long statementId) {
216+
this.transport = transport;
217+
this.client = client;
218+
this.sessionId = sessionId;
219+
this.statementId = statementId;
220+
}
221+
222+
private static ThriftClientContext open() throws Exception {
223+
DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0);
224+
TTransport transport =
225+
DeepCopyRpcTransportFactory.INSTANCE.getTransport(
226+
dataNode.getIp(), dataNode.getPort(), 0);
227+
transport.open();
228+
IClientRPCService.Client client =
229+
new IClientRPCService.Client(new TBinaryProtocol(transport));
230+
TSOpenSessionResp openSessionResp = client.openSession(createOpenSessionReq());
231+
RpcUtils.verifySuccess(openSessionResp.getStatus());
232+
long sessionId = openSessionResp.getSessionId();
233+
return new ThriftClientContext(
234+
transport, client, sessionId, client.requestStatementId(sessionId));
235+
}
236+
237+
private void executeStatement(String sql) throws Exception {
238+
TSExecuteStatementResp resp =
239+
client.executeStatementV2(new TSExecuteStatementReq(sessionId, sql, statementId));
240+
RpcUtils.verifySuccess(resp.getStatus());
241+
}
242+
243+
private void markBroken() {
244+
broken = true;
245+
}
246+
247+
@Override
248+
public void close() throws Exception {
249+
try {
250+
if (!broken) {
251+
client.closeOperation(new TSCloseOperationReq(sessionId).setStatementId(statementId));
252+
client.closeSession(new TSCloseSessionReq(sessionId));
253+
}
254+
} finally {
255+
transport.close();
256+
}
257+
}
258+
}
259+
260+
private static boolean checkConfigFileContains(AbstractNodeWrapper nodeWrapper, String content) {
261+
try {
262+
String systemPropertiesPath =
263+
nodeWrapper.getNodePath()
264+
+ File.separator
265+
+ "conf"
266+
+ File.separator
267+
+ CommonConfig.SYSTEM_CONFIG_NAME;
268+
return new String(Files.readAllBytes(new File(systemPropertiesPath).toPath()))
269+
.contains(content);
270+
} catch (Exception ignore) {
271+
return false;
272+
}
273+
}
274+
}

‎integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoadTsFileIT.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public void setUp() throws Exception {
8686
EnvFactory.getEnv().getConfig().getCommonConfig().setTimePartitionInterval(PARTITION_INTERVAL);
8787
EnvFactory.getEnv().getConfig().getCommonConfig().setEnforceStrongPassword(false);
8888
EnvFactory.getEnv().getConfig().getCommonConfig().setPipeMemoryManagementEnabled(false);
89-
EnvFactory.getEnv().getConfig().getCommonConfig().setDatanodeMemoryProportion("1:10:1:1:1:1");
89+
EnvFactory.getEnv().getConfig().getCommonConfig().setDatanodeMemoryProportion("1:10:1:1:1:0");
9090
EnvFactory.getEnv()
9191
.getConfig()
9292
.getDataNodeConfig()

‎integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/tablemodel/manual/AbstractPipeTableModelDualManualIT.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ protected void setupConfig() {
4646
.getConfig()
4747
.getCommonConfig()
4848
.setAutoCreateSchemaEnabled(true)
49+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
4950
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
5051
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
5152
.setEnforceStrongPassword(false)
@@ -56,6 +57,7 @@ protected void setupConfig() {
5657
.getConfig()
5758
.getCommonConfig()
5859
.setAutoCreateSchemaEnabled(true)
60+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
5961
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
6062
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
6163
.setEnforceStrongPassword(false)

‎integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/tablemodel/manual/basic/IoTDBPipePermissionIT.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public void setUp() {
6565
.getConfig()
6666
.getCommonConfig()
6767
.setAutoCreateSchemaEnabled(false)
68+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
6869
.setDefaultSchemaRegionGroupNumPerDatabase(1)
6970
.setTimestampPrecision("ms")
7071
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
@@ -77,6 +78,7 @@ public void setUp() {
7778
.getConfig()
7879
.getCommonConfig()
7980
.setAutoCreateSchemaEnabled(false)
81+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
8082
.setTimestampPrecision("ms")
8183
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
8284
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)

‎integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/tablemodel/manual/basic/IoTDBPipeProtocolIT.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ private void innerSetUp(
7171
.getConfig()
7272
.getCommonConfig()
7373
.setAutoCreateSchemaEnabled(true)
74+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
7475
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
7576
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
7677
.setDataRegionConsensusProtocolClass(dataRegionConsensus)
@@ -83,6 +84,7 @@ private void innerSetUp(
8384
.getConfig()
8485
.getCommonConfig()
8586
.setAutoCreateSchemaEnabled(true)
87+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
8688
.setConfigNodeConsensusProtocolClass(configNodeConsensus)
8789
.setSchemaRegionConsensusProtocolClass(schemaRegionConsensus)
8890
.setDataRegionConsensusProtocolClass(dataRegionConsensus)
@@ -167,6 +169,7 @@ public void testPipeOnBothSenderAndReceiver() throws Exception {
167169
.getConfig()
168170
.getCommonConfig()
169171
.setAutoCreateSchemaEnabled(true)
172+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
170173
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
171174
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
172175
.setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS)
@@ -179,6 +182,7 @@ public void testPipeOnBothSenderAndReceiver() throws Exception {
179182
.getConfig()
180183
.getCommonConfig()
181184
.setAutoCreateSchemaEnabled(true)
185+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
182186
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
183187
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
184188
.setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS)
@@ -361,6 +365,7 @@ private void doTestUseNodeUrls(String connectorName) throws Exception {
361365
.getConfig()
362366
.getCommonConfig()
363367
.setAutoCreateSchemaEnabled(true)
368+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
364369
.setPipeAirGapReceiverEnabled(true)
365370
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
366371
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
@@ -377,6 +382,7 @@ private void doTestUseNodeUrls(String connectorName) throws Exception {
377382
.getConfig()
378383
.getCommonConfig()
379384
.setAutoCreateSchemaEnabled(true)
385+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
380386
.setPipeAirGapReceiverEnabled(true)
381387
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
382388
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)

‎integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/tablemodel/manual/basic/IoTDBPipeSourceIT.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public void setUp() {
6565
.getConfig()
6666
.getCommonConfig()
6767
.setAutoCreateSchemaEnabled(true)
68+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
6869
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
6970
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
7071
// Disable sender compaction for tsfile determination in loose range test
@@ -80,6 +81,7 @@ public void setUp() {
8081
.getConfig()
8182
.getCommonConfig()
8283
.setAutoCreateSchemaEnabled(true)
84+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
8385
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
8486
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
8587
.setDnConnectionTimeoutMs(600000)

‎integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/tablemodel/manual/basic/IoTDBPipeWithLoadIT.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public void setUp() {
6363
.getConfig()
6464
.getCommonConfig()
6565
.setAutoCreateSchemaEnabled(true)
66+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
6667
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
6768
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
6869
// Disable sender compaction to test mods
@@ -77,6 +78,7 @@ public void setUp() {
7778
.getConfig()
7879
.getCommonConfig()
7980
.setAutoCreateSchemaEnabled(true)
81+
.setDatanodeMemoryProportion("3:3:1:1:1:0")
8082
.setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
8183
.setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
8284
.setDnConnectionTimeoutMs(600000)

0 commit comments

Comments
 (0)