Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0b9f95c
module-1
Wei-hao-Li Aug 12, 2026
96b953f
module-2
Wei-hao-Li Aug 14, 2026
16560fd
module-3
Wei-hao-Li Aug 14, 2026
633c4f0
module-4
Wei-hao-Li Aug 17, 2026
d2b74b0
fix & add IT
Wei-hao-Li Aug 18, 2026
b4c60d1
fix IT & hot-load & explain analyze & metric
Wei-hao-Li Aug 20, 2026
9adfe1d
fix IT
Wei-hao-Li Aug 23, 2026
d1a2449
fix rate
Wei-hao-Li Aug 23, 2026
85d190c
fix rpc endpoint
Wei-hao-Li Aug 24, 2026
843971c
support spill process for TVF distributionPlan
Wei-hao-Li Aug 25, 2026
39de0b3
fix according review
Wei-hao-Li Aug 28, 2026
8a6bb33
fix-1
Wei-hao-Li Aug 28, 2026
bc30915
fix exception process
Wei-hao-Li Aug 31, 2026
ff5f36a
fix UT check
Wei-hao-Li Aug 31, 2026
928a691
fix iocheck & topKSourceId
Wei-hao-Li Aug 31, 2026
c8d94e1
add warn of spill configuration
Wei-hao-Li Aug 31, 2026
293596d
fix i18n & related log
Wei-hao-Li Aug 31, 2026
42368c0
remove useless
Wei-hao-Li Aug 31, 2026
bee3ffc
fix partitionCache
Wei-hao-Li Sep 1, 2026
46f3674
fix some
Wei-hao-Li Sep 3, 2026
d10984d
fix batch & shared QuerySource mixed
Wei-hao-Li Sep 3, 2026
5017cdd
fix applySortProperty of constructSpilledDeviceTableScanByTags
Wei-hao-Li Sep 3, 2026
5568de6
fix Materializer
Wei-hao-Li Sep 3, 2026
667f666
fix DeviceEntryCount
Wei-hao-Li Sep 3, 2026
68eb2fe
fix setDeviceEntryDataSet
Wei-hao-Li Sep 3, 2026
ff95f37
make some code about batch & shared QuerySource mixed clear
Wei-hao-Li Sep 3, 2026
da04c13
fix err code & message
Wei-hao-Li Sep 3, 2026
0b15469
fix isFinished/hasNext
Wei-hao-Li Sep 3, 2026
2e7a822
fix ensureFieldColumnForTreeNonAlignedDeviceViewScan
Wei-hao-Li Sep 3, 2026
16b5217
fix null check of prepareNextDeviceBatch
Wei-hao-Li Sep 3, 2026
96ca9d6
fix close exception override
Wei-hao-Li Sep 3, 2026
ab4e919
fix FIContext statistics
Wei-hao-Li Sep 3, 2026
408b655
rename metric to DEVICE_ENTRY_DISK_READ_WRITE
Wei-hao-Li Sep 3, 2026
a2c0d64
reserve frame size
Wei-hao-Li Sep 3, 2026
510762c
add timeout logic when segment rpc retry
Wei-hao-Li Sep 3, 2026
452f95c
fix CI
Wei-hao-Li Sep 3, 2026
f3f6ec1
fix last cache cross region
Wei-hao-Li Sep 4, 2026
8c6dc45
remove BATCH_SERIES_SCAN & add retry count for batch query
Wei-hao-Li Sep 4, 2026
e19a721
support catch AccessDeniedException when delete for Windows
Wei-hao-Li Sep 4, 2026
8a2df7e
add collectSeriesPartitionslots when fetchSchema
Wei-hao-Li Sep 4, 2026
106c842
Merge branch 'master' of github.com:apache/iotdb into deviceEntrySpil…
Wei-hao-Li Sep 4, 2026
3967187
fix metric when initQueryDataSource
Wei-hao-Li Sep 4, 2026
5cd41c4
fix log message
Wei-hao-Li Sep 4, 2026
d46b584
fix deviceCountMap when cross region
Wei-hao-Li Sep 4, 2026
c546c7b
optimize mayUseLastCache
Wei-hao-Li Sep 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,10 @@ public DataNodeConfig setDnMultiDirStrategy(String multiDirStrategy) {
setProperty("dn_multi_dir_strategy", multiDirStrategy);
return this;
}

