Skip to content

[Query] Cancel dispatched fragment instances right away when the dispatch fails - #19085

Open
chrisdutz wants to merge 1 commit into
masterfrom
fix/query-terminate-on-dispatch-failure
Open

chrisdutz wants to merge 1 commit into
masterfrom
fix/query-terminate-on-dispatch-failure

Conversation

@chrisdutz

Copy link
Copy Markdown
Contributor

Description

Problem

IoTDBMultiTAGsWithAttributesTableIT is flaky. Usually only fourTableJoinTest fails (a known issue with the small memory pool of the test DataNode). But in some runs 9 to 15 test cases fail, all with:

719: There is not enough memory to execute current fragment instance, current remaining free memory is 921B, estimated memory usage for current fragment instance is 1960B

Once this starts, every later query in the class fails, even ones that need less than 2 KB.

Cause

fourTableJoinTest plans 25 fragment instances, many of them reserving about 1 MB of operator memory, and the operator pool of the test DataNode has about 15 MB. Some instances fail in LocalExecutionPlanner.checkMemory(), so the dispatch fails and ClusterScheduler fails the query.

The instances that were dispatched before the failure are cancelled by SimpleQueryTerminator, but only after TERMINATION_GRACE_PERIOD_IN_MS (1 s). Depending on timing, some of them have already lost their consumer and can't finish on their own. They keep their reservation until the cancel arrives.

With DEBUG logging for LocalExecutionPlanner enabled, a failing run shows it:

  • fourTableJoinTest fails at 15:46:19,687. Six of its instances (1.6, 1.8, 1.9, 1.12, 1.14, 1.20) log [ConsumeMemory] but never [ReleaseMemory], together about 4.2 MB.
  • The next test queries arrive within milliseconds. They fail their dispatch the same way and each leaves its own instances holding memory for another second, so free memory drops to 921 B.
  • The class ends at 15:46:20,351, before the first deferred cancel would have run.

Fix

ClusterScheduler remembers that the dispatch failed, and stop() then calls the new IQueryTerminator.terminateImmediately(), which cancels the dispatched instances without the grace period.

The grace period was added in #5808 so that instances flushing their last results to the consumer aren't cancelled while they finish normally. A query whose dispatch failed has no results to deliver, so it doesn't need it. Queries that were dispatched successfully keep the grace period.

Verification

  • New SimpleQueryTerminatorTest checks the delay of both methods. SimpleQueryTerminatorTest, QueryExecutionRetryTest, QueryOwnershipTest and AsyncPlanNodeSenderTest pass (17 tests).

  • spotless:check passes for iotdb-core/datanode.

  • IoTDBMultiTAGsWithAttributesTableIT alone, 10 runs each on macOS, on this branch's base and with this change:

    Failing test cases per run Base This change
    1 (fourTableJoinTest only) 5 10
    2 (fourTableJoinTest, timeColumnOuterJoinTest1) 1 0
    14 or 15 (cascade) 4 0

    fourTableJoinTest itself still fails, because its 25 instances don't fit into the memory pool of the test DataNode. That's a separate issue and unchanged by this PR.


This PR has:

  • been self-reviewed.
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths.

Key changed/added classes (or packages if there are too many classes) in this PR
  • iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/ClusterScheduler.java
  • iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/IQueryTerminator.java
  • iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/SimpleQueryTerminator.java
  • iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/scheduler/SimpleQueryTerminatorTest.java

🤖 Generated with Claude Code

…atch fails

When the dispatch of a query fails part way, for example because one
fragment instance can't get enough operator memory, ClusterScheduler
fails the query and SimpleQueryTerminator cancels the fragment
instances that were already dispatched. It waits for a grace period of
one second before it does so.

Those fragment instances can't finish on their own: their consumer is
gone, so they keep the operator memory they reserved in
LocalExecutionPlanner.checkMemory() until they are cancelled. If more
queries arrive within that second, they find the memory taken, fail
their dispatch too and hold more memory for another second. With a
small memory pool this cascades.

Remember in ClusterScheduler that the dispatch failed, and terminate
such a query without the grace period. Queries that ran normally keep
the grace period.
@sonarqubecloud

sonarqubecloud Bot commented Nov 2, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@codecov

codecov Bot commented Nov 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 55.55556% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.12%. Comparing base (999721f) to head (ba964d8).
⚠️ Report is 11 commits behind head on master.

Files with missing lines Patch % Lines
...b/queryengine/plan/scheduler/ClusterScheduler.java 33.33% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19085      +/-   ##
============================================
+ Coverage     48.11%   48.12%   +0.01%     
- Complexity      621      625       +4     
============================================
  Files          5572     5572              
  Lines        410286   411555    +1269     
  Branches      54129    54191      +62     
============================================
+ Hits         197408   198068     +660     
- Misses       212878   213487     +609     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@chrisdutz

Copy link
Copy Markdown
Contributor Author

The failing SonarCloud quality gate is not caused by this change. Its only bug finding is S3078 ("Use an AtomicBoolean") in iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java:1174, which this PR does not touch. The file was changed by #19066, which is part of this branch's base but newer than SonarCloud's last master analysis (2026-10-31, 26cbf0f), so SonarCloud counts it as new code here. Other current PRs against master fail the same gate for the same reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant