Skip to content

Commit ccbc71d

Browse files
authored
Fix misleading shutdown interruption warnings (#18939)
1 parent ddb1741 commit ccbc71d

15 files changed

Lines changed: 1164 additions & 58 deletions

File tree

‎iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,7 @@ public final class DataNodeQueryMessages {
12631263
"Interrupted when dispatching read async";
12641264
public static final String INTERRUPTED_WHEN_DISPATCHING_WRITE_ASYNC =
12651265
"Interrupted when dispatching write async";
1266+
public static final String MESSAGE_INTERRUPTED_ERRORS_ARG_876F1B4B = "Interrupted errors: %s";
12661267
public static final String DESERIALIZE_CONSENSUSGROUPID_FAILED =
12671268
"Deserialize ConsensusGroupId failed. ";
12681269
public static final String CAN_T_CONNECT_TO_NODE =
@@ -3476,6 +3477,7 @@ private DataNodeQueryMessages() {}
34763477
"already being cancelled";
34773478
public static final String DRIVER_TASK_ABORTED_BY_INTERNAL_ERROR_SCHEDULED =
34783479
"internal error scheduled";
3480+
public static final String EXCEPTION_SCHEDULER_SHUTDOWN_323938F5 = "scheduler shutdown";
34793481
public static final String DRIVER_TASK_ABORTED_BY_MEMORY_NOT_ENOUGH =
34803482
"Memory is not enough to execute the query task.";
34813483
public static final String ROOT_FRAGMENT_INSTANCE_PLACEMENT_ERROR_FMT =

‎iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,7 @@ public final class DataNodeQueryMessages {
12431243
"异步分发读取操作时被中断";
12441244
public static final String INTERRUPTED_WHEN_DISPATCHING_WRITE_ASYNC =
12451245
"异步分发写入操作时被中断";
1246+
public static final String MESSAGE_INTERRUPTED_ERRORS_ARG_876F1B4B = "中断错误:%s";
12461247
public static final String DESERIALIZE_CONSENSUSGROUPID_FAILED =
12471248
"反序列化 ConsensusGroupId 失败。";
12481249
public static final String CAN_T_CONNECT_TO_NODE =
@@ -4127,6 +4128,7 @@ private DataNodeQueryMessages() {}
41274128
"已经在取消中";
41284129
public static final String DRIVER_TASK_ABORTED_BY_INTERNAL_ERROR_SCHEDULED =
41294130
"内部错误调度";
4131+
public static final String EXCEPTION_SCHEDULER_SHUTDOWN_323938F5 = "调度器关闭";
41304132
public static final String DRIVER_TASK_ABORTED_BY_MEMORY_NOT_ENOUGH =
41314133
"内存不足,无法执行查询任务。";
41324134
public static final String ROOT_FRAGMENT_INSTANCE_PLACEMENT_ERROR_FMT =

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/QueryStateMachine.java‎

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public class QueryStateMachine {
5454
private final StateMachine<QueryState> queryState;
5555

5656
private final AtomicReference<Throwable> failureException = new AtomicReference<>();
57-
private final AtomicReference<TSStatus> failureStatus = new AtomicReference<>();
57+
private final AtomicReference<FailureStatus> failureStatus = new AtomicReference<>();
5858

5959
public QueryStateMachine(QueryId queryId, ExecutorService executor) {
6060
this.queryState =
@@ -90,7 +90,7 @@ public void transitionToDispatching() {
9090
}
9191

9292
public void transitionToPendingRetry(TSStatus failureStatus) {
93-
this.failureStatus.compareAndSet(null, failureStatus);
93+
recordFailureStatus(failureStatus, false);
9494
queryState.setIf(PENDING_RETRY, currentState -> currentState == DISPATCHING);
9595
}
9696

@@ -110,7 +110,7 @@ public void transitionToCanceled() {
110110
}
111111

112112
public boolean transitionToCanceled(Throwable throwable, TSStatus failureStatus) {
113-
this.failureStatus.compareAndSet(null, failureStatus);
113+
recordFailureStatus(failureStatus, false);
114114
this.failureException.compareAndSet(null, throwable);
115115
return transitionToDoneState(CANCELED);
116116
}
@@ -129,10 +129,34 @@ public void transitionToFailed(Throwable throwable) {
129129
}
130130

131131
public void transitionToFailed(TSStatus failureStatus) {
132-
this.failureStatus.compareAndSet(null, failureStatus);
132+
transitionToFailed(failureStatus, false);
133+
}
134+
135+
public void transitionToFailed(TSStatus failureStatus, boolean failureCausedByInterruption) {
136+
recordFailureStatus(failureStatus, failureCausedByInterruption);
133137
transitionToDoneState(FAILED);
134138
}
135139

140+
private void recordFailureStatus(TSStatus status, boolean failureCausedByInterruption) {
141+
if (status != null) {
142+
// Publish the status and its origin together. A later interruption must not reclassify
143+
// the first recorded failure.
144+
failureStatus.compareAndSet(null, new FailureStatus(status, failureCausedByInterruption));
145+
}
146+
}
147+
148+
/** Only the exact recorded status carries the interruption provenance stored with it. */
149+
public boolean isFailureCausedByInterruption(TSStatus status) {
150+
FailureStatus failure = failureStatus.get();
151+
if (failure == null || failure.status != status || !failure.failureCausedByInterruption) {
152+
return false;
153+
}
154+
// Statuses and exceptions can be recorded independently. Keep an unrelated exception
155+
// visible even if a subsequent dispatch interruption supplied the status.
156+
Throwable throwable = failureException.get();
157+
return throwable == null || getRootCause(throwable) instanceof InterruptedException;
158+
}
159+
136160
private boolean transitionToDoneState(QueryState doneState) {
137161
requireNonNull(doneState, DataNodeQueryMessages.EXCEPTION_DONESTATE_IS_NULL_D88F77E5);
138162
checkArgument(
@@ -161,9 +185,9 @@ public Throwable getFailureException() {
161185
}
162186

163187
public TSStatus getFailureStatus() {
164-
TSStatus status = failureStatus.get();
188+
FailureStatus status = failureStatus.get();
165189
if (status != null) {
166-
return status;
190+
return status.status;
167191
} else {
168192
Throwable throwable = failureException.get();
169193
if (throwable != null) {
@@ -174,7 +198,18 @@ public TSStatus getFailureStatus() {
174198
return RpcUtils.getStatus(((IoTDBException) t).getErrorCode(), t.getMessage());
175199
}
176200
}
177-
return failureStatus.get();
201+
status = failureStatus.get();
202+
return status == null ? null : status.status;
203+
}
204+
}
205+
206+
private static class FailureStatus {
207+
private final TSStatus status;
208+
private final boolean failureCausedByInterruption;
209+
210+
private FailureStatus(TSStatus status, boolean failureCausedByInterruption) {
211+
this.status = status;
212+
this.failureCausedByInterruption = failureCausedByInterruption;
178213
}
179214
}
180215
}

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/AbstractDriverThread.java‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,11 @@ public void run() {
6767
try {
6868
next = queue.poll();
6969
} catch (InterruptedException e) {
70-
logger.warn(
71-
DataNodeQueryMessages.EXECUTOR_FAILED_TO_POLL_DRIVER_TASK_FROM_QUEUE, this.getName());
70+
if (!closed) {
71+
logger.warn(
72+
DataNodeQueryMessages.EXECUTOR_FAILED_TO_POLL_DRIVER_TASK_FROM_QUEUE,
73+
this.getName());
74+
}
7275
Thread.currentThread().interrupt();
7376
break;
7477
}
@@ -86,7 +89,14 @@ public void run() {
8689
try (SetThreadName driverTaskName =
8790
new SetThreadName(next.getDriver().getDriverTaskId().getFullId())) {
8891
Throwable rootCause = ErrorHandlingCommonUtils.getRootCause(e);
89-
if (rootCause instanceof CorruptedTsFileException) {
92+
if (closed && rootCause instanceof InterruptedException) {
93+
Thread.currentThread().interrupt();
94+
// Still abort the task below so that shutdown releases its query resources.
95+
next.setAbortCause(
96+
new DriverTaskAbortedException(
97+
next.getDriverTaskId().getFullId(),
98+
DriverTaskAbortedException.BY_SCHEDULER_SHUTDOWN));
99+
} else if (rootCause instanceof CorruptedTsFileException) {
90100
// CorruptedTsFileException no longer chains the original IOException as its
91101
// cause (it uses addSuppressed instead), so getRootCause returns the exception
92102
// itself and we can match it here.

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/schedule/DriverTaskAbortedException.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class DriverTaskAbortedException extends Exception {
3434
DataNodeQueryMessages.DRIVER_TASK_ABORTED_BY_ALREADY_BEING_CANCELLED;
3535
public static final String BY_INTERNAL_ERROR_SCHEDULED =
3636
DataNodeQueryMessages.DRIVER_TASK_ABORTED_BY_INTERNAL_ERROR_SCHEDULED;
37+
public static final String BY_SCHEDULER_SHUTDOWN =
38+
DataNodeQueryMessages.EXCEPTION_SCHEDULER_SHUTDOWN_323938F5;
3739

3840
public static final String BY_MEMORY_NOT_ENOUGH =
3941
DataNodeQueryMessages.DRIVER_TASK_ABORTED_BY_MEMORY_NOT_ENOUGH;

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/ExecutionResult.java‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,16 @@ public class ExecutionResult {
2626
public final QueryId queryId;
2727
public final TSStatus status;
2828

29+
/** Whether this failure originated from an interrupted wait rather than another query error. */
30+
public final boolean failureCausedByInterruption;
31+
2932
public ExecutionResult(QueryId queryId, TSStatus status) {
33+
this(queryId, status, false);
34+
}
35+
36+
ExecutionResult(QueryId queryId, TSStatus status, boolean failureCausedByInterruption) {
3037
this.queryId = queryId;
3138
this.status = status;
39+
this.failureCausedByInterruption = failureCausedByInterruption;
3240
}
3341
}

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java‎

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -644,19 +644,29 @@ public ExecutionResult getStatus() {
644644
} catch (InterruptedException e) {
645645
// TODO: (xingtanzjr) use more accurate error handling
646646
Thread.currentThread().interrupt();
647+
// Dispatch may already have converted an interruption to a failure status. Preserve its
648+
// recorded origin without reclassifying an unrelated failure.
649+
TSStatus failureStatus = stateMachine.getFailureStatus();
650+
if (failureStatus != null) {
651+
return new ExecutionResult(
652+
context.getQueryId(),
653+
failureStatus,
654+
stateMachine.isFailureCausedByInterruption(failureStatus));
655+
}
647656
return new ExecutionResult(
648657
context.getQueryId(),
649-
stateMachine.getFailureStatus() == null
650-
? RpcUtils.getStatus(
651-
TSStatusCode.INTERNAL_SERVER_ERROR, stateMachine.getFailureMessage())
652-
: stateMachine.getFailureStatus());
658+
RpcUtils.getStatus(TSStatusCode.INTERNAL_SERVER_ERROR, stateMachine.getFailureMessage()),
659+
// A terminal query may have failed with an exception that has no dedicated status code.
660+
!stateMachine.getState().isDone());
653661
} catch (ExecutionException e) {
662+
TSStatus failureStatus = stateMachine.getFailureStatus();
654663
return new ExecutionResult(
655664
context.getQueryId(),
656-
stateMachine.getFailureStatus() == null
665+
failureStatus == null
657666
? RpcUtils.getStatus(
658667
TSStatusCode.INTERNAL_SERVER_ERROR, stateMachine.getFailureMessage())
659-
: stateMachine.getFailureStatus());
668+
: failureStatus,
669+
stateMachine.isFailureCausedByInterruption(failureStatus));
660670
}
661671
}
662672

@@ -727,7 +737,8 @@ private ExecutionResult getExecutionResult(QueryState state) {
727737
|| IoTDBConstant.ClientVersion.V_1_0.equals(context.getSession().getVersion())) {
728738
planner.setRedirectInfo(analysis, CONFIG.getAddressAndPort(), tsstatus);
729739
}
730-
return new ExecutionResult(context.getQueryId(), tsstatus);
740+
return new ExecutionResult(
741+
context.getQueryId(), tsstatus, stateMachine.isFailureCausedByInterruption(tsstatus));
731742
}
732743

733744
public DistributedQueryPlan getDistributedPlan() {

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/ClusterScheduler.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ public void start() {
125125
if (needRetry(result.getFailureStatus())) {
126126
stateMachine.transitionToPendingRetry(result.getFailureStatus());
127127
} else {
128-
stateMachine.transitionToFailed(result.getFailureStatus());
128+
stateMachine.transitionToFailed(
129+
result.getFailureStatus(), result.isFailureCausedByInterruption());
129130
}
130131
return;
131132
}

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragInstanceDispatchResult.java‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,21 @@ public class FragInstanceDispatchResult {
2626

2727
private TSStatus failureStatus;
2828

29+
private final boolean failureCausedByInterruption;
30+
2931
public FragInstanceDispatchResult(boolean successful) {
3032
this.successful = successful;
33+
this.failureCausedByInterruption = false;
3134
}
3235

3336
public FragInstanceDispatchResult(TSStatus failureStatus) {
37+
this(failureStatus, false);
38+
}
39+
40+
public FragInstanceDispatchResult(TSStatus failureStatus, boolean failureCausedByInterruption) {
3441
this.successful = false;
3542
this.failureStatus = failureStatus;
43+
this.failureCausedByInterruption = failureCausedByInterruption;
3644
}
3745

3846
public boolean isSuccessful() {
@@ -42,4 +50,8 @@ public boolean isSuccessful() {
4250
public TSStatus getFailureStatus() {
4351
return failureStatus;
4452
}
53+
54+
public boolean isFailureCausedByInterruption() {
55+
return failureCausedByInterruption;
56+
}
4557
}

‎iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,11 @@ private Future<FragInstanceDispatchResult> dispatchWrite(List<FragmentInstance>
330330
return immediateFuture(
331331
new FragInstanceDispatchResult(
332332
RpcUtils.getStatus(
333-
TSStatusCode.INTERNAL_SERVER_ERROR, "Interrupted errors: " + e.getMessage())));
333+
TSStatusCode.INTERNAL_SERVER_ERROR,
334+
String.format(
335+
DataNodeQueryMessages.MESSAGE_INTERRUPTED_ERRORS_ARG_876F1B4B,
336+
e.getMessage())),
337+
true));
334338
}
335339

336340
if (dispatchFailures.isEmpty()) {

0 commit comments

Comments
 (0)