@Override
public DataNodeConfig setTableQueryDeviceEntryBatchSizeInBytes(long batchSizeInBytes) {
setProperty("table_query_device_entry_batch_size_in_bytes", String.valueOf(batchSizeInBytes));
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,9 @@ public DataNodeConfig setDnDataDirs(String dnDataDirs) {
public DataNodeConfig setDnMultiDirStrategy(String multiDirStrategy) {
return this;
}

@Override
public DataNodeConfig setTableQueryDeviceEntryBatchSizeInBytes(long batchSizeInBytes) {
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,6 @@ DataNodeConfig setLoadActiveListeningCheckIntervalSeconds(
DataNodeConfig setDnDataDirs(String dnDataDirs);

DataNodeConfig setDnMultiDirStrategy(String multiDirStrategy);

DataNodeConfig setTableQueryDeviceEntryBatchSizeInBytes(long batchSizeInBytes);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.iotdb.relational.it.query.recent;

import org.apache.iotdb.it.env.EnvFactory;
import org.apache.iotdb.it.framework.IoTDBTestRunner;
import org.apache.iotdb.itbase.category.TableClusterIT;
import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
import org.apache.iotdb.itbase.env.BaseEnv;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import static org.junit.Assert.assertEquals;

@RunWith(IoTDBTestRunner.class)
@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
public class IoTDBDeviceEntrySpillIT {

@BeforeClass
public static void setUp() throws Exception {
EnvFactory.getEnv().getConfig().getDataNodeConfig().setTableQueryDeviceEntryBatchSizeInBytes(1);
EnvFactory.getEnv().initClusterEnvironment();
try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = connection.createStatement()) {
statement.execute("CREATE DATABASE spill_test");
statement.execute(
"CREATE TABLE spill_test.device_data (tag1 STRING TAG, tag2 STRING TAG, "
+ "value INT32 FIELD)");
statement.execute(
"INSERT INTO spill_test.device_data(tag1, tag2, time, value) "
+ "VALUES ('a', 'x', 1, 10), ('a', 'x', 2, 20), "
+ "('b', 'y', 1, 30), ('c', 'z', 1, 40)");
}
}

@AfterClass
public static void tearDown() throws Exception {
EnvFactory.getEnv().cleanClusterEnvironment();
}

@Test
public void testRawFullTableQueryWithSpill() throws Exception {
try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = connection.createStatement();
ResultSet resultSet =
statement.executeQuery("SELECT tag1, tag2, value FROM spill_test.device_data")) {
int rowCount = 0;
while (resultSet.next()) {
rowCount++;
}
assertEquals(4, rowCount);
}
}

@Test
public void testRawQueriesWithTimeFilterProjectionFilterLimitAndOrdering() throws Exception {
String[] queries = {
"SELECT * FROM spill_test.device_data WHERE time >= 1 AND time < 3",
"SELECT time, value FROM spill_test.device_data WHERE time >= 1 AND time < 3",
"SELECT tag1, tag2, value FROM spill_test.device_data "
+ "WHERE time >= 1 AND time < 3 AND value > 10",
"SELECT * FROM spill_test.device_data WHERE time >= 1 AND time < 3 LIMIT 2",
"SELECT * FROM spill_test.device_data WHERE time >= 1 AND time < 3 ORDER BY time ASC",
"SELECT * FROM spill_test.device_data WHERE time >= 1 AND time < 3 " + "ORDER BY tag1, time"
};
for (String query : queries) {
assertRowCount(query, query.contains("LIMIT 2") ? 2 : query.contains("value > 10") ? 3 : 4);
}
}

@Test
public void testAggregationQueryWithSpill() throws Exception {
try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = connection.createStatement();
ResultSet resultSet =
statement.executeQuery("SELECT count(value) FROM spill_test.device_data")) {
assertEquals(true, resultSet.next());
assertEquals(4, resultSet.getLong(1));
assertEquals(false, resultSet.next());
}
}

@Test
public void testGroupedAggregationAcrossSpillSegments() throws Exception {
assertRowCount("SELECT tag1, count(*) FROM spill_test.device_data GROUP BY tag1", 3);
assertRowCount(
"SELECT tag1, tag2, count(*), sum(value) FROM spill_test.device_data "
+ "GROUP BY tag1, tag2",
3);
assertRowCount(
"SELECT date_bin(1ms, time), count(*) FROM spill_test.device_data "
+ "GROUP BY date_bin(1ms, time)",
2);
}

@Test
public void testOrPredicateDoesNotDuplicateDeviceRows() throws Exception {
try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = connection.createStatement();
ResultSet resultSet =
statement.executeQuery(
"SELECT count(*) FROM spill_test.device_data "
+ "WHERE tag1 = 'a' OR tag2 = 'x'")) {
assertEquals(true, resultSet.next());
assertEquals(2, resultSet.getLong(1));
assertEquals(false, resultSet.next());
}
}

private void assertRowCount(String sql, int expectedRowCount) throws Exception {
try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)) {
int rowCount = 0;
while (resultSet.next()) {
rowCount++;
}
assertEquals(expectedRowCount, rowCount);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ public enum TSStatusCode {
REPEATED_RPC_CALL(723),
CANNOT_READ_TSFILE(724),
COPY_TO_WRITE_ERROR(725),
DEVICE_ENTRY_SPILL_NOT_FOUND(726),

// OBJECT
OBJECT_NOT_EXISTS(740),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1485,5 +1485,8 @@ private DataNodeMiscMessages() {}
"Missing value arrays for aligned column index %d during expand";
public static final String EXCEPTION_MISSING_VALUE_ARRAYS_FOR_ALIGNED_COLUMN_INDEX_ARG_DURING_MARK_NULL_VALUE_2893628E =
"Missing value arrays for aligned column index %d during mark null value";
public static final String
LOG_TABLE_QUERY_DEVICE_ENTRY_BATCH_SIZE_IN_BYTES_ARG_EXCEEDS_DN_THRIFT_MAX_FRAME_SIZE_ARG_USING_ARG_AS_THE_EFFECTIVE_VALUE_2AE1BEDA =
"table_query_device_entry_batch_size_in_bytes (%d) exceeds the maximum RPC payload (dn_thrift_max_frame_size %d minus 1024 bytes); using %d as the effective value";

}
Original file line number Diff line number Diff line change
Expand Up @@ -3820,5 +3820,49 @@ private DataNodeQueryMessages() {}
public static final String EXCEPTION_VISIBLEALIASES_IS_NULL_630B27F1 = "visibleAliases is null";
public static final String EXCEPTION_HAS_NO_PERMISSION_TO_EXECUTE_ARG_BECAUSE_ONLY_THE_SUPERUSER_CAN_ALTER_HIM_HERSELF_C5902893 =
"Has no permission to execute %s, because only the superuser can alter him/herself.";
public static final String
LOG_FAILED_TO_CLEAN_DEVICEENTRY_DATA_SET_ASYNCHRONOUSLY_QUERYID_ARG_PLANNODEID_ARG_9106C4C5 =
"Failed to clean DeviceEntry data set asynchronously: queryId=%s, planNodeId=%s";
public static final String LOG_FAILED_TO_CLEAN_DEVICEENTRY_SPILL_DIRECTORY_FOR_QUERY_ARG_53D9C1FC =
"Failed to clean DeviceEntry spill directory for query %s";
public static final String
LOG_FAILED_TO_CLOSE_DEVICEENTRY_SPILL_WRITER_DURING_CLEANUP_ARG_EA7F7941 =
"Failed to close DeviceEntry spill writer during cleanup: %s";
public static final String
LOG_FAILED_TO_CLOSE_DEVICEENTRY_DATA_SET_AFTER_INDEX_SCAN_FAILURE_ARG_57F04319 =
"Failed to close DeviceEntry data set after index scan failure: %s";
public static final String
EXCEPTION_DEVICEENTRY_SPILL_SEGMENT_UNAVAILABLE_MAY_BE_DUE_TO_TIMEOUT_OR_KILL_ARG_B932D10D =
"DeviceEntry spill segment is unavailable: %s. The query may have timed out or been killed.";
public static final String
EXCEPTION_DEVICEENTRY_SEGMENT_AND_ENTRY_COUNTS_MUST_BE_NON_NEGATIVE_7C6E569C =
"DeviceEntry segment count and entry count must be non-negative";
public static final String EXCEPTION_OPEN_CONSUMING_READER_IS_NOT_SUPPORTED_8B2A59A4 =
"Open consuming reader is not supported";
public static final String
EXCEPTION_DEVICEENTRY_MATERIALIZER_THRESHOLD_MUST_BE_POSITIVE_ARG_35CF53FD =
"DeviceEntry materializer threshold must be positive: %d";
public static final String
EXCEPTION_DEVICEENTRY_MATERIALIZATION_MEMORY_LIMIT_MUST_BE_POSITIVE_ARG_2EB63404 =
"DeviceEntry materialization memory limit must be positive: %d";
public static final String
EXCEPTION_DEVICEENTRY_DATA_SET_PATH_ESCAPES_THE_QUERY_DIRECTORY_ARG_394A9840 =
"DeviceEntry data set path escapes the query directory: %s";
public static final String EXCEPTION_DEVICEENTRY_SEGMENT_ID_MUST_BE_NON_NEGATIVE_ARG_F7653A57 =
"DeviceEntry segment ID must be non-negative: %d";
public static final String EXCEPTION_DEVICEENTRY_SEGMENT_HAS_INCOMPLETE_RECORD_LENGTH_0A282C19 =
"DeviceEntry segment has incomplete record length";
public static final String
EXCEPTION_INVALID_DEVICEENTRY_RECORD_LENGTH_ARG_REMAINING_BYTES_ARG_F1C43B72 =
"Invalid DeviceEntry record length %d, remaining bytes %d";
public static final String EXCEPTION_DEVICEENTRY_MATERIALIZER_HAS_ALREADY_FINISHED_5538544E =
"DeviceEntry materializer has already finished";
public static final String
EXCEPTION_NO_MATERIALIZER_IS_AVAILABLE_TO_ENFORCE_THE_DEVICEENTRY_MEMORY_LIMIT_FD0604AF =
"No materializer is available to enforce the DeviceEntry memory limit";
public static final String EXCEPTION_NO_MORE_DEVICEENTRY_RECORDS_ARE_AVAILABLE_8D51C199 =
"No more DeviceEntry records are available";
public static final String EXCEPTION_ONLY_INMEMORYDEVICEENTRYDATASET_SUPPORTS_GET_INLINE_DEVICE_ENTRIES_07A52CAB =
"Only InMemoryDeviceEntryDataSet supports get inline device entries";

}
Original file line number Diff line number Diff line change
Expand Up @@ -1465,5 +1465,8 @@ private DataNodeMiscMessages() {}
"扩容过程中缺少对齐列索引 %d 的值数组";
public static final String EXCEPTION_MISSING_VALUE_ARRAYS_FOR_ALIGNED_COLUMN_INDEX_ARG_DURING_MARK_NULL_VALUE_2893628E =
"标记空值过程中缺少对齐列索引 %d 的值数组";
public static final String
LOG_TABLE_QUERY_DEVICE_ENTRY_BATCH_SIZE_IN_BYTES_ARG_EXCEEDS_DN_THRIFT_MAX_FRAME_SIZE_ARG_USING_ARG_AS_THE_EFFECTIVE_VALUE_2AE1BEDA =
"table_query_device_entry_batch_size_in_bytes(%d)超过最大 RPC payload(dn_thrift_max_frame_size %d 减去 1024 字节),将使用 %d 作为生效值";

}
Original file line number Diff line number Diff line change
Expand Up @@ -4577,5 +4577,49 @@ private DataNodeQueryMessages() {}
public static final String EXCEPTION_VISIBLEALIASES_IS_NULL_630B27F1 = "visibleAliases 不能为空";
public static final String EXCEPTION_HAS_NO_PERMISSION_TO_EXECUTE_ARG_BECAUSE_ONLY_THE_SUPERUSER_CAN_ALTER_HIM_HERSELF_C5902893 =
"无权执行 %s,因为只有超级用户可以修改其自身。";
public static final String
LOG_FAILED_TO_CLEAN_DEVICEENTRY_DATA_SET_ASYNCHRONOUSLY_QUERYID_ARG_PLANNODEID_ARG_9106C4C5 =
"异步清理 DeviceEntry 数据集失败:queryId=%s,planNodeId=%s";
public static final String LOG_FAILED_TO_CLEAN_DEVICEENTRY_SPILL_DIRECTORY_FOR_QUERY_ARG_53D9C1FC =
"清理 query %s 的 DeviceEntry spill 目录失败";
public static final String
LOG_FAILED_TO_CLOSE_DEVICEENTRY_SPILL_WRITER_DURING_CLEANUP_ARG_EA7F7941 =
"清理过程中关闭 DeviceEntry spill writer 失败:%s";
public static final String
LOG_FAILED_TO_CLOSE_DEVICEENTRY_DATA_SET_AFTER_INDEX_SCAN_FAILURE_ARG_57F04319 =
"index scan 失败后关闭 DeviceEntry data set 失败:%s";
public static final String
EXCEPTION_DEVICEENTRY_SPILL_SEGMENT_UNAVAILABLE_MAY_BE_DUE_TO_TIMEOUT_OR_KILL_ARG_B932D10D =
"DeviceEntry spill segment 不可用:%s。查询可能因超时或主动 kill 而终止。";
public static final String
EXCEPTION_DEVICEENTRY_SEGMENT_AND_ENTRY_COUNTS_MUST_BE_NON_NEGATIVE_7C6E569C =
"DeviceEntry segment count 和 entry count 不能为负数";
public static final String EXCEPTION_OPEN_CONSUMING_READER_IS_NOT_SUPPORTED_8B2A59A4 =
"不支持打开消费型读取器";
public static final String
EXCEPTION_DEVICEENTRY_MATERIALIZER_THRESHOLD_MUST_BE_POSITIVE_ARG_35CF53FD =
"DeviceEntry materializer 阈值必须为正数:%d";
public static final String
EXCEPTION_DEVICEENTRY_MATERIALIZATION_MEMORY_LIMIT_MUST_BE_POSITIVE_ARG_2EB63404 =
"DeviceEntry materialization 内存限制必须为正数:%d";
public static final String
EXCEPTION_DEVICEENTRY_DATA_SET_PATH_ESCAPES_THE_QUERY_DIRECTORY_ARG_394A9840 =
"DeviceEntry data set 路径超出 query 目录:%s";
public static final String EXCEPTION_DEVICEENTRY_SEGMENT_ID_MUST_BE_NON_NEGATIVE_ARG_F7653A57 =
"DeviceEntry segment ID 不能为负数:%d";
public static final String EXCEPTION_DEVICEENTRY_SEGMENT_HAS_INCOMPLETE_RECORD_LENGTH_0A282C19 =
"DeviceEntry segment 的记录长度不完整";
public static final String
EXCEPTION_INVALID_DEVICEENTRY_RECORD_LENGTH_ARG_REMAINING_BYTES_ARG_F1C43B72 =
"DeviceEntry 记录长度 %d 非法,剩余字节数为 %d";
public static final String EXCEPTION_DEVICEENTRY_MATERIALIZER_HAS_ALREADY_FINISHED_5538544E =
"DeviceEntry materializer 已结束";
public static final String
EXCEPTION_NO_MATERIALIZER_IS_AVAILABLE_TO_ENFORCE_THE_DEVICEENTRY_MEMORY_LIMIT_FD0604AF =
"没有可用于执行 DeviceEntry 内存限制的 materializer";
public static final String EXCEPTION_NO_MORE_DEVICEENTRY_RECORDS_ARE_AVAILABLE_8D51C199 =
"没有更多可用的 DeviceEntry 记录";
public static final String EXCEPTION_ONLY_INMEMORYDEVICEENTRYDATASET_SUPPORTS_GET_INLINE_DEVICE_ENTRIES_07A52CAB =
"只有 InMemoryDeviceEntryDataSet 支持获取内存中的设备条目";

}
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,12 @@ public class IoTDBConfig {
private String queryDir =
IoTDBConstant.DN_DEFAULT_DATA_DIR + File.separator + IoTDBConstant.QUERY_FOLDER_NAME;

/**
* Maximum DeviceEntry bytes kept in memory before a table-query spill, capped by the effective
* Thrift frame size minus 1 KiB reserved for the RPC response envelope.
*/
private long tableQueryDeviceEntryBatchSizeInBytes;

/** External lib directory, stores user-uploaded JAR files */
private String extDir = IoTDBConstant.EXT_FOLDER_NAME;

Expand Down Expand Up @@ -1792,6 +1798,14 @@ public void setQueryDir(String queryDir) {
this.queryDir = queryDir;
}

public long getTableQueryDeviceEntryBatchSizeInBytes() {
return tableQueryDeviceEntryBatchSizeInBytes;
}

public void setTableQueryDeviceEntryBatchSizeInBytes(long tableQueryDeviceEntryBatchSizeInBytes) {
this.tableQueryDeviceEntryBatchSizeInBytes = tableQueryDeviceEntryBatchSizeInBytes;
}

public String getRatisDataRegionSnapshotDir() {
return ratisDataRegionSnapshotDir;
}
Expand Down
Loading
Loading