3737import org .apache .iotdb .confignode .rpc .thrift .TShowRegionResp ;
3838import org .apache .iotdb .confignode .rpc .thrift .TTimeSlotList ;
3939import org .apache .iotdb .consensus .ConsensusFactory ;
40+ import org .apache .iotdb .db .storageengine .dataregion .wal .utils .WALFileUtils ;
4041import org .apache .iotdb .it .env .EnvFactory ;
42+ import org .apache .iotdb .it .env .cluster .node .DataNodeWrapper ;
4143import org .apache .iotdb .it .framework .IoTDBTestRunner ;
4244import org .apache .iotdb .itbase .category .ClusterIT ;
4345import org .apache .iotdb .rpc .TSStatusCode ;
4951import org .junit .experimental .categories .Category ;
5052import org .junit .runner .RunWith ;
5153
54+ import java .io .File ;
55+ import java .io .IOException ;
56+ import java .nio .file .Files ;
57+ import java .nio .file .Path ;
58+ import java .sql .Connection ;
59+ import java .sql .Statement ;
5260import java .util .ArrayList ;
5361import java .util .Collections ;
62+ import java .util .Comparator ;
5463import java .util .HashMap ;
5564import java .util .List ;
5665import java .util .Map ;
5766import java .util .concurrent .TimeUnit ;
67+ import java .util .stream .Collectors ;
68+ import java .util .stream .Stream ;
5869
5970@ RunWith (IoTDBTestRunner .class )
6071@ Category ({ClusterIT .class })
@@ -68,9 +79,15 @@ public class IoTDBRegionGroupLeaderBalanceWithWALBlockIT {
6879 private static final int TEST_DATA_NODE_NUM = 3 ;
6980 private static final int DATABASE_NUM = 3 ;
7081 private static final int RETRY_NUM = 60 ;
82+ private static final int TEST_SERIES_PARTITION_SLOT = 0 ;
83+ private static final long TEST_TIME_PARTITION_SLOT = 0 ;
84+ private static final int WAL_FILE_SIZE_THRESHOLD_IN_BYTE = 1024 ;
85+ private static final int WAL_PAYLOAD_REPEAT_COUNT = 128 ;
7186
7287 private static final String DATABASE = "root.wal_block_db" ;
7388 private static final String WAL_THROTTLE_THRESHOLD_IN_BYTE = "wal_throttle_threshold_in_byte" ;
89+ private static final String WAL_FILE_SIZE_THRESHOLD_IN_BYTE_CONFIG =
90+ "wal_file_size_threshold_in_byte" ;
7491 private static final String WAL_BLOCKED_STATUS = NodeStatus .ReadOnly .getStatus () + "(WALBlocked)" ;
7592
7693 @ Before
@@ -84,6 +101,7 @@ public void setUp() {
84101 .setDataRegionConsensusProtocolClass (TEST_DATA_REGION_CONSENSUS_PROTOCOL_CLASS )
85102 .setSchemaReplicationFactor (TEST_REPLICATION_FACTOR )
86103 .setDataReplicationFactor (TEST_REPLICATION_FACTOR )
104+ .setSeriesSlotNum (1 )
87105 .setCheckPeriodWhenInsertBlocked (50 )
88106 .setMaxWaitingTimeWhenInsertBlocked (2000 );
89107 EnvFactory .getEnv ().initClusterEnvironment (1 , TEST_DATA_NODE_NUM );
@@ -104,7 +122,8 @@ public void testRegionLeaderBalanceWhenWalLongTermBlocked() throws Exception {
104122 () -> isLeaderDistributionBalanced (client ));
105123
106124 TRegionInfo targetLeader = findAnyDataRegionLeader (client );
107- triggerLongTermWalBlockingOnDataNode (client , targetLeader .getDataNodeId ());
125+ long walDiskUsage = generateWalTraffic (client , targetLeader );
126+ triggerLongTermWalBlockingOnDataNode (client , targetLeader .getDataNodeId (), walDiskUsage );
108127
109128 waitUntil (
110129 "target leader DataNode becomes ReadOnly because of long-term WAL blocking" ,
@@ -124,9 +143,10 @@ private void createDataRegionGroups(SyncConfigNodeIServiceClient client) throws
124143
125144 Map <TSeriesPartitionSlot , TTimeSlotList > seriesSlotMap = new HashMap <>();
126145 seriesSlotMap .put (
127- new TSeriesPartitionSlot (1 ),
146+ new TSeriesPartitionSlot (TEST_SERIES_PARTITION_SLOT ),
128147 new TTimeSlotList ()
129- .setTimePartitionSlots (Collections .singletonList (new TTimePartitionSlot (100 ))));
148+ .setTimePartitionSlots (
149+ Collections .singletonList (new TTimePartitionSlot (TEST_TIME_PARTITION_SLOT ))));
130150 Map <String , Map <TSeriesPartitionSlot , TTimeSlotList >> databaseSlotsMap = new HashMap <>();
131151 databaseSlotsMap .put (DATABASE + i , seriesSlotMap );
132152
@@ -168,13 +188,95 @@ private TRegionInfo findAnyDataRegionLeader(SyncConfigNodeIServiceClient client)
168188 }
169189
170190 private void triggerLongTermWalBlockingOnDataNode (
171- SyncConfigNodeIServiceClient client , int dataNodeId ) throws Exception {
191+ SyncConfigNodeIServiceClient client , int dataNodeId , long walDiskUsage ) throws Exception {
192+ Assert .assertTrue ("No WAL traffic was generated on target DataNode" , walDiskUsage > 0 );
193+
194+ Map <String , String > configItems = new HashMap <>();
195+ configItems .put (WAL_THROTTLE_THRESHOLD_IN_BYTE , Long .toString (walDiskUsage ));
196+ TSStatus status = client .setConfiguration (new TSetConfigurationReq (configItems , dataNodeId ));
197+ Assert .assertEquals (TSStatusCode .SUCCESS_STATUS .getStatusCode (), status .getCode ());
198+ }
199+
200+ private long generateWalTraffic (SyncConfigNodeIServiceClient client , TRegionInfo targetLeader )
201+ throws Exception {
202+ int dataNodeId = targetLeader .getDataNodeId ();
203+ long originalWalDiskUsage = countWalDiskUsage (dataNodeId );
172204 Map <String , String > configItems = new HashMap <>();
173- // The throttle threshold used by WALManager is 80% of this value, so 1 makes it 0 and
174- // deterministically triggers long-term WAL blocking on the target DataNode heartbeat.
175- configItems .put (WAL_THROTTLE_THRESHOLD_IN_BYTE , "1" );
205+ configItems .put (
206+ WAL_FILE_SIZE_THRESHOLD_IN_BYTE_CONFIG , Integer .toString (WAL_FILE_SIZE_THRESHOLD_IN_BYTE ));
176207 TSStatus status = client .setConfiguration (new TSetConfigurationReq (configItems , dataNodeId ));
177208 Assert .assertEquals (TSStatusCode .SUCCESS_STATUS .getStatusCode (), status .getCode ());
209+
210+ DataNodeWrapper dataNodeWrapper =
211+ EnvFactory .getEnv ()
212+ .dataNodeIdToWrapper (dataNodeId )
213+ .orElseThrow (() -> new AssertionError ("DataNode not found: " + dataNodeId ));
214+
215+ try (Connection connection =
216+ EnvFactory .getEnv ().getConnectionWithSpecifiedDataNode (dataNodeWrapper );
217+ Statement statement = connection .createStatement ()) {
218+ String payload = String .join ("" , Collections .nCopies (WAL_PAYLOAD_REPEAT_COUNT , "wal_block" ));
219+ String device =
220+ targetLeader .getDatabase () + ".d" + targetLeader .getConsensusGroupId ().getId ();
221+ statement .execute ("CREATE TIMESERIES " + device + ".s WITH DATATYPE=TEXT, ENCODING=PLAIN" );
222+ for (int i = 0 ; i < 16 ; i ++) {
223+ statement .execute (
224+ "INSERT INTO "
225+ + device
226+ + "(time,s) VALUES("
227+ + (TEST_TIME_PARTITION_SLOT + i )
228+ + ", '"
229+ + payload
230+ + "')" );
231+ }
232+ }
233+
234+ final long [] currentWalDiskUsage = new long [1 ];
235+ waitUntil (
236+ "target DataNode generates WAL files" ,
237+ () -> {
238+ currentWalDiskUsage [0 ] = countWalDiskUsage (dataNodeId );
239+ return currentWalDiskUsage [0 ] > originalWalDiskUsage ;
240+ });
241+ return currentWalDiskUsage [0 ];
242+ }
243+
244+ private long countWalDiskUsage (int dataNodeId ) throws IOException {
245+ DataNodeWrapper dataNodeWrapper =
246+ EnvFactory .getEnv ()
247+ .dataNodeIdToWrapper (dataNodeId )
248+ .orElseThrow (() -> new AssertionError ("DataNode not found: " + dataNodeId ));
249+ Path walDir = new File (dataNodeWrapper .getWalDir ()).toPath ();
250+ if (!Files .exists (walDir )) {
251+ return 0 ;
252+ }
253+ try (Stream <Path > paths = Files .walk (walDir )) {
254+ Map <Path , List <Path >> walFilesByDir =
255+ paths
256+ .filter (Files ::isRegularFile )
257+ .filter (
258+ path ->
259+ WALFileUtils .walFilenameFilter (
260+ path .getParent ().toFile (), path .getFileName ().toString ()))
261+ .collect (Collectors .groupingBy (Path ::getParent ));
262+ long walDiskUsage = 0 ;
263+ for (List <Path > walFiles : walFilesByDir .values ()) {
264+ if (walFiles .size () <= 1 ) {
265+ continue ;
266+ }
267+ Path currentWalFile =
268+ Collections .max (
269+ walFiles ,
270+ Comparator .comparingLong (
271+ path -> WALFileUtils .parseVersionId (path .getFileName ().toString ())));
272+ for (Path walFile : walFiles ) {
273+ if (!walFile .equals (currentWalFile )) {
274+ walDiskUsage += walFile .toFile ().length ();
275+ }
276+ }
277+ }
278+ return walDiskUsage ;
279+ }
178280 }
179281
180282 private String getNodeStatusWithReason (SyncConfigNodeIServiceClient client , int dataNodeId )
0 commit comments