This is an automated email from the ASF dual-hosted git repository.
jt2594838 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 fb0fb2095de Improve schema metadata error messages and recovery logs
(#18937)
fb0fb2095de is described below
commit fb0fb2095de39d9eea199c79a0d490f8a18765ed
Author: Caideyipi <[email protected]>
AuthorDate: Wed Oct 21 19:35:46 2026 +0800
Improve schema metadata error messages and recovery logs (#18937)
* Improve schema metadata error messages and recovery logs
* Fix SDT property error expectation in integration test
---
.../org/apache/iotdb/db/it/IoTDBSimpleQueryIT.java | 2 +-
.../iotdb/db/i18n/DataNodeSchemaMessages.java | 25 ++--
.../iotdb/db/i18n/DataNodeSchemaMessages.java | 25 ++--
.../apache/iotdb/db/schemaengine/SchemaEngine.java | 6 +-
.../schemaregion/impl/SchemaRegionMemoryImpl.java | 9 +-
.../schemaregion/utils/MetaFormatUtils.java | 36 ++++--
.../template/ClusterTemplateManager.java | 36 +++---
.../i18n/DataNodeSchemaMessagesZhFormatTest.java | 8 +-
.../iotdb/db/metadata/MetaFormatUtilsTest.java | 111 +++++++++++++++++
.../template/ClusterTemplateManagerTest.java | 132 +++++++++++++++++++++
10 files changed, 333 insertions(+), 57 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSimpleQueryIT.java
b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSimpleQueryIT.java
index 677128d0bd8..599f2cb8611 100644
---
a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSimpleQueryIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSimpleQueryIT.java
@@ -111,7 +111,7 @@ public class IoTDBSimpleQueryIT {
} catch (Exception e) {
assertEquals(
TSStatusCode.ILLEGAL_PARAMETER.getStatusCode()
- + ": SDT compression deviation cannot be negative. Failed to
create timeseries for path root.sg1.d0.s1",
+ + ": SDT property compdev must be non-negative, but was
[-2]. Failed to create timeseries for path root.sg1.d0.s1",
e.getMessage());
}
diff --git
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeSchemaMessages.java
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeSchemaMessages.java
index c0e0d67346b..06b0080d7a7 100644
---
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeSchemaMessages.java
+++
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeSchemaMessages.java
@@ -171,7 +171,7 @@ public final class DataNodeSchemaMessages {
public static final String ERROR_DURING_INIT_SCHEMA_REGION =
"Error occurred during initializing schemaRegion {}";
public static final String FAILED_TO_RECOVER_TAG_INDEX =
- "Failed to recover tagIndex for {} in schemaRegion {}.";
+ "Failed to recover tag index for measurement [{}] in database [{}],
SchemaRegion [{}], tag offset [{}].";
public static final String FAILED_TO_READ_TAG_ATTRIBUTE =
"Failed to read tag and attribute info because {}";
@@ -213,19 +213,19 @@ public final class DataNodeSchemaMessages {
public static final String DATABASE_NAME_ILLEGAL_CHARS =
"The database name can only contain english or chinese characters,
numbers, backticks and underscores. %s";
public static final String SDT_COMPRESSION_DEVIATION_REQUIRED =
- "SDT compression deviation is required";
+ "SDT compression requires the compdev property";
public static final String SDT_COMPRESSION_DEVIATION_NEGATIVE =
- "SDT compression deviation cannot be negative";
+ "SDT property compdev must be non-negative, but was [%s]";
public static final String SDT_COMPRESSION_DEVIATION_FORMAT_ERROR =
- "SDT compression deviation formatting error";
+ "SDT property compdev must be a number, but was [%s]";
public static final String SDT_COMPRESSION_MAX_GREATER_THAN_MIN =
- "SDT compression maximum time needs to be greater than compression
minimum time";
+ "SDT property compmaxtime [%s] must be greater than compmintime [%s]";
public static final String SDT_COMPRESSION_TIME_NEGATIVE =
- "SDT compression %s time cannot be negative";
+ "SDT property %s must be non-negative, but was [%s]";
public static final String SDT_COMPRESSION_TIME_FORMAT_ERROR =
- "SDT compression %s time formatting error";
+ "SDT property %s must be a 64-bit integer, but was [%s]";
public static final String SDT_ENABLED_NO_COMPRESSION_TIME =
- "{} enabled SDT but did not set compression {} time";
+ "Timeseries [{}] uses SDT without property [{}]; using default [{}].";
// ======================== Tag/Attribute ========================
@@ -489,9 +489,12 @@ public final class DataNodeSchemaMessages {
public static final String FAILED_TO_CREATE_TEMPLATE =
"Failed to execute create device template {} in config node, status is
{}.";
public static final String CREATE_TEMPLATE_ERROR_PREFIX = "create template
error -";
- public static final String CREATE_TEMPLATE_ERROR = "create template error.";
- public static final String GET_ALL_TEMPLATE_ERROR = "get all template
error.";
- public static final String GET_TEMPLATE_INFO_ERROR = "get template info
error.";
+ public static final String CREATE_TEMPLATE_ERROR =
+ "Failed to create schema template [%s] on ConfigNode: %s";
+ public static final String GET_ALL_TEMPLATE_ERROR =
+ "Failed to get schema templates from ConfigNode: %s";
+ public static final String GET_TEMPLATE_INFO_ERROR =
+ "Failed to get schema template [%s] from ConfigNode: %s";
public static final String FAILED_TO_SET_TEMPLATE =
"Failed to execute set device template {} on path {} in config node,
status is {}.";
diff --git
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeSchemaMessages.java
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeSchemaMessages.java
index 417f547573a..a932bc061cd 100644
---
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeSchemaMessages.java
+++
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeSchemaMessages.java
@@ -164,7 +164,7 @@ public final class DataNodeSchemaMessages {
public static final String ERROR_DURING_INIT_SCHEMA_REGION =
"初始化 schemaRegion {} 过程中发生错误";
public static final String FAILED_TO_RECOVER_TAG_INDEX =
- "恢复 {} 的 tagIndex 失败,schemaRegion 为 {}。";
+ "恢复测点 [{}] 的 tag 索引失败,数据库为 [{}],SchemaRegion 为 [{}],tag 偏移量为 [{}]。";
public static final String FAILED_TO_READ_TAG_ATTRIBUTE =
"读取 tag 和 attribute 信息失败:{}";
@@ -206,19 +206,19 @@ public final class DataNodeSchemaMessages {
public static final String DATABASE_NAME_ILLEGAL_CHARS =
"数据库名称只能包含中英文字符、数字、反引号和下划线。%s";
public static final String SDT_COMPRESSION_DEVIATION_REQUIRED =
- "SDT 压缩偏差为必填项";
+ "SDT 压缩必须设置 compdev 属性";
public static final String SDT_COMPRESSION_DEVIATION_NEGATIVE =
- "SDT 压缩偏差不能为负数";
+ "SDT 属性 compdev 必须为非负数,实际值为 [%s]";
public static final String SDT_COMPRESSION_DEVIATION_FORMAT_ERROR =
- "SDT 压缩偏差格式错误";
+ "SDT 属性 compdev 必须为数字,实际值为 [%s]";
public static final String SDT_COMPRESSION_MAX_GREATER_THAN_MIN =
- "SDT 压缩最大时间必须大于最小时间";
+ "SDT 属性 compmaxtime [%s] 必须大于 compmintime [%s]";
public static final String SDT_COMPRESSION_TIME_NEGATIVE =
- "SDT 压缩 %s 时间不能为负数";
+ "SDT 属性 %s 必须为非负数,实际值为 [%s]";
public static final String SDT_COMPRESSION_TIME_FORMAT_ERROR =
- "SDT 压缩 %s 时间格式错误";
+ "SDT 属性 %s 必须为 64 位整数,实际值为 [%s]";
public static final String SDT_ENABLED_NO_COMPRESSION_TIME =
- "{} 已启用 SDT 但未设置压缩 {} 时间";
+ "时间序列 [{}] 已启用 SDT 但未设置属性 [{}],使用默认值 [{}]。";
// ======================== Tag/Attribute 相关消息 ========================
@@ -480,9 +480,12 @@ public final class DataNodeSchemaMessages {
public static final String FAILED_TO_CREATE_TEMPLATE =
"在 ConfigNode 中执行创建设备模板 {} 失败,状态为 {}。";
public static final String CREATE_TEMPLATE_ERROR_PREFIX = "创建模板出错 - ";
- public static final String CREATE_TEMPLATE_ERROR = "创建模板出错。";
- public static final String GET_ALL_TEMPLATE_ERROR = "获取所有模板出错。";
- public static final String GET_TEMPLATE_INFO_ERROR = "获取模板信息出错。";
+ public static final String CREATE_TEMPLATE_ERROR =
+ "在 ConfigNode 上创建元数据模板 [%s] 失败:%s";
+ public static final String GET_ALL_TEMPLATE_ERROR =
+ "从 ConfigNode 获取元数据模板列表失败:%s";
+ public static final String GET_TEMPLATE_INFO_ERROR =
+ "从 ConfigNode 获取元数据模板 [%s] 失败:%s";
public static final String FAILED_TO_SET_TEMPLATE =
"在 ConfigNode 中执行设置设备模板 {} 到路径 {} 失败,状态为 {}。";
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/SchemaEngine.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/SchemaEngine.java
index ce8ba551a40..87410d51c99 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/SchemaEngine.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/SchemaEngine.java
@@ -346,12 +346,12 @@ public class SchemaEngine {
timeRecord);
return schemaRegion;
} catch (final MetadataException e) {
- logger.error(
+ throw new RuntimeException(
String.format(
DataNodeSchemaMessages.SCHEMA_REGION_FAILED_TO_RECOVER,
schemaRegionId.getId(),
- storageGroup));
- throw new RuntimeException(e);
+ storageGroup),
+ e);
}
};
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java
index e15e7e132c8..dfc305656ae 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java
@@ -166,7 +166,6 @@ import java.util.stream.Collectors;
import static
org.apache.iotdb.calc.plan.planner.TableOperatorGenerator.makeLayout;
import static
org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.TableDeviceSchemaFetcher.convertTagValuesToDeviceID;
-import static org.apache.tsfile.common.constant.TsFileConstant.PATH_SEPARATOR;
/**
* This class takes the responsibility of serialization of all the metadata
info of one certain
@@ -625,10 +624,14 @@ public class SchemaRegionMemoryImpl implements
ISchemaRegion {
try {
tagManager.recoverIndex(measurementMNode.getOffset(),
measurementMNode);
} catch (final IOException e) {
+ // Snapshot callbacks run before the measurement is attached
to its parent.
logger.error(
DataNodeSchemaMessages.FAILED_TO_RECOVER_TAG_INDEX,
- databaseFullPath + PATH_SEPARATOR +
measurementMNode.getFullPath(),
- schemaRegionId);
+ measurementMNode.getName(),
+ databaseFullPath,
+ schemaRegionId,
+ measurementMNode.getOffset(),
+ e);
}
},
deviceMNode -> {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/utils/MetaFormatUtils.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/utils/MetaFormatUtils.java
index c28a918e451..8b0ae04b658 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/utils/MetaFormatUtils.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/utils/MetaFormatUtils.java
@@ -153,11 +153,16 @@ public class MetaFormatUtils {
double d = Double.parseDouble(props.get(SDT_COMP_DEV));
if (d < 0) {
throw new IllegalParameterOfPathException(
- DataNodeSchemaMessages.SDT_COMPRESSION_DEVIATION_NEGATIVE, path);
+ String.format(
+ DataNodeSchemaMessages.SDT_COMPRESSION_DEVIATION_NEGATIVE,
props.get(SDT_COMP_DEV)),
+ path);
}
} catch (NumberFormatException e) {
throw new IllegalParameterOfPathException(
- DataNodeSchemaMessages.SDT_COMPRESSION_DEVIATION_FORMAT_ERROR, path);
+ String.format(
+ DataNodeSchemaMessages.SDT_COMPRESSION_DEVIATION_FORMAT_ERROR,
+ props.get(SDT_COMP_DEV)),
+ path);
}
long compMinTime = sdtCompressionTimeFormat(SDT_COMP_MIN_TIME, props,
path);
@@ -165,7 +170,11 @@ public class MetaFormatUtils {
if (compMaxTime <= compMinTime) {
throw new IllegalParameterOfPathException(
- DataNodeSchemaMessages.SDT_COMPRESSION_MAX_GREATER_THAN_MIN, path);
+ String.format(
+ DataNodeSchemaMessages.SDT_COMPRESSION_MAX_GREATER_THAN_MIN,
+ compMaxTime,
+ compMinTime),
+ path);
}
}
@@ -174,20 +183,23 @@ public class MetaFormatUtils {
throws IllegalParameterOfPathException {
boolean isCompMaxTime = compTime.equals(SDT_COMP_MAX_TIME);
long time = isCompMaxTime ? Long.MAX_VALUE : 0;
- String s = isCompMaxTime ? "maximum" : "minimum";
if (props.containsKey(compTime)) {
+ String value = props.get(compTime);
try {
- time = Long.parseLong(props.get(compTime));
- if (time < 0) {
- throw new IllegalParameterOfPathException(
-
String.format(DataNodeSchemaMessages.SDT_COMPRESSION_TIME_NEGATIVE, s), path);
- }
- } catch (IllegalParameterOfPathException e) {
+ time = Long.parseLong(value);
+ } catch (NumberFormatException e) {
throw new IllegalParameterOfPathException(
-
String.format(DataNodeSchemaMessages.SDT_COMPRESSION_TIME_FORMAT_ERROR, s),
path);
+ String.format(
+ DataNodeSchemaMessages.SDT_COMPRESSION_TIME_FORMAT_ERROR,
compTime, value),
+ path);
+ }
+ if (time < 0) {
+ throw new IllegalParameterOfPathException(
+
String.format(DataNodeSchemaMessages.SDT_COMPRESSION_TIME_NEGATIVE, compTime,
value),
+ path);
}
} else {
- logger.info(DataNodeSchemaMessages.SDT_ENABLED_NO_COMPRESSION_TIME,
path, s);
+ logger.debug(DataNodeSchemaMessages.SDT_ENABLED_NO_COMPRESSION_TIME,
path, compTime, time);
}
return time;
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java
index 825ea757a77..816effce329 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java
@@ -130,11 +130,11 @@ public class ClusterTemplateManager implements
ITemplateManager {
e,
TSStatusCode.CREATE_TEMPLATE_ERROR.getStatusCode()));
} catch (ClientManagerException | TException e) {
- throw new RuntimeException(
- new IoTDBException(
- DataNodeSchemaMessages.CREATE_TEMPLATE_ERROR,
- e,
- TSStatusCode.CREATE_TEMPLATE_ERROR.getStatusCode()));
+ throw wrapTemplateRpcException(
+ String.format(
+ DataNodeSchemaMessages.CREATE_TEMPLATE_ERROR,
statement.getName(), e.getMessage()),
+ e,
+ TSStatusCode.CREATE_TEMPLATE_ERROR);
}
}
@@ -177,10 +177,10 @@ public class ClusterTemplateManager implements
ITemplateManager {
throw new IoTDBRuntimeException(resp.getStatus());
}
} catch (ClientManagerException | TException e) {
- throw new RuntimeException(
- new IoTDBException(
- DataNodeSchemaMessages.GET_ALL_TEMPLATE_ERROR,
- TSStatusCode.UNDEFINED_TEMPLATE.getStatusCode()));
+ throw wrapTemplateRpcException(
+ String.format(DataNodeSchemaMessages.GET_ALL_TEMPLATE_ERROR,
e.getMessage()),
+ e,
+ TSStatusCode.UNDEFINED_TEMPLATE);
}
return templatesList;
}
@@ -199,13 +199,21 @@ public class ClusterTemplateManager implements
ITemplateManager {
throw new IoTDBException(resp.status);
}
} catch (ClientManagerException | TException e) {
- throw new RuntimeException(
- new IoTDBException(
- DataNodeSchemaMessages.GET_TEMPLATE_INFO_ERROR,
- TSStatusCode.UNDEFINED_TEMPLATE.getStatusCode()));
+ throw wrapTemplateRpcException(
+ String.format(DataNodeSchemaMessages.GET_TEMPLATE_INFO_ERROR, name,
e.getMessage()),
+ e,
+ TSStatusCode.UNDEFINED_TEMPLATE);
}
}
+ private static RuntimeException wrapTemplateRpcException(
+ String message, Exception cause, TSStatusCode statusCode) {
+ IoTDBException exception = new IoTDBException(message,
statusCode.getStatusCode());
+ // Query error handling uses the deepest cause to determine the status and
message.
+ exception.addSuppressed(cause);
+ return new RuntimeException(exception);
+ }
+
@Override
public void setSchemaTemplate(String queryId, String name, PartialPath path)
{
try (ConfigNodeClient configNodeClient =
@@ -256,7 +264,7 @@ public class ClusterTemplateManager implements
ITemplateManager {
try {
listPath.add(new PartialPath(item));
} catch (IllegalPathException e) {
- LOGGER.error(DataNodeSchemaMessages.ILLEGAL_PATH_LOG,
item);
+ LOGGER.error(DataNodeSchemaMessages.ILLEGAL_PATH_LOG,
item, e);
}
});
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/i18n/DataNodeSchemaMessagesZhFormatTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/i18n/DataNodeSchemaMessagesZhFormatTest.java
index 294ebf04b1b..44708a1c0dd 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/i18n/DataNodeSchemaMessagesZhFormatTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/i18n/DataNodeSchemaMessagesZhFormatTest.java
@@ -46,9 +46,13 @@ public class DataNodeSchemaMessagesZhFormatTest {
"跳过 128 失败,schemaRegion 目录为 C:/schema",
formatSlf4j(readZhMessage("FAILED_TO_SKIP_MLOG"), 128, "C:/schema"));
Assert.assertEquals(
- "恢复 root.sg.d.s 的 tagIndex 失败,schemaRegion 为 SchemaRegionId{1}。",
+ "恢复测点 [s] 的 tag 索引失败,数据库为 [root.sg],SchemaRegion 为
[SchemaRegionId{1}],tag 偏移量为 [128]。",
formatSlf4j(
- readZhMessage("FAILED_TO_RECOVER_TAG_INDEX"), "root.sg.d.s",
"SchemaRegionId{1}"));
+ readZhMessage("FAILED_TO_RECOVER_TAG_INDEX"),
+ "s",
+ "root.sg",
+ "SchemaRegionId{1}",
+ 128));
Assert.assertEquals(
"MTree 刷写耗时 321ms,SchemaRegion 为 SchemaRegionId{1}",
formatSlf4j(readZhMessage("MTREE_FLUSH_COST"), 321,
"SchemaRegionId{1}"));
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/MetaFormatUtilsTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/MetaFormatUtilsTest.java
new file mode 100644
index 00000000000..66835c33388
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/MetaFormatUtilsTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.db.metadata;
+
+import org.apache.iotdb.db.exception.metadata.IllegalParameterOfPathException;
+import org.apache.iotdb.db.schemaengine.schemaregion.utils.MetaFormatUtils;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+public class MetaFormatUtilsTest {
+
+ private static final String PATH = "root.sg.d.s";
+
+ @Test
+ public void testNonNumericSdtProperties() {
+ for (String property : new String[] {"compdev", "compmintime",
"compmaxtime"}) {
+ assertInvalidProperty(property, "invalid");
+ }
+ }
+
+ @Test
+ public void testNegativeSdtProperties() {
+ for (String property : new String[] {"compdev", "compmintime",
"compmaxtime"}) {
+ assertInvalidProperty(property, "-1");
+ }
+ }
+
+ @Test
+ public void testSdtTimeOverflow() {
+ for (String property : new String[] {"compmintime", "compmaxtime"}) {
+ assertInvalidProperty(property, "9223372036854775808");
+ assertInvalidProperty(property, "-9223372036854775809");
+ }
+ }
+
+ @Test
+ public void testInvalidSdtTimeRange() {
+ Map<String, String> props = sdtProps();
+ props.put("compmintime", "20");
+ props.put("compmaxtime", "10");
+ IllegalParameterOfPathException exception =
+ assertThrows(
+ IllegalParameterOfPathException.class,
+ () -> MetaFormatUtils.checkTimeseriesProps(PATH, props));
+
+ assertUserParameterError(exception);
+ assertTrue(exception.getMessage().contains("compmintime [20]"));
+ assertTrue(exception.getMessage().contains("compmaxtime [10]"));
+ }
+
+ @Test
+ public void testValidSdtTimeBoundsAndDefaults() throws
IllegalParameterOfPathException {
+ Map<String, String> props = sdtProps();
+ MetaFormatUtils.checkTimeseriesProps(PATH, props);
+ props.put("compmintime", "0");
+ MetaFormatUtils.checkTimeseriesProps(PATH, props);
+ props.put("compmaxtime", Long.toString(Long.MAX_VALUE));
+ MetaFormatUtils.checkTimeseriesProps(PATH, props);
+ }
+
+ private static void assertInvalidProperty(String property, String value) {
+ Map<String, String> props = sdtProps();
+ props.put(property, value);
+ IllegalParameterOfPathException exception =
+ assertThrows(
+ IllegalParameterOfPathException.class,
+ () -> MetaFormatUtils.checkTimeseriesProps(PATH, props));
+
+ assertUserParameterError(exception);
+ assertTrue(exception.getMessage().contains(property));
+ assertTrue(exception.getMessage().contains("[" + value + "]"));
+ }
+
+ private static void assertUserParameterError(IllegalParameterOfPathException
exception) {
+ assertEquals(TSStatusCode.ILLEGAL_PARAMETER.getStatusCode(),
exception.getErrorCode());
+ assertTrue(exception.isUserException());
+ assertTrue(exception.getMessage().contains(PATH));
+ }
+
+ private static Map<String, String> sdtProps() {
+ Map<String, String> props = new HashMap<>();
+ props.put("loss", "sdt");
+ props.put("compdev", "0.01");
+ return props;
+ }
+}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManagerTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManagerTest.java
new file mode 100644
index 00000000000..bc6cd1629b0
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManagerTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.db.schemaengine.template;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.IClientManager;
+import org.apache.iotdb.commons.consensus.ConfigRegionId;
+import org.apache.iotdb.db.protocol.client.ConfigNodeClient;
+import org.apache.iotdb.db.protocol.client.ConfigNodeClientManager;
+import org.apache.iotdb.db.protocol.client.ConfigNodeInfo;
+import org.apache.iotdb.db.protocol.thrift.OperationType;
+import
org.apache.iotdb.db.queryengine.plan.statement.metadata.template.CreateSchemaTemplateStatement;
+import org.apache.iotdb.db.utils.ErrorHandlingUtils;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.apache.thrift.TException;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.file.metadata.enums.CompressionType;
+import org.apache.tsfile.file.metadata.enums.TSEncoding;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(ConfigNodeClientManager.class)
+@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*",
"javax.management.*"})
+public class ClusterTemplateManagerTest {
+
+ @SuppressWarnings("unchecked")
+ private static final IClientManager<ConfigRegionId, ConfigNodeClient>
CLIENT_MANAGER =
+ Mockito.mock(IClientManager.class);
+
+ private ConfigNodeClient client;
+
+ @Before
+ public void setUp() throws Exception {
+ Mockito.reset(CLIENT_MANAGER);
+ client = Mockito.mock(ConfigNodeClient.class);
+ PowerMockito.mockStatic(ConfigNodeClientManager.class);
+
PowerMockito.when(ConfigNodeClientManager.getInstance()).thenReturn(CLIENT_MANAGER);
+
Mockito.when(CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)).thenReturn(client);
+ }
+
+ @Test
+ public void testGetTemplateRpcFailure() throws Exception {
+ TException rpcFailure = new TException("connection refused");
+ Mockito.when(client.getTemplate("test_template")).thenThrow(rpcFailure);
+
+ RuntimeException exception =
+ assertThrows(
+ RuntimeException.class,
+ () ->
ClusterTemplateManager.getInstance().getTemplate("test_template"));
+
+ TSStatus status = assertRpcFailure(exception, rpcFailure,
TSStatusCode.UNDEFINED_TEMPLATE);
+ assertTrue(status.getMessage().contains("test_template"));
+ }
+
+ @Test
+ public void testGetAllTemplatesRpcFailure() throws Exception {
+ TException rpcFailure = new TException("connection refused");
+ Mockito.when(client.getAllTemplates()).thenThrow(rpcFailure);
+
+ RuntimeException exception =
+ assertThrows(
+ RuntimeException.class, () ->
ClusterTemplateManager.getInstance().getAllTemplates());
+
+ assertRpcFailure(exception, rpcFailure, TSStatusCode.UNDEFINED_TEMPLATE);
+ }
+
+ @Test
+ public void testCreateTemplateRpcFailure() throws Exception {
+ TException rpcFailure = new TException("connection refused");
+
Mockito.when(client.createSchemaTemplate(Mockito.any())).thenThrow(rpcFailure);
+ CreateSchemaTemplateStatement statement =
+ new CreateSchemaTemplateStatement(
+ "test_template",
+ Collections.singletonList(Collections.singletonList("s")),
+
Collections.singletonList(Collections.singletonList(TSDataType.INT32)),
+
Collections.singletonList(Collections.singletonList(TSEncoding.PLAIN)),
+
Collections.singletonList(Collections.singletonList(CompressionType.UNCOMPRESSED)),
+ false);
+
+ RuntimeException exception =
+ assertThrows(
+ RuntimeException.class,
+ () ->
ClusterTemplateManager.getInstance().createSchemaTemplate(statement));
+
+ TSStatus status = assertRpcFailure(exception, rpcFailure,
TSStatusCode.CREATE_TEMPLATE_ERROR);
+ assertTrue(status.getMessage().contains("test_template"));
+ }
+
+ private static TSStatus assertRpcFailure(
+ RuntimeException exception, TException rpcFailure, TSStatusCode
statusCode) {
+ TSStatus status =
+ ErrorHandlingUtils.onQueryException(exception,
OperationType.EXECUTE_STATEMENT);
+ assertEquals(statusCode.getStatusCode(), status.getCode());
+ assertTrue(status.getMessage().contains("ConfigNode"));
+ assertTrue(status.getMessage().contains("connection refused"));
+ assertEquals(1, exception.getCause().getSuppressed().length);
+ assertSame(rpcFailure, exception.getCause().getSuppressed()[0]);
+ return status;
+ }
+}