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 86ae95729ab [Subscription][Pipe] Improve receiver error messages and
diagnostics (#18932)
86ae95729ab is described below
commit 86ae95729ab5bf205aa88221d6e7cff1dc99c75d
Author: Caideyipi <[email protected]>
AuthorDate: Tue Oct 20 19:18:09 2026 +0800
[Subscription][Pipe] Improve receiver error messages and diagnostics
(#18932)
* [Subscription][Pipe] Improve receiver error messages and diagnostics
* [Subscription] Explain rejected request slices
---
.../apache/iotdb/db/i18n/DataNodePipeMessages.java | 83 +++++++++++
.../apache/iotdb/db/i18n/DataNodePipeMessages.java | 83 +++++++++++
.../legacy/IoTDBLegacyPipeReceiverAgent.java | 41 ++++--
.../receiver/SubscriptionReceiverV1.java | 157 ++++++++++++++-------
.../legacy/IoTDBLegacyPipeReceiverAgentTest.java | 58 +++++++-
.../receiver/SubscriptionReceiverV1Test.java | 151 +++++++++++++++++++-
6 files changed, 505 insertions(+), 68 deletions(-)
diff --git
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
index e676d5c93e9..374a35e1642 100644
---
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
+++
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
@@ -2710,4 +2710,87 @@ public final class DataNodePipeMessages {
"[%s] Subscription entry requires %d bytes, above the current per-queue
maximum %d bytes (DataNode budget %d bytes). Reduce the write batch/field size
or increase subscription materialization memory. WAL progress has not
advanced.";
public static final String
LOG_SUBSCRIPTION_FAILED_TO_REPLAY_DELIVERY_BATCH_ARG_FROM_WAL_RETAINING_UNCOMMITTED_STATE_0654EACD
=
"Subscription failed to replay delivery batch {} from WAL; retaining
uncommitted state";
+
+ // ===================== SUBSCRIPTION RECEIVER RESPONSES
=====================
+
+ public static final String
+
MESSAGE_SUBSCRIPTION_MALFORMED_REQUEST_SLICE_RESEND_THE_COMPLETE_REQUEST_STARTING_FROM_SLICE_INDEX_0_D91FD262
=
+ "Subscription: malformed request slice. Resend the complete request
starting from slice index 0.";
+ public static final String
+
MESSAGE_SUBSCRIPTION_INVALID_OR_OUT_OF_ORDER_REQUEST_SLICE_ORDERID_ARG_SLICEINDEX_ARG_SLICECOUNT_ARG_ORIGINBODYSIZE_ARG_BYTES_RESEND_THE_COMPLETE_REQUEST_STARTING_FROM_SLICE_INDEX_0_A08D6D8E
=
+ "Subscription: invalid or out-of-order request slice (orderId %d,
sliceIndex %d, sliceCount %d, originBodySize %d bytes). Resend the complete
request starting from slice index 0.";
+ public static final String
+
MESSAGE_SUBSCRIPTION_REQUEST_BODY_SIZE_ARG_BYTES_EXCEEDS_THE_RECEIVER_LIMIT_OF_ARG_BYTES_SPLIT_THE_OPERATION_INTO_SMALLER_REQUESTS_C9513027
=
+ "Subscription: request body size %d bytes exceeds the receiver limit
of %d bytes. Split the operation into smaller requests.";
+
+ public static final String
MESSAGE_MISSING_CONSUMER_CONFIG_PLEASE_HANDSHAKE_FIRST_CE588329 =
+ "Missing consumer config, please handshake first.";
+ public static final String
MESSAGE_UNKNOWN_PIPESUBSCRIBEREQUESTTYPE_ARG_7EBB93F8 =
+ "Unknown PipeSubscribeRequestType %s.";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_HANDSHAKING_WITH_REQUEST_ARG_ARG_C1BC903B
=
+ "Subscription: something unexpected happened when handshaking with
request %s: %s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_HEARTBEAT_WITH_REQUEST_ARG_ARG_790B328F
=
+ "Subscription: something unexpected happened when heartbeat with
request %s: %s";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_FETCH_ENDPOINTS_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_267904CB
=
+ "Subscription: Failed to fetch endpoints for consumer %s in config
node, exception is %s.";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_SUBSCRIBING_WITH_REQUEST_ARG_ARG_084648A4
=
+ "Subscription: something unexpected happened when subscribing with
request %s: %s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_UNSUBSCRIBING_WITH_REQUEST_ARG_ARG_1C15D8EC
=
+ "Subscription: something unexpected happened when unsubscribing with
request %s: %s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_POLLING_WITH_REQUEST_ARG_ARG_DE2CF98B
=
+ "Subscription: something unexpected happened when polling with
request %s: %s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_COMMITTING_WITH_REQUEST_ARG_ARG_9AA9866F
=
+ "Subscription: something unexpected happened when committing with
request %s: %s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_CLOSING_WITH_REQUEST_ARG_ARG_1E3215D2
=
+ "Subscription: something unexpected happened when closing with
request %s: %s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_SEEKING_WITH_REQUEST_ARG_ARG_97D0AD1C
=
+ "Subscription: something unexpected happened when seeking with
request %s: %s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_UNSUPPORTED_SEEKTYPE_ARG_FOR_TOPIC_ARG_CONSENSUS_SUBSCRIPTION_ONLY_SUPPORTS_SEEKTOBEGINNING_SEEKTOEND_SEEK_TOPICPROGRESS_AND_SEEKAFTER_TOPICPROGRESS_5A3436C3
=
+ "Subscription: unsupported seekType %s for topic %s. Consensus
subscription only supports seekToBeginning, seekToEnd, seek(topicProgress), and
seekAfter(topicProgress).";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_CREATE_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_051923FB
=
+ "Subscription: Failed to create consumer %s in config node, status
is %s.";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_CREATE_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_6152986E
=
+ "Subscription: Failed to create consumer %s in config node,
exception is %s.";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_CLOSE_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_F31E2A0F
=
+ "Subscription: Failed to close consumer %s in config node, status is
%s.";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_CLOSE_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_11845DE5
=
+ "Subscription: Failed to close consumer %s in config node, exception
is %s.";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_SUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_9F933EBA
=
+ "Subscription: Failed to subscribe topics %s for consumer %s in
config node, status is %s.";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_SUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_0B6D1355
=
+ "Subscription: Failed to subscribe topics %s for consumer %s in
config node, exception is %s.";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_UNSUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_74DF0145
=
+ "Subscription: Failed to unsubscribe topics %s for consumer %s in
config node, status is %s.";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_UNSUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_A92043B5
=
+ "Subscription: Failed to unsubscribe topics %s for consumer %s in
config node, exception is %s.";
+
+ // ===================== LEGACY PIPE RECEIVER RESPONSES =====================
+
+ public static final String MESSAGE_PIPE_DATA_TRANSPORT_ERROR_ARG_ECE6A238 =
+ "Pipe data transport error, %s";
+ public static final String
MESSAGE_FAIL_TO_LOAD_PIPEDATA_BECAUSE_ARG_49526D0C =
+ "Fail to load pipeData because %s";
+ public static final String
MESSAGE_FAILED_TO_RESOLVE_LEGACY_PIPE_RECEIVER_FILE_ARG_ARG_9B82CF3F =
+ "Failed to resolve legacy pipe receiver file %s: %s";
+ public static final String
+
MESSAGE_FAILED_TO_APPEND_LEGACY_PIPE_RECEIVER_FILE_ARG_AT_OFFSET_ARG_WITH_ARG_BYTES_ARG_57428A06
=
+ "Failed to append legacy pipe receiver file %s at offset %d with %d
bytes: %s";
}
diff --git
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
index a3949cd283e..d9439de9014 100644
---
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
+++
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
@@ -2535,4 +2535,87 @@ public final class DataNodePipeMessages {
"[%s] 订阅 entry 需要 %d 字节,超过当前队列可用上限 %d 字节(DataNode 预算 %d 字节)。请减小写入
batch/字段大小或增加订阅物化内存。WAL 进度未推进。";
public static final String
LOG_SUBSCRIPTION_FAILED_TO_REPLAY_DELIVERY_BATCH_ARG_FROM_WAL_RETAINING_UNCOMMITTED_STATE_0654EACD
=
"Subscription 无法从 WAL 重放交付批次 {};保留未提交状态";
+
+ // ===================== SUBSCRIPTION RECEIVER RESPONSES
=====================
+
+ public static final String
+
MESSAGE_SUBSCRIPTION_MALFORMED_REQUEST_SLICE_RESEND_THE_COMPLETE_REQUEST_STARTING_FROM_SLICE_INDEX_0_D91FD262
=
+ "Subscription:请求分片格式错误。请从分片索引 0 开始重新发送完整请求。";
+ public static final String
+
MESSAGE_SUBSCRIPTION_INVALID_OR_OUT_OF_ORDER_REQUEST_SLICE_ORDERID_ARG_SLICEINDEX_ARG_SLICECOUNT_ARG_ORIGINBODYSIZE_ARG_BYTES_RESEND_THE_COMPLETE_REQUEST_STARTING_FROM_SLICE_INDEX_0_A08D6D8E
=
+ "Subscription:请求分片无效或顺序错误(orderId %d,sliceIndex %d,sliceCount
%d,originBodySize %d 字节)。请从分片索引 0 开始重新发送完整请求。";
+ public static final String
+
MESSAGE_SUBSCRIPTION_REQUEST_BODY_SIZE_ARG_BYTES_EXCEEDS_THE_RECEIVER_LIMIT_OF_ARG_BYTES_SPLIT_THE_OPERATION_INTO_SMALLER_REQUESTS_C9513027
=
+ "Subscription:请求体大小为 %d 字节,超过接收端上限 %d 字节。请将操作拆分为更小的请求。";
+
+ public static final String
MESSAGE_MISSING_CONSUMER_CONFIG_PLEASE_HANDSHAKE_FIRST_CE588329 =
+ "缺少 consumer 配置,请先完成 handshake。";
+ public static final String
MESSAGE_UNKNOWN_PIPESUBSCRIBEREQUESTTYPE_ARG_7EBB93F8 =
+ "未知的 PipeSubscribeRequestType:%s。";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_HANDSHAKING_WITH_REQUEST_ARG_ARG_C1BC903B
=
+ "Subscription:处理 handshake 请求 %s 时发生意外错误:%s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_HEARTBEAT_WITH_REQUEST_ARG_ARG_790B328F
=
+ "Subscription:处理 heartbeat 请求 %s 时发生意外错误:%s";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_FETCH_ENDPOINTS_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_267904CB
=
+ "Subscription:在 ConfigNode 中为 consumer %s 获取节点端点失败,异常:%s。";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_SUBSCRIBING_WITH_REQUEST_ARG_ARG_084648A4
=
+ "Subscription:处理 subscribe 请求 %s 时发生意外错误:%s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_UNSUBSCRIBING_WITH_REQUEST_ARG_ARG_1C15D8EC
=
+ "Subscription:处理 unsubscribe 请求 %s 时发生意外错误:%s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_POLLING_WITH_REQUEST_ARG_ARG_DE2CF98B
=
+ "Subscription:处理 poll 请求 %s 时发生意外错误:%s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_COMMITTING_WITH_REQUEST_ARG_ARG_9AA9866F
=
+ "Subscription:处理 commit 请求 %s 时发生意外错误:%s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_CLOSING_WITH_REQUEST_ARG_ARG_1E3215D2
=
+ "Subscription:处理 close 请求 %s 时发生意外错误:%s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_SEEKING_WITH_REQUEST_ARG_ARG_97D0AD1C
=
+ "Subscription:处理 seek 请求 %s 时发生意外错误:%s";
+ public static final String
+
MESSAGE_SUBSCRIPTION_UNSUPPORTED_SEEKTYPE_ARG_FOR_TOPIC_ARG_CONSENSUS_SUBSCRIPTION_ONLY_SUPPORTS_SEEKTOBEGINNING_SEEKTOEND_SEEK_TOPICPROGRESS_AND_SEEKAFTER_TOPICPROGRESS_5A3436C3
=
+ "Subscription:seekType %s 不支持用于 topic %s。Consensus subscription 仅支持
seekToBeginning、seekToEnd、seek(topicProgress) 和 seekAfter(topicProgress)。";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_CREATE_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_051923FB
=
+ "Subscription:在 ConfigNode 中创建 consumer %s 失败,状态为 %s。";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_CREATE_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_6152986E
=
+ "Subscription:在 ConfigNode 中创建 consumer %s 失败,异常为 %s。";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_CLOSE_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_F31E2A0F
=
+ "Subscription:在 ConfigNode 中关闭 consumer %s 失败,状态为 %s。";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_CLOSE_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_11845DE5
=
+ "Subscription:在 ConfigNode 中关闭 consumer %s 失败,异常为 %s。";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_SUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_9F933EBA
=
+ "Subscription:在 ConfigNode 中订阅主题 %s 失败,consumer 为 %s,状态为 %s。";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_SUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_0B6D1355
=
+ "Subscription:在 ConfigNode 中订阅主题 %s 失败,consumer 为 %s,异常为 %s。";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_UNSUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_74DF0145
=
+ "Subscription:在 ConfigNode 中取消订阅主题 %s 失败,consumer 为 %s,状态为 %s。";
+ public static final String
+
EXCEPTION_SUBSCRIPTION_FAILED_TO_UNSUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_A92043B5
=
+ "Subscription:在 ConfigNode 中取消订阅主题 %s 失败,consumer 为 %s,异常为 %s。";
+
+ // ===================== LEGACY PIPE RECEIVER RESPONSES =====================
+
+ public static final String MESSAGE_PIPE_DATA_TRANSPORT_ERROR_ARG_ECE6A238 =
+ "Pipe 数据传输错误:%s";
+ public static final String
MESSAGE_FAIL_TO_LOAD_PIPEDATA_BECAUSE_ARG_49526D0C =
+ "加载 pipeData 失败,原因:%s";
+ public static final String
MESSAGE_FAILED_TO_RESOLVE_LEGACY_PIPE_RECEIVER_FILE_ARG_ARG_9B82CF3F =
+ "解析 legacy pipe receiver 文件 %s 失败:%s";
+ public static final String
+
MESSAGE_FAILED_TO_APPEND_LEGACY_PIPE_RECEIVER_FILE_ARG_AT_OFFSET_ARG_WITH_ARG_BYTES_ARG_57428A06
=
+ "追加 legacy pipe receiver 文件 %s 失败,偏移量 %d,写入 %d 字节:%s";
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgent.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgent.java
index 7dbf2db122b..3bad4b45398 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgent.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgent.java
@@ -164,9 +164,11 @@ public class IoTDBLegacyPipeReceiverAgent {
handleTsFilePipeData(tsFilePipeData, fileDir);
}
} catch (final IOException e) {
- LOGGER.error(DataNodePipeMessages.PIPE_DATA_TRANSPORT_ERROR,
e.getMessage());
- return RpcUtils.getStatus(
- TSStatusCode.PIPESERVER_ERROR, "Pipe data transport error, " +
e.getMessage());
+ final String errorMessage =
+ String.format(
+
DataNodePipeMessages.MESSAGE_PIPE_DATA_TRANSPORT_ERROR_ARG_ECE6A238,
e.getMessage());
+ LOGGER.error(errorMessage, e);
+ return RpcUtils.getStatus(TSStatusCode.PIPESERVER_ERROR, errorMessage);
}
// step3. load PipeData
@@ -181,9 +183,12 @@ public class IoTDBLegacyPipeReceiverAgent {
DataNodePipeMessages.LOAD_PIPEDATA_WITH_SERIALIZE_NUMBER_SUCCESSFULLY,
pipeData.getSerialNumber());
} catch (final PipeException e) {
- LOGGER.error(DataNodePipeMessages.FAIL_TO_LOAD_PIPEDATA_BECAUSE,
e.getMessage());
- return RpcUtils.getStatus(
- TSStatusCode.PIPESERVER_ERROR, "Fail to load pipeData because " +
e.getMessage());
+ final String errorMessage =
+ String.format(
+
DataNodePipeMessages.MESSAGE_FAIL_TO_LOAD_PIPEDATA_BECAUSE_ARG_49526D0C,
+ e.getMessage());
+ LOGGER.error(errorMessage, e);
+ return RpcUtils.getStatus(TSStatusCode.PIPESERVER_ERROR, errorMessage);
}
return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS, "");
@@ -273,8 +278,14 @@ public class IoTDBLegacyPipeReceiverAgent {
fileWithoutPatch = resolveFileInFileDataDir(fileDir, fileName);
file = resolveFileInFileDataDir(fileDir, fileName + PATCH_SUFFIX);
} catch (final IOException e) {
- LOGGER.warn(e.getMessage());
- return RpcUtils.getStatus(TSStatusCode.SYNC_FILE_ERROR, e.getMessage());
+ final String errorMessage =
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_FAILED_TO_RESOLVE_LEGACY_PIPE_RECEIVER_FILE_ARG_ARG_9B82CF3F,
+ fileName,
+ e.getMessage());
+ LOGGER.warn(errorMessage, e);
+ return RpcUtils.getStatus(TSStatusCode.SYNC_FILE_ERROR, errorMessage);
}
// step2. check startIndex
@@ -284,8 +295,8 @@ public class IoTDBLegacyPipeReceiverAgent {
}
// step3. append file
+ final int length = buff.remaining();
try (final RandomAccessFile randomAccessFile = new RandomAccessFile(file,
"rw")) {
- final int length = buff.remaining();
randomAccessFile.seek(startIndex);
final byte[] byteArray = new byte[length];
buff.get(byteArray);
@@ -294,8 +305,16 @@ public class IoTDBLegacyPipeReceiverAgent {
LOGGER.debug(
DataNodePipeMessages.SYNC_START_AT_TO_IS_DONE, fileName, startIndex,
startIndex + length);
} catch (final IOException e) {
- LOGGER.error(e.getMessage());
- return RpcUtils.getStatus(TSStatusCode.SYNC_FILE_ERROR, e.getMessage());
+ final String errorMessage =
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_FAILED_TO_APPEND_LEGACY_PIPE_RECEIVER_FILE_ARG_AT_OFFSET_ARG_WITH_ARG_BYTES_ARG_57428A06,
+ fileName,
+ startIndex,
+ length,
+ e.getMessage());
+ LOGGER.error(errorMessage, e);
+ return RpcUtils.getStatus(TSStatusCode.SYNC_FILE_ERROR, errorMessage);
}
return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS, "");
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1.java
index 7e9b94b508e..cd53d9376ab 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1.java
@@ -122,7 +122,7 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
new TPipeSubscribeResp(
RpcUtils.getStatus(
TSStatusCode.SUBSCRIPTION_MISSING_CONSUMER,
- "Missing consumer config, please handshake first."),
+
DataNodePipeMessages.MESSAGE_MISSING_CONSUMER_CONFIG_PLEASE_HANDSHAKE_FIRST_CE588329),
PipeSubscribeResponseVersion.VERSION_1.getVersion(),
PipeSubscribeResponseType.ACK.getType());
@@ -141,12 +141,6 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
PipeSubscribeResponseVersion.VERSION_1.getVersion(),
PipeSubscribeResponseType.ACK.getType());
- private static final TPipeSubscribeResp SUBSCRIPTION_SLICE_ERROR_RESP =
- new TPipeSubscribeResp(
- new TSStatus(TSStatusCode.SUBSCRIPTION_TYPE_ERROR.getStatusCode()),
- PipeSubscribeResponseVersion.VERSION_1.getVersion(),
- PipeSubscribeResponseType.ACK.getType());
-
private final ThreadLocal<ConsumerConfig> consumerConfigThreadLocal = new
ThreadLocal<>();
private final ThreadLocal<PollTimer> pollTimerThreadLocal = new
ThreadLocal<>();
private volatile String authenticatedUsername;
@@ -155,8 +149,9 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
private volatile boolean consumerFenced;
private volatile long lastActivityTimeMs = System.currentTimeMillis();
private final AtomicLong inFlightRequestCount = new AtomicLong(0);
+ private final int maxReassembledRequestBodySize =
getMaxReassembledRequestBodySize();
private final PipeSubscribeSliceReqHandler sliceReqHandler =
- new PipeSubscribeSliceReqHandler(getMaxReassembledRequestBodySize());
+ new PipeSubscribeSliceReqHandler(maxReassembledRequestBodySize);
private long consumerStateVersion;
private static final String SQL_DIALECT_TABLE_VALUE = "table";
@@ -203,7 +198,9 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
final TSStatus status =
RpcUtils.getStatus(
TSStatusCode.SUBSCRIPTION_TYPE_ERROR,
- String.format("Unknown PipeSubscribeRequestType %s.", reqType));
+ String.format(
+
DataNodePipeMessages.MESSAGE_UNKNOWN_PIPESUBSCRIBEREQUESTTYPE_ARG_7EBB93F8,
+ reqType));
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_UNKNOWN_REQUEST_TYPE,
status);
return new TPipeSubscribeResp(
status,
@@ -302,10 +299,27 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
sliceReq = PipeSubscribeSliceReq.fromTPipeSubscribeReq(thriftReq);
} catch (final RuntimeException e) {
sliceReqHandler.clear();
- return SUBSCRIPTION_SLICE_ERROR_RESP;
+ return createSliceErrorResp(
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_MALFORMED_REQUEST_SLICE_RESEND_THE_COMPLETE_REQUEST_STARTING_FROM_SLICE_INDEX_0_D91FD262);
}
if (!sliceReqHandler.receiveSlice(sliceReq)) {
- return SUBSCRIPTION_SLICE_ERROR_RESP;
+ if (sliceReq.getOriginBodySize() > maxReassembledRequestBodySize) {
+ return createSliceErrorResp(
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_REQUEST_BODY_SIZE_ARG_BYTES_EXCEEDS_THE_RECEIVER_LIMIT_OF_ARG_BYTES_SPLIT_THE_OPERATION_INTO_SMALLER_REQUESTS_C9513027,
+ sliceReq.getOriginBodySize(),
+ maxReassembledRequestBodySize));
+ }
+ return createSliceErrorResp(
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_INVALID_OR_OUT_OF_ORDER_REQUEST_SLICE_ORDERID_ARG_SLICEINDEX_ARG_SLICECOUNT_ARG_ORIGINBODYSIZE_ARG_BYTES_RESEND_THE_COMPLETE_REQUEST_STARTING_FROM_SLICE_INDEX_0_A08D6D8E,
+ sliceReq.getOrderId(),
+ sliceReq.getSliceIndex(),
+ sliceReq.getSliceCount(),
+ sliceReq.getOriginBodySize()));
}
return sliceReqHandler
.makeReqIfComplete()
@@ -313,6 +327,13 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
.orElse(SUBSCRIPTION_SLICE_ACK_RESP);
}
+ private static TPipeSubscribeResp createSliceErrorResp(final String message)
{
+ return new TPipeSubscribeResp(
+ RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_TYPE_ERROR, message),
+ PipeSubscribeResponseVersion.VERSION_1.getVersion(),
+ PipeSubscribeResponseType.ACK.getType());
+ }
+
private static int getMaxReassembledRequestBodySize() {
final long maxFrameSize =
IoTDBDescriptor.getInstance().getConfig().getThriftMaxFrameSize();
final long desiredSize =
@@ -379,8 +400,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_HANDSHAKING,
req, e);
final String exceptionMessage =
String.format(
- "Subscription: something unexpected happened when handshaking
with request %s: %s",
- req, e);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_HANDSHAKING_WITH_REQUEST_ARG_ARG_C1BC903B,
+ req,
+ e);
return PipeSubscribeHandshakeResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_HANDSHAKE_ERROR,
exceptionMessage),
-1,
@@ -450,8 +473,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_HEARTBEAT, req,
e);
final String exceptionMessage =
String.format(
- "Subscription: something unexpected happened when heartbeat with
request %s: %s",
- req, e);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_HEARTBEAT_WITH_REQUEST_ARG_ARG_790B328F,
+ req,
+ e);
return PipeSubscribeHeartbeatResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_HEARTBEAT_ERROR,
exceptionMessage));
}
@@ -534,8 +559,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
e);
final String exceptionMessage =
String.format(
- "Subscription: Failed to fetch endpoints for consumer %s in
config node, exception is %s.",
- consumerConfig, e);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_FETCH_ENDPOINTS_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_267904CB,
+ consumerConfig,
+ e);
throw new SubscriptionException(exceptionMessage);
}
@@ -570,8 +597,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_SUBSCRIBING,
req, e);
final String exceptionMessage =
String.format(
- "Subscription: something unexpected happened when subscribing
with request %s: %s",
- req, e);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_SUBSCRIBING_WITH_REQUEST_ARG_ARG_084648A4,
+ req,
+ e);
return PipeSubscribeSubscribeResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_SUBSCRIBE_ERROR,
exceptionMessage));
}
@@ -626,8 +655,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_UNSUBSCRIBING,
req, e);
final String exceptionMessage =
String.format(
- "Subscription: something unexpected happened when unsubscribing
with request %s: %s",
- req, e);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_UNSUBSCRIBING_WITH_REQUEST_ARG_ARG_1C15D8EC,
+ req,
+ e);
return PipeSubscribeUnsubscribeResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_UNSUBSCRIBE_ERROR,
exceptionMessage));
}
@@ -670,8 +701,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_POLLING, req, e);
final String exceptionMessage =
String.format(
- "Subscription: something unexpected happened when polling with
request %s: %s",
- req, e);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_POLLING_WITH_REQUEST_ARG_ARG_DE2CF98B,
+ req,
+ e);
return PipeSubscribePollResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_POLL_ERROR,
exceptionMessage),
Collections.emptyList());
@@ -956,8 +989,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_COMMITTING, req,
e);
final String exceptionMessage =
String.format(
- "Subscription: something unexpected happened when committing
with request %s: %s",
- req, e);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_COMMITTING_WITH_REQUEST_ARG_ARG_9AA9866F,
+ req,
+ e);
return PipeSubscribeCommitResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_COMMIT_ERROR,
exceptionMessage));
}
@@ -1137,8 +1172,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_CLOSING, req, e);
final String exceptionMessage =
String.format(
- "Subscription: something unexpected happened when closing with
request %s: %s",
- req, e);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_CLOSING_WITH_REQUEST_ARG_ARG_1E3215D2,
+ req,
+ e);
return PipeSubscribeCloseResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_CLOSE_ERROR,
exceptionMessage));
}
@@ -1172,8 +1209,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
e);
final String exceptionMessage =
String.format(
- "Subscription: something unexpected happened when seeking with
request %s: %s",
- req, e);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_SEEKING_WITH_REQUEST_ARG_ARG_97D0AD1C,
+ req,
+ e);
return PipeSubscribeSeekResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_SEEK_ERROR,
exceptionMessage));
}
@@ -1231,10 +1270,10 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
} else {
final String errorMessage =
String.format(
- "Subscription: unsupported seekType %s for topic %s. "
- + "Consensus subscription only supports seekToBeginning,
seekToEnd, "
- + "seek(topicProgress), and seekAfter(topicProgress).",
- seekType, topicName);
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_UNSUPPORTED_SEEKTYPE_ARG_FOR_TOPIC_ARG_CONSENSUS_SUBSCRIPTION_ONLY_SUPPORTS_SEEKTOBEGINNING_SEEKTOEND_SEEK_TOPICPROGRESS_AND_SEEKAFTER_TOPICPROGRESS_5A3436C3,
+ seekType,
+ topicName);
LOGGER.warn(errorMessage);
return PipeSubscribeSeekResp.toTPipeSubscribeResp(
RpcUtils.getStatus(TSStatusCode.SUBSCRIPTION_SEEK_ERROR,
errorMessage));
@@ -1327,16 +1366,20 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
consumerConfig);
final String exceptionMessage =
String.format(
- "Subscription: Failed to create consumer %s in config node,
status is %s.",
- consumerConfig, tsStatus);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_CREATE_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_051923FB,
+ consumerConfig,
+ tsStatus);
throw new SubscriptionException(exceptionMessage);
}
} catch (final ClientManagerException | TException e) {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_CREATING_CONSUMER,
consumerConfig, e);
final String exceptionMessage =
String.format(
- "Subscription: Failed to create consumer %s in config node,
exception is %s.",
- consumerConfig, e);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_CREATE_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_6152986E,
+ consumerConfig,
+ e);
throw new SubscriptionException(exceptionMessage);
}
}
@@ -1357,16 +1400,20 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
consumerConfig);
final String exceptionMessage =
String.format(
- "Subscription: Failed to close consumer %s in config node,
status is %s.",
- consumerConfig, tsStatus);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_CLOSE_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_F31E2A0F,
+ consumerConfig,
+ tsStatus);
throw new SubscriptionException(exceptionMessage);
}
} catch (final ClientManagerException | TException e) {
LOGGER.warn(DataNodeMiscMessages.SUBSCRIPTION_EXCEPTION_CLOSING_CONSUMER,
consumerConfig, e);
final String exceptionMessage =
String.format(
- "Subscription: Failed to close consumer %s in config node,
exception is %s.",
- consumerConfig, e);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_CLOSE_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_11845DE5,
+ consumerConfig,
+ e);
throw new SubscriptionException(exceptionMessage);
}
@@ -1394,8 +1441,11 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
consumerConfig);
final String exceptionMessage =
String.format(
- "Subscription: Failed to subscribe topics %s for consumer %s
in config node, status is %s.",
- topicNames, consumerConfig, tsStatus);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_SUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_9F933EBA,
+ topicNames,
+ consumerConfig,
+ tsStatus);
if (TSStatusCode.SUBSCRIPTION_PIPE_TIMEOUT_ERROR.getStatusCode() ==
tsStatus.getCode()) {
throw new SubscriptionPipeTimeoutException(exceptionMessage);
} else {
@@ -1411,8 +1461,11 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
e);
final String exceptionMessage =
String.format(
- "Subscription: Failed to subscribe topics %s for consumer %s in
config node, exception is %s.",
- topicNames, consumerConfig, e);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_SUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_0B6D1355,
+ topicNames,
+ consumerConfig,
+ e);
throw new SubscriptionException(exceptionMessage);
}
}
@@ -1438,8 +1491,11 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
consumerConfig);
final String exceptionMessage =
String.format(
- "Subscription: Failed to unsubscribe topics %s for consumer %s
in config node, status is %s.",
- topicNames, consumerConfig, tsStatus);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_UNSUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_STATUS_IS_ARG_74DF0145,
+ topicNames,
+ consumerConfig,
+ tsStatus);
if (TSStatusCode.SUBSCRIPTION_PIPE_TIMEOUT_ERROR.getStatusCode() ==
tsStatus.getCode()) {
throw new SubscriptionPipeTimeoutException(exceptionMessage);
} else {
@@ -1455,8 +1511,11 @@ public class SubscriptionReceiverV1 implements
SubscriptionReceiver {
e);
final String exceptionMessage =
String.format(
- "Subscription: Failed to unsubscribe topics %s for consumer %s
in config node, exception is %s.",
- topicNames, consumerConfig, e);
+ DataNodePipeMessages
+
.EXCEPTION_SUBSCRIPTION_FAILED_TO_UNSUBSCRIBE_TOPICS_ARG_FOR_CONSUMER_ARG_IN_CONFIG_NODE_EXCEPTION_IS_ARG_A92043B5,
+ topicNames,
+ consumerConfig,
+ e);
throw new SubscriptionException(exceptionMessage);
}
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgentTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgentTest.java
index 5ce4df74f7f..111350d7607 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgentTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgentTest.java
@@ -21,15 +21,21 @@ package org.apache.iotdb.db.pipe.receiver.protocol.legacy;
import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.commons.conf.CommonDescriptor;
+import org.apache.iotdb.commons.i18n.PipeMessages;
+import org.apache.iotdb.db.i18n.DataNodePipeMessages;
import org.apache.iotdb.db.pipe.sink.payload.legacy.TsFilePipeData;
import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.service.rpc.thrift.TSyncIdentityInfo;
import org.apache.iotdb.service.rpc.thrift.TSyncTransportMetaInfo;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.ByteBuffer;
@@ -85,7 +91,15 @@ public class IoTDBLegacyPipeReceiverAgentTest {
ByteBuffer.wrap("pwned".getBytes(StandardCharsets.UTF_8)));
Assert.assertEquals(TSStatusCode.SYNC_FILE_ERROR.getStatusCode(),
status.getCode());
- Assert.assertTrue(status.getMessage().contains("Illegal fileName"));
+ Assert.assertTrue(
+ status
+ .getMessage()
+ .startsWith(
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_FAILED_TO_RESOLVE_LEGACY_PIPE_RECEIVER_FILE_ARG_ARG_9B82CF3F,
+ traversal,
+
String.format(PipeMessages.ILLEGAL_FILENAME_PATH_TRAVERSAL, traversal))));
Assert.assertFalse(Files.exists(syncDir.resolve("pwned.patch")));
}
@@ -102,6 +116,40 @@ public class IoTDBLegacyPipeReceiverAgentTest {
Assert.assertArrayEquals(payload, Files.readAllBytes(patchFile));
}
+ @Test
+ public void testTransportFileWriteFailureIncludesContextAndLogsException()
throws Exception {
+ final String fileName = "1-2-3-4.tsfile";
+ final byte[] payload = "iotdb".getBytes(StandardCharsets.UTF_8);
+ Files.createDirectory(getFileDataDir().resolve(fileName + ".patch"));
+ final Logger logger = (Logger)
LoggerFactory.getLogger(IoTDBLegacyPipeReceiverAgent.class);
+ final ListAppender<ILoggingEvent> appender = new ListAppender<>();
+ appender.start();
+ logger.addAppender(appender);
+ try {
+ final TSStatus status =
+ agent.transportFile(new TSyncTransportMetaInfo(fileName, 0),
ByteBuffer.wrap(payload));
+
+ Assert.assertEquals(TSStatusCode.SYNC_FILE_ERROR.getStatusCode(),
status.getCode());
+ Assert.assertTrue(
+ status
+ .getMessage()
+ .startsWith(
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_FAILED_TO_APPEND_LEGACY_PIPE_RECEIVER_FILE_ARG_AT_OFFSET_ARG_WITH_ARG_BYTES_ARG_57428A06,
+ fileName,
+ 0,
+ payload.length,
+ "")));
+ final ILoggingEvent failure = appender.list.get(appender.list.size() -
1);
+ Assert.assertEquals(status.getMessage(), failure.getFormattedMessage());
+ Assert.assertNotNull(failure.getThrowableProxy());
+ } finally {
+ logger.detachAppender(appender);
+ appender.stop();
+ }
+ }
+
@Test
public void testTransportPipeDataRejectsPathTraversalTsFileName() throws
Exception {
final String traversal = ".." + File.separator + "evil.tsfile";
@@ -110,7 +158,13 @@ public class IoTDBLegacyPipeReceiverAgentTest {
agent.transportPipeData(ByteBuffer.wrap(new TsFilePipeData("",
traversal, -1).serialize()));
Assert.assertEquals(TSStatusCode.PIPESERVER_ERROR.getStatusCode(),
status.getCode());
- Assert.assertTrue(status.getMessage().contains("Illegal fileName"));
+ Assert.assertTrue(
+ status
+ .getMessage()
+ .startsWith(
+ String.format(
+
DataNodePipeMessages.MESSAGE_PIPE_DATA_TRANSPORT_ERROR_ARG_ECE6A238,
+
String.format(PipeMessages.ILLEGAL_FILENAME_PATH_TRAVERSAL, traversal))));
}
private Path getFileDataDir() {
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1Test.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1Test.java
index a8c7dc7841f..40482fe1b20 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1Test.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/receiver/SubscriptionReceiverV1Test.java
@@ -19,17 +19,22 @@
package org.apache.iotdb.db.subscription.receiver;
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.commons.subscription.config.SubscriptionConfig;
import org.apache.iotdb.commons.subscription.meta.consumer.ConsumerGroupMeta;
import org.apache.iotdb.commons.subscription.meta.consumer.ConsumerMeta;
import org.apache.iotdb.commons.subscription.meta.topic.TopicMeta;
+import org.apache.iotdb.db.i18n.DataNodePipeMessages;
import org.apache.iotdb.db.subscription.agent.SubscriptionAgent;
import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.rpc.subscription.config.ConsumerConfig;
import org.apache.iotdb.rpc.subscription.config.ConsumerConstant;
import org.apache.iotdb.rpc.subscription.config.TopicConstant;
import
org.apache.iotdb.rpc.subscription.payload.poll.SubscriptionCommitContext;
+import org.apache.iotdb.rpc.subscription.payload.request.PipeSubscribeCloseReq;
import
org.apache.iotdb.rpc.subscription.payload.request.PipeSubscribeHandshakeReq;
+import
org.apache.iotdb.rpc.subscription.payload.request.PipeSubscribeRequestType;
+import
org.apache.iotdb.rpc.subscription.payload.request.PipeSubscribeRequestVersion;
import
org.apache.iotdb.rpc.subscription.payload.request.PipeSubscribeSliceReqBuilder;
import
org.apache.iotdb.rpc.subscription.payload.request.SubscriptionHeartbeatReq;
import org.apache.iotdb.service.rpc.thrift.TPipeSubscribeReq;
@@ -40,6 +45,7 @@ import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -219,9 +225,57 @@ public class SubscriptionReceiverV1Test {
public void testNeverHandshakenConsumerStillReturnsMissingConsumerStatus() {
final SubscriptionReceiverV1 receiver = new SubscriptionReceiverV1();
+ final TSStatus status =
receiver.handle(SubscriptionHeartbeatReq.toThriftReq()).getStatus();
+
Assert.assertEquals(
- TSStatusCode.SUBSCRIPTION_MISSING_CONSUMER.getStatusCode(),
-
receiver.handle(SubscriptionHeartbeatReq.toThriftReq()).getStatus().getCode());
+ TSStatusCode.SUBSCRIPTION_MISSING_CONSUMER.getStatusCode(),
status.getCode());
+ Assert.assertEquals(
+
DataNodePipeMessages.MESSAGE_MISSING_CONSUMER_CONFIG_PLEASE_HANDSHAKE_FIRST_CE588329,
+ status.getMessage());
+ }
+
+ @Test
+ public void testUnknownRequestTypeReturnsLocalizedMessageTemplate() {
+ final SubscriptionReceiverV1 receiver = new SubscriptionReceiverV1();
+ final short requestType = Short.MAX_VALUE;
+
+ final TSStatus status =
+ receiver.handle(new
TPipeSubscribeReq().setType(requestType)).getStatus();
+
+ Assert.assertEquals(TSStatusCode.SUBSCRIPTION_TYPE_ERROR.getStatusCode(),
status.getCode());
+ Assert.assertEquals(
+ String.format(
+
DataNodePipeMessages.MESSAGE_UNKNOWN_PIPESUBSCRIBEREQUESTTYPE_ARG_7EBB93F8,
+ requestType),
+ status.getMessage());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testCloseFailurePreservesRequestAndExceptionInResponse() throws
Exception {
+ final RuntimeException failure = new RuntimeException("expected cleanup
failure");
+ final SubscriptionReceiverV1 receiver =
+ new SubscriptionReceiverV1() {
+ @Override
+ void closeConsumer(final ConsumerConfig consumerConfig) {
+ throw failure;
+ }
+ };
+ final ThreadLocal<ConsumerConfig> consumerConfigThreadLocal =
+ (ThreadLocal<ConsumerConfig>) getField(receiver,
"consumerConfigThreadLocal");
+ consumerConfigThreadLocal.set(createConsumerConfig(1_000L));
+ final PipeSubscribeCloseReq req =
PipeSubscribeCloseReq.toTPipeSubscribeReq();
+
+ final TSStatus status = receiver.handle(req).getStatus();
+
+ Assert.assertEquals(TSStatusCode.SUBSCRIPTION_CLOSE_ERROR.getStatusCode(),
status.getCode());
+ Assert.assertEquals(
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_SOMETHING_UNEXPECTED_HAPPENED_WHEN_CLOSING_WITH_REQUEST_ARG_ARG_1E3215D2,
+ req,
+ failure),
+ status.getMessage());
}
@Test
@@ -247,6 +301,83 @@ public class SubscriptionReceiverV1Test {
TSStatusCode.SUBSCRIPTION_MISSING_CONSUMER.getStatusCode(),
lastResp.getStatus().getCode());
}
+ @Test
+ public void testMalformedSliceReturnsMessageAndAllowsCompleteRequestRetry()
throws Exception {
+ final SubscriptionReceiverV1 receiver = new SubscriptionReceiverV1();
+ final TPipeSubscribeReq heartbeatReq =
+ SubscriptionHeartbeatReq.toThriftReq(Collections.emptyList());
+ final int bodySizeLimit = 2;
+ final int sliceCount =
PipeSubscribeSliceReqBuilder.getSliceCount(heartbeatReq, bodySizeLimit);
+
+ receiver.handle(
+ PipeSubscribeSliceReqBuilder.buildSliceReq(heartbeatReq, 1, 0,
sliceCount, bodySizeLimit));
+ final TSStatus status =
+ receiver
+ .handle(
+ new TPipeSubscribeReq()
+
.setVersion(PipeSubscribeRequestVersion.VERSION_1.getVersion())
+ .setType(PipeSubscribeRequestType.SLICE.getType())
+ .setBody(ByteBuffer.allocate(1)))
+ .getStatus();
+
+ Assert.assertEquals(TSStatusCode.SUBSCRIPTION_TYPE_ERROR.getStatusCode(),
status.getCode());
+ Assert.assertEquals(
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_MALFORMED_REQUEST_SLICE_RESEND_THE_COMPLETE_REQUEST_STARTING_FROM_SLICE_INDEX_0_D91FD262,
+ status.getMessage());
+ Assert.assertEquals(
+ TSStatusCode.SUBSCRIPTION_TYPE_ERROR.getStatusCode(),
+ receiver
+ .handle(
+ PipeSubscribeSliceReqBuilder.buildSliceReq(
+ heartbeatReq, 1, 1, sliceCount, bodySizeLimit))
+ .getStatus()
+ .getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ receiver
+ .handle(
+ PipeSubscribeSliceReqBuilder.buildSliceReq(
+ heartbeatReq, 2, 0, sliceCount, bodySizeLimit))
+ .getStatus()
+ .getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUBSCRIPTION_MISSING_CONSUMER.getStatusCode(),
+ receiver
+ .handle(
+ PipeSubscribeSliceReqBuilder.buildSliceReq(
+ heartbeatReq, 2, 1, sliceCount, bodySizeLimit))
+ .getStatus()
+ .getCode());
+ }
+
+ @Test
+ public void testOversizedSliceReturnsRequestedSizeAndReceiverLimit() throws
Exception {
+ final SubscriptionReceiverV1 receiver = new SubscriptionReceiverV1();
+ final int maxBodySize = (int) getField(receiver,
"maxReassembledRequestBodySize");
+ final int requestedBodySize = maxBodySize + 1;
+ final TPipeSubscribeReq heartbeatReq =
+ SubscriptionHeartbeatReq.toThriftReq(Collections.emptyList());
+ final int bodySizeLimit = 2;
+ final int sliceCount =
PipeSubscribeSliceReqBuilder.getSliceCount(heartbeatReq, bodySizeLimit);
+ final TPipeSubscribeReq sliceReq =
+ PipeSubscribeSliceReqBuilder.buildSliceReq(heartbeatReq, 1, 0,
sliceCount, bodySizeLimit);
+ // Advertise an oversized body without allocating it. The size follows the
order ID, version,
+ // and type in the slice header.
+ sliceReq.body.putInt(Integer.BYTES + Byte.BYTES + Short.BYTES,
requestedBodySize);
+
+ final TSStatus status = receiver.handle(sliceReq).getStatus();
+
+ Assert.assertEquals(TSStatusCode.SUBSCRIPTION_TYPE_ERROR.getStatusCode(),
status.getCode());
+ Assert.assertEquals(
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_REQUEST_BODY_SIZE_ARG_BYTES_EXCEEDS_THE_RECEIVER_LIMIT_OF_ARG_BYTES_SPLIT_THE_OPERATION_INTO_SMALLER_REQUESTS_C9513027,
+ requestedBodySize,
+ maxBodySize),
+ status.getMessage());
+ }
+
@Test
public void testNonSliceRequestClearsIncompleteSlices() throws Exception {
final SubscriptionReceiverV1 receiver = new SubscriptionReceiverV1();
@@ -266,14 +397,22 @@ public class SubscriptionReceiverV1Test {
Assert.assertEquals(
TSStatusCode.SUBSCRIPTION_MISSING_CONSUMER.getStatusCode(),
receiver.handle(SubscriptionHeartbeatReq.toThriftReq()).getStatus().getCode());
- Assert.assertEquals(
- TSStatusCode.SUBSCRIPTION_TYPE_ERROR.getStatusCode(),
+ final TSStatus status =
receiver
.handle(
PipeSubscribeSliceReqBuilder.buildSliceReq(
heartbeatReq, 1, 1, sliceCount, bodySizeLimit))
- .getStatus()
- .getCode());
+ .getStatus();
+ Assert.assertEquals(TSStatusCode.SUBSCRIPTION_TYPE_ERROR.getStatusCode(),
status.getCode());
+ Assert.assertEquals(
+ String.format(
+ DataNodePipeMessages
+
.MESSAGE_SUBSCRIPTION_INVALID_OR_OUT_OF_ORDER_REQUEST_SLICE_ORDERID_ARG_SLICEINDEX_ARG_SLICECOUNT_ARG_ORIGINBODYSIZE_ARG_BYTES_RESEND_THE_COMPLETE_REQUEST_STARTING_FROM_SLICE_INDEX_0_A08D6D8E,
+ 1,
+ 1,
+ sliceCount,
+ heartbeatReq.body.remaining()),
+ status.getMessage());
}
@Test