From a2a2e305e9a6c0fb6f291cecc6bafbd73179a369 Mon Sep 17 00:00:00 2001 From: shuwenwei Date: Thu, 6 Aug 2026 16:48:03 +0800 Subject: [PATCH 1/3] Bump tsfile version to 2.4.1-260806-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 94c0b3a80317..43ec486debb0 100644 --- a/pom.xml +++ b/pom.xml @@ -146,7 +146,7 @@ 0.23.0 1.9 1.5.6-3 - 2.3.2-260731-SNAPSHOT + 2.4.1-260806-SNAPSHOT en From 80174c4e23ac918853ac56e494f571c6d0ea015e Mon Sep 17 00:00:00 2001 From: shuwenwei Date: Fri, 7 Aug 2026 11:11:36 +0800 Subject: [PATCH 2/3] Add COPY_TO_WRITE_ERROR status code for COPY TO write failures When the target file write fails during COPY TO (e.g. due to insufficient disk space), the IOException thrown by the underlying writer used to surface as a generic EXECUTE_STATEMENT_ERROR with no way for callers to distinguish it from other failures. Add a dedicated COPY_TO_WRITE_ERROR(725) status code and translate the write IOException in TableCopyToOperator into an IoTDBRuntimeException carrying it. The exception is thrown without a cause chain so the error code survives the query engine's getRootCause() unwrapping; the original IOException is logged with its full stack trace at the translation site for debuggability. Also null out the internal writer in TsFileFormatCopyToWriter on failure so operator teardown does not double-close and the residual target file is cleaned up. Messages are kept format-agnostic ("target file") since COPY TO may support more output formats in the future. --- .../org/apache/iotdb/rpc/TSStatusCode.java | 1 + .../iotdb/db/i18n/DataNodeQueryMessages.java | 4 +++ .../iotdb/db/i18n/DataNodeQueryMessages.java | 4 +++ .../process/copyto/TableCopyToOperator.java | 27 +++++++++++++++++-- .../tsfile/TsFileFormatCopyToWriter.java | 20 +++++++++----- .../iotdb/commons/i18n/UtilMessages.java | 2 ++ .../iotdb/commons/i18n/UtilMessages.java | 2 ++ .../iotdb/commons/utils/StatusUtils.java | 3 +++ 8 files changed, 55 insertions(+), 8 deletions(-) diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java index ae988b8d1c73..e7b9cff080c5 100644 --- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java +++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java @@ -151,6 +151,7 @@ public enum TSStatusCode { CANNOT_FETCH_FI_STATE(722), REPEATED_RPC_CALL(723), CANNOT_READ_TSFILE(724), + COPY_TO_WRITE_ERROR(725), // OBJECT OBJECT_NOT_EXISTS(740), diff --git a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java index 0e9e8158ac7b..ade27d85d09e 100644 --- a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java +++ b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java @@ -2428,6 +2428,10 @@ public final class DataNodeQueryMessages { "Duplicate tag column in TAGS clause: %s"; public static final String EXCEPTION_DUPLICATE_OPTION_IN_COPY_TO_STATEMENT_ARG_99CFE09F = "Duplicate option in COPY TO statement: %s"; + public static final String EXCEPTION_FAILED_TO_WRITE_THE_TARGET_FILE_ARG_5AC3025D = + "Failed to write the target file: %s"; + public static final String LOG_FAILED_TO_WRITE_THE_TARGET_FILE_DURING_COPY_TO_EE25EF37 = + "Failed to write the target file during COPY TO"; public static final String SIMULTANEOUS_SETTING_OF_MONTHLY_AND_NON_MONTHLY_INTERVALS_IS_NOT_SUPPORTED = "Simultaneous setting of monthly and non-monthly intervals is not supported."; public static final String DON_T_NEED_TO_SPECIFY_TIME_COLUMN_WHILE_EITHER_TIME_BOUND_OR_FILL_GROUP_PARAMETER_IS_NOT = diff --git a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java index 1ab922fa121c..31278e1e48ea 100644 --- a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java +++ b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java @@ -2803,6 +2803,10 @@ public final class DataNodeQueryMessages { "TAGS 子句中存在重复的 TAG 列:%s"; public static final String EXCEPTION_DUPLICATE_OPTION_IN_COPY_TO_STATEMENT_ARG_99CFE09F = "COPY TO 语句中存在重复的选项:%s"; + public static final String EXCEPTION_FAILED_TO_WRITE_THE_TARGET_FILE_ARG_5AC3025D = + "写入目标文件失败:%s"; + public static final String LOG_FAILED_TO_WRITE_THE_TARGET_FILE_DURING_COPY_TO_EE25EF37 = + "COPY TO 写入目标文件失败"; public static final String SIMULTANEOUS_SETTING_OF_MONTHLY_AND_NON_MONTHLY_INTERVALS_IS_NOT_SUPPORTED = "不支持同时设置月级和非月级时间间隔。"; public static final String DON_T_NEED_TO_SPECIFY_TIME_COLUMN_WHILE_EITHER_TIME_BOUND_OR_FILL_GROUP_PARAMETER_IS_NOT = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/copyto/TableCopyToOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/copyto/TableCopyToOperator.java index 7c4ff8223139..030a5ca4ae61 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/copyto/TableCopyToOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/copyto/TableCopyToOperator.java @@ -21,6 +21,7 @@ import org.apache.iotdb.calc.execution.operator.Operator; import org.apache.iotdb.calc.execution.operator.process.ProcessOperator; +import org.apache.iotdb.commons.exception.IoTDBRuntimeException; import org.apache.iotdb.commons.queryengine.execution.MemoryEstimationHelper; import org.apache.iotdb.commons.schema.column.ColumnHeader; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; @@ -28,11 +29,14 @@ import org.apache.iotdb.db.queryengine.execution.operator.process.copyto.tsfile.CopyToTsFileOptions; import org.apache.iotdb.db.queryengine.execution.operator.process.copyto.tsfile.TsFileFormatCopyToWriter; import org.apache.iotdb.db.storageengine.rescon.disk.TierManager; +import org.apache.iotdb.rpc.TSStatusCode; import com.google.common.util.concurrent.ListenableFuture; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.read.common.block.TsBlock; import org.apache.tsfile.utils.RamUsageEstimator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; @@ -40,6 +44,7 @@ import java.util.List; public class TableCopyToOperator implements ProcessOperator { + private static final Logger LOGGER = LoggerFactory.getLogger(TableCopyToOperator.class); private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(TableCopyToOperator.class); @@ -79,7 +84,11 @@ public OperatorContext getOperatorContext() { public TsBlock next() throws Exception { IFormatCopyToWriter formatWriter = getWriter(); if (!childOperator.hasNext()) { - formatWriter.seal(); + try { + formatWriter.seal(); + } catch (IOException e) { + throw newCopyToWriteError(e); + } isFinished = true; return formatWriter.buildResultTsBlock(); } @@ -88,10 +97,24 @@ public TsBlock next() throws Exception { return null; } hasData = true; - formatWriter.write(tsBlock); + try { + formatWriter.write(tsBlock); + } catch (IOException e) { + throw newCopyToWriteError(e); + } return null; } + private IoTDBRuntimeException newCopyToWriteError(IOException e) { + LOGGER.warn( + DataNodeQueryMessages.LOG_FAILED_TO_WRITE_THE_TARGET_FILE_DURING_COPY_TO_EE25EF37, e); + return new IoTDBRuntimeException( + String.format( + DataNodeQueryMessages.EXCEPTION_FAILED_TO_WRITE_THE_TARGET_FILE_ARG_5AC3025D, + e.getMessage()), + TSStatusCode.COPY_TO_WRITE_ERROR.getStatusCode()); + } + private IFormatCopyToWriter getWriter() throws Exception { if (writer != null) { return writer; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/copyto/tsfile/TsFileFormatCopyToWriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/copyto/tsfile/TsFileFormatCopyToWriter.java index 08b8ce127baf..50600003b13c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/copyto/tsfile/TsFileFormatCopyToWriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/copyto/tsfile/TsFileFormatCopyToWriter.java @@ -136,7 +136,12 @@ public TsFileFormatCopyToWriter( @Override public void write(TsBlock tsBlock) throws Exception { - tsFileWriter.get().write(tsBlock); + try { + tsFileWriter.get().write(tsBlock); + } catch (Exception e) { + tsFileWriter = null; + throw e; + } } @Override @@ -145,11 +150,14 @@ public void seal() throws Exception { return; } TableTsBlock2TsFileWriter writer = tsFileWriter.get(); - writer.close(); - // should call these methods after writer.close() - deviceCount = writer.getDeviceCount(); - rowCount = writer.getRowCount(); - tsFileWriter = null; + try { + writer.close(); + // should call these methods after writer.close() + deviceCount = writer.getDeviceCount(); + rowCount = writer.getRowCount(); + } finally { + tsFileWriter = null; + } } @Override diff --git a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/UtilMessages.java b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/UtilMessages.java index 0fbf9a941e5f..fb5a13bbd31d 100644 --- a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/UtilMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/UtilMessages.java @@ -248,6 +248,8 @@ private UtilMessages() {} public static final String MESSAGE_MEET_ERROR_CLOSE_OPERATION_1C7D0589 = "Meet error in close operation."; public static final String MESSAGE_FAIL_DO_NON_QUERY_OPERATIONS_BECAUSE_SYSTEM_READ_ONLY_10CA1ED2 = "Fail to do non-query operations because system is read-only."; public static final String MESSAGE_DISK_SPACE_INSUFFICIENT_DF6205B0 = "Disk space is insufficient."; + public static final String MESSAGE_FAILED_TO_WRITE_THE_TARGET_FILE_4C48CE25 = + "Failed to write the target file."; public static final String MESSAGE_MEET_ERROR_STARTING_UP_22A4CBFE = "Meet error while starting up."; public static final String MESSAGE_USERNAME_PASSWORD_WRONG_C44C4AF0 = "Username or password is wrong."; public static final String MESSAGE_HAS_NOT_LOGGED_A2BA0267 = "Has not logged in."; diff --git a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/UtilMessages.java b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/UtilMessages.java index 6cdf3ffaacb7..e58c42381e6d 100644 --- a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/UtilMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/UtilMessages.java @@ -246,6 +246,8 @@ private UtilMessages() {} public static final String MESSAGE_MEET_ERROR_CLOSE_OPERATION_1C7D0589 = "关闭操作中发生错误。"; public static final String MESSAGE_FAIL_DO_NON_QUERY_OPERATIONS_BECAUSE_SYSTEM_READ_ONLY_10CA1ED2 = "系统只读,无法执行非查询操作。"; public static final String MESSAGE_DISK_SPACE_INSUFFICIENT_DF6205B0 = "磁盘空间不足。"; + public static final String MESSAGE_FAILED_TO_WRITE_THE_TARGET_FILE_4C48CE25 = + "写入目标文件失败。"; public static final String MESSAGE_MEET_ERROR_STARTING_UP_22A4CBFE = "启动时发生错误。"; public static final String MESSAGE_USERNAME_PASSWORD_WRONG_C44C4AF0 = "用户名或密码错误。"; public static final String MESSAGE_HAS_NOT_LOGGED_A2BA0267 = "尚未登录。"; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/StatusUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/StatusUtils.java index f9cdb35b1188..f88844e46e69 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/StatusUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/StatusUtils.java @@ -182,6 +182,9 @@ public static TSStatus getStatus(TSStatusCode statusCode) { case DISK_SPACE_INSUFFICIENT: status.setMessage(UtilMessages.MESSAGE_DISK_SPACE_INSUFFICIENT_DF6205B0); break; + case COPY_TO_WRITE_ERROR: + status.setMessage(UtilMessages.MESSAGE_FAILED_TO_WRITE_THE_TARGET_FILE_4C48CE25); + break; case START_UP_ERROR: status.setMessage(UtilMessages.MESSAGE_MEET_ERROR_STARTING_UP_22A4CBFE); break; From b8036acefc277f4faeae48ee735e69100ca92498 Mon Sep 17 00:00:00 2001 From: shuwenwei Date: Fri, 7 Aug 2026 11:28:01 +0800 Subject: [PATCH 3/3] Log COPY_TO_WRITE_ERROR at info level in query exception handling COPY_TO_WRITE_ERROR is a known, expected failure already logged with its full stack trace at the failure source (TableCopyToOperator). Log it at info level without a stack trace in ErrorHandlingUtils.onQueryException to avoid duplicating the stack in the RPC layer, consistent with CANNOT_READ_TSFILE. --- .../java/org/apache/iotdb/db/utils/ErrorHandlingUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/ErrorHandlingUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/ErrorHandlingUtils.java index bbe30acba01c..5e9c1758664e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/ErrorHandlingUtils.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/ErrorHandlingUtils.java @@ -135,7 +135,8 @@ public static TSStatus onQueryException(Exception e, String operation, TSStatusC || status.getCode() == TSStatusCode.PATH_ALREADY_EXIST.getStatusCode() || status.getCode() == TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode() || status.getCode() == TSStatusCode.QUERY_TIMEOUT.getStatusCode() - || status.getCode() == TSStatusCode.CANNOT_READ_TSFILE.getStatusCode()) { + || status.getCode() == TSStatusCode.CANNOT_READ_TSFILE.getStatusCode() + || status.getCode() == TSStatusCode.COPY_TO_WRITE_ERROR.getStatusCode()) { LOGGER.info(message); } else { LOGGER.warn(message, e);