This is an automated email from the ASF dual-hosted git repository.
yongzao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new d703886e2c8 [Bug fix] The partition table is cleaned incorrectly when
set only device TTL for tree mode (#17123)
d703886e2c8 is described below
commit d703886e2c8ee24c54c013243e4a477497cc4e2f
Author: Yongzao <[email protected]>
AuthorDate: Fri Jan 30 23:08:27 2026 +0800
[Bug fix] The partition table is cleaned incorrectly when set only device
TTL for tree mode (#17123)
---
.../partition/IoTDBPartitionTableAutoCleanIT.java | 57 +++++++++++++++++++++-
.../iotdb/confignode/manager/TTLManager.java | 14 ++++++
.../iotdb/confignode/persistence/TTLInfo.java | 17 +++++++
.../procedure/PartitionTableAutoCleaner.java | 18 +++++--
.../apache/iotdb/commons/schema/ttl/TTLCache.java | 29 +++++++++++
5 files changed, 129 insertions(+), 6 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/confignode/it/partition/IoTDBPartitionTableAutoCleanIT.java
b/integration-test/src/test/java/org/apache/iotdb/confignode/it/partition/IoTDBPartitionTableAutoCleanIT.java
index 3f3596746f0..d150348c096 100644
---
a/integration-test/src/test/java/org/apache/iotdb/confignode/it/partition/IoTDBPartitionTableAutoCleanIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/confignode/it/partition/IoTDBPartitionTableAutoCleanIT.java
@@ -50,7 +50,7 @@ public class IoTDBPartitionTableAutoCleanIT {
private static final int TEST_REPLICATION_FACTOR = 1;
private static final long TEST_TIME_PARTITION_INTERVAL = 604800000;
- private static final long TEST_TTL_CHECK_INTERVAL = 5_000;
+ private static final long TEST_TTL_CHECK_INTERVAL_IN_MS = 5_00;
private static final TTimePartitionSlot TEST_CURRENT_TIME_SLOT =
new TTimePartitionSlot()
@@ -68,7 +68,7 @@ public class IoTDBPartitionTableAutoCleanIT {
.setSchemaReplicationFactor(TEST_REPLICATION_FACTOR)
.setDataReplicationFactor(TEST_REPLICATION_FACTOR)
.setTimePartitionInterval(TEST_TIME_PARTITION_INTERVAL)
- .setTTLCheckInterval(TEST_TTL_CHECK_INTERVAL);
+ .setTTLCheckInterval(TEST_TTL_CHECK_INTERVAL_IN_MS);
// Init 1C1D environment
EnvFactory.getEnv().initClusterEnvironment(1, 1);
@@ -135,6 +135,59 @@ public class IoTDBPartitionTableAutoCleanIT {
Assert.fail("The PartitionTable in the ConfigNode is not auto cleaned!");
}
+ @Test
+ public void testAutoCleanTakesNoEffectsForTreeDeviceTTL() throws Exception {
+ try (Connection connection =
EnvFactory.getEnv().getConnection(BaseEnv.TREE_SQL_DIALECT);
+ Statement statement = connection.createStatement()) {
+ // Create databases and insert test data
+ for (int i = 0; i < 3; i++) {
+ String databaseName = String.format("%s%d", TREE_DATABASE_PREFIX, i);
+ statement.execute(String.format("CREATE DATABASE %s", databaseName));
+ statement.execute(
+ String.format(
+ "CREATE TIMESERIES %s.s WITH DATATYPE=INT64,ENCODING=PLAIN",
databaseName));
+ // Insert expired data
+ statement.execute(
+ String.format(
+ "INSERT INTO %s(timestamp, s) VALUES (%d, %d)",
+ databaseName, TEST_CURRENT_TIME_SLOT.getStartTime() - TEST_TTL
* 2, -1));
+ // Insert existed data
+ statement.execute(
+ String.format(
+ "INSERT INTO %s(timestamp, s) VALUES (%d, %d)",
+ databaseName, TEST_CURRENT_TIME_SLOT.getStartTime(), 1));
+ // Create an empty device and set a TTL.
+ // This TTL should not trigger the auto cleaner,
+ // since the database does not have a TTL.
+ statement.execute(
+ String.format(
+ "CREATE TIMESERIES %s.m.empty WITH
DATATYPE=INT64,ENCODING=PLAIN", databaseName));
+ statement.execute(String.format("SET TTL TO %s.m.** %d", databaseName,
TEST_TTL));
+ }
+ }
+
+ TDataPartitionReq req = new TDataPartitionReq();
+ for (int i = 0; i < 3; i++) {
+ req.putToPartitionSlotsMap(String.format("%s%d", TREE_DATABASE_PREFIX,
i), new TreeMap<>());
+ }
+ try (SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient)
EnvFactory.getEnv().getLeaderConfigNodeConnection()) {
+ for (int retry = 0; retry < 10; retry++) {
+ // Ensure the partitions are not cleaned
+ boolean partitionTableAutoCleaned = false;
+ TDataPartitionTableResp resp = client.getDataPartitionTable(req);
+ if (TSStatusCode.SUCCESS_STATUS.getStatusCode() ==
resp.getStatus().getCode()) {
+ partitionTableAutoCleaned =
+ resp.getDataPartitionTable().entrySet().stream()
+ .flatMap(e1 -> e1.getValue().entrySet().stream())
+ .allMatch(e2 -> e2.getValue().size() == 1);
+ }
+ Assert.assertFalse(partitionTableAutoCleaned);
+ TimeUnit.SECONDS.sleep(1);
+ }
+ }
+ }
+
@Test
public void testAutoCleanPartitionTableForTableModel() throws Exception {
try (final Connection connection =
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/TTLManager.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/TTLManager.java
index e7ec400d0a9..b5c2e4900d7 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/TTLManager.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/TTLManager.java
@@ -130,6 +130,20 @@ public class TTLManager {
return ttlInfo.getTTLCount();
}
+ /**
+ * Get the maximum ttl of the corresponding database level.
+ *
+ * @param database the path of the database.
+ * @return the maximum ttl of the corresponding database level.
+ */
+ public long getDatabaseLevelTTL(final String database) {
+ final long ttl = ttlInfo.getDatabaseLevelTTL(database);
+ return ttl == Long.MAX_VALUE || ttl < 0
+ ? ttl
+ : CommonDateTimeUtils.convertMilliTimeWithPrecision(
+ ttl,
CommonDescriptor.getInstance().getConfig().getTimestampPrecision());
+ }
+
/**
* Get the maximum ttl of the subtree of the corresponding database.
*
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/TTLInfo.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/TTLInfo.java
index 0d07f9414d4..f38e711f6d5 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/TTLInfo.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/TTLInfo.java
@@ -158,6 +158,23 @@ public class TTLInfo implements SnapshotProcessor {
}
}
+ /**
+ * Get the maximum ttl of the corresponding database level.
+ *
+ * @param database the path of the database.
+ * @return the maximum ttl of the corresponding database level.
+ */
+ public long getDatabaseLevelTTL(final String database) {
+ lock.readLock().lock();
+ try {
+ return ttlCache.getDatabaseLevelTTL(database);
+ } catch (IllegalPathException e) {
+ return TTLCache.NULL_TTL;
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
/**
* Get the maximum ttl of the subtree of the corresponding database.
*
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/PartitionTableAutoCleaner.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/PartitionTableAutoCleaner.java
index 2ab15283207..84f12a78ec4 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/PartitionTableAutoCleaner.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/PartitionTableAutoCleaner.java
@@ -61,10 +61,20 @@ public class PartitionTableAutoCleaner<Env> extends
InternalProcedure<Env> {
List<String> databases =
configManager.getClusterSchemaManager().getDatabaseNames(null);
Map<String, Long> databaseTTLMap = new TreeMap<>();
for (String database : databases) {
- long databaseTTL =
- PathUtils.isTableModelDatabase(database)
- ?
configManager.getClusterSchemaManager().getDatabaseMaxTTL(database)
- : configManager.getTTLManager().getDatabaseMaxTTL(database);
+ long databaseTTL;
+ if (PathUtils.isTableModelDatabase(database)) {
+ // For table mode, the auto cleaner takes effect
+ // when the maximum TTL among tables is less than Long.MAX_VALUE.
+ // Because the database-level TTL do not affect data in table mode.
+ databaseTTL =
configManager.getClusterSchemaManager().getDatabaseMaxTTL(database);
+ } else {
+ databaseTTL =
configManager.getTTLManager().getDatabaseLevelTTL(database);
+ if (0 < databaseTTL && databaseTTL < Long.MAX_VALUE) {
+ // For tree mode, the auto cleaner takes effect only when the
database-level TTL is set.
+ // Subsequently, we employ the maximum TTL among all time series in
this database.
+ databaseTTL =
configManager.getTTLManager().getDatabaseMaxTTL(database);
+ }
+ }
databaseTTLMap.put(database, databaseTTL);
}
LOGGER.info(
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/ttl/TTLCache.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/ttl/TTLCache.java
index 9bed883a275..3bc72ed0472 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/ttl/TTLCache.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/ttl/TTLCache.java
@@ -175,6 +175,35 @@ public class TTLCache {
return node.ttl;
}
+ /**
+ * Get the maximum ttl of the corresponding database level.
+ *
+ * @param database the path of the database.
+ * @return the maximum ttl of the corresponding database level.
+ * @throws IllegalPathException if the database path is illegal.
+ */
+ public long getDatabaseLevelTTL(String database) throws IllegalPathException
{
+ long curTTL = NULL_TTL;
+ // Get global TTL root.** if exists
+ CacheNode curNode =
ttlCacheTree.searchChild(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD);
+ if (curNode != null && curNode.ttl < Long.MAX_VALUE) {
+ curTTL = curNode.ttl;
+ }
+ // Compare database TTL if exists
+ curNode = ttlCacheTree.searchChild(database);
+ if (curNode != null && curNode.ttl < Long.MAX_VALUE) {
+ curTTL = Math.max(curTTL, curNode.ttl);
+ }
+ // Compare database.** TTL if exists
+ curNode =
+ ttlCacheTree.searchChild(
+ database + IoTDBConstant.PATH_SEPARATOR +
IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD);
+ if (curNode != null && curNode.ttl < Long.MAX_VALUE) {
+ curTTL = Math.max(curTTL, curNode.ttl);
+ }
+ return curTTL;
+ }
+
/**
* Get the maximum ttl of the subtree of the corresponding database.
*