MPP task can remain alive after cancel and abort return
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 1k
- Forks
- 423
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 24
Description
Problem
An MPP task can remain alive for hours even after TiFlash receives cancel and MPPTask::abort() returns. Repeated queries then keep adding retained tasks until the TiFlash process is restarted.
Incident trigger and impact
On 2026-08-26, all three TiFlash compute instances entered RSS memory protection: RSS was about 47 GiB, above the 45.61 GiB data-compute limit. Two instances recovered, but tikc2v continued accumulating MPP tasks.
Its last long-lived-task monitor entry at 15:07:05 PDT contained:
- 5,794 live tasks across 3,286 unique queries;
- task ID 3: 2,945 entries;
- task ID 6: 2,158 entries;
- 5,094 tasks aged at least 10 hours.
The 1,000 rows returned by several memory-protection searches were the Loki UI limit, not a query count.
Confirmed retained-task lifecycle
Representative task:
query_ts=1787741933891977310local_query_id=13747start_ts=468645821513728101- task ID:
3 - instance:
db-13dd8e6a-compute-tiflash-tikc2v - SQL digest:
2e9084c507b31179f6f27687f0c04726a6cb05a979d963d8dd22c2c60c0c3752 - plan digest:
a6820fa8b4e72e1dd94ead2567b8779d5b82db50276568a351ca594fd8bab4eb - stage: Columnar table scan -> projection -> hash aggregation -> exchange sender
Observed timeline (PDT):
| Time | Event |
|---|---|
| 03:58:54.590 | TiFlash receives the TiDB cancel request. |
| 03:58:54.591 | Begin abort task and Finish abort task from running are logged. |
| same lifecycle | Tunnel 3+4 establish completes; total cost is 608 ms. |
| afterward | No mpp task finish execute, task statistics/end timestamp, destructor, or unregister is found. |
| 15:07:05 | TMTContext still reports the exact task alive for 40,091.56 seconds. |
This sample proves that cancel was delivered and the synchronous abort() path returned, but the task did not finish execution and unregister.
The older IDs previously listed as dispatch-only orphans are not evidence for this issue: full-namespace searches show establish, FINISHED, and nonzero task-end timestamps for them. Also, the absence of an establish RPC for every non-root task is expected and is not an orphan signal.
Source assessment
Confirmed deployed build:
- TiFlash version:
v26.3.10 - TiFlash commit:
d89f347e5331ad47abe277764f67ff21f6a6976c - CSE commit:
219b17ddf78dae2b9ae41afeca38dd33f08b9c0c
The cancellation chain is:
MPPTask::abort() -> QueryExecutor::cancel() -> PipelineExecutorContext::cancel()
Relevant behavior at the deployed TiFlash commit:
MPPTask::abort()requests executor cancellation and explicitly leaves the remaining work to the runningrunImpl().MPPTask::runImpl()can record task end and unregister only afterquery_executor_holder->execute()returns.- Production
PipelineExecutor::wait()has no timeout. PipelineExecutorContext::wait()waits foractive_ref_count == 0;cancel()marks the context cancelled and asks the scheduler to cancel the query.- Scheduler cancellation moves queued tasks to cancellation queues, but cannot preempt a
Taskalready owned by a CPU/IO worker. A Task decrementsactive_ref_countonly when its destructor runs.
The representative task is a Columnar scan stage. The deployed Columnar path contains several non-cancel-aware blocking sections:
buildColumnarReadTaskWithBackoff()andcreateColumnarReaderWithBackoff()retry without a query-cancel check;getOrCreateReader()waits onRNColumnarReaderWork::cvwithout cancellation in the predicate;RNColumnarSourceOp::executeIOImpl()enterscurrent_input_stream->read()after only an outer entry check;RNColumnarInputStream::readImpl()calls the CSE FFIfn_read_block()without a cancellation token/check inside the call.
The deployed and current master blobs for StorageDisaggregatedColumnar.cpp are identical as of 2026-08-26, so no upstream fix was found in that path.
Working failure mechanism
Cancel reaches TiFlash -> synchronous abort returns -> an already-running pipeline/preprocess operation does not observe cancellation or return -> its Task remains alive and
active_ref_countstays nonzero ->PipelineExecutorContext::wait()never returns ->MPPTask::runImpl()never records task end, unregisters, or destructs the MPP task.
Proof boundary
Confirmed:
- The retained entries are real
MPPTaskobjects in TiFlash's in-process monitor. - Cancel reached TiFlash for the representative task.
- The synchronous abort path returned, but execution completion/unregister did not occur.
- The source allows an already-running Task to keep
active_ref_countnonzero after cancel. - The Columnar path has concrete cancellation gaps capable of producing this state.
Not confirmed because native stacks were not captured before restart:
- The exact blocked frame for task 3: preprocess/backoff, reader materialization wait,
fn_read_block, deserialization, tunnel send, or another operator. - Whether all 5,794 retained tasks share the same blocked primitive; the large task-6 population suggests receiver/intermediate stages also need inspection.
Requested change
- Make Columnar build/materialization/read waits cancellation-aware, including backoff loops,
RNColumnarReaderWorkwaits, and the CSE FFI read boundary. - When executor drain after cancel exceeds a bounded interval, log
active_ref_countand the active task/status/operator information; provide an on-demand stack/task dump for diagnosis. - Add a regression test that cancels while a task is already executing/blocking in Columnar IO and asserts executor drain, task-end statistics, unregister, and TMT monitor removal.
- Evaluate a safe bounded cleanup/watchdog path for cancelled tasks whose executor does not drain. Do not reclaim an MPPTask while a worker can still reference it.
- Improve TiDB/TiFlash per-address cancel-result logging and idempotent retry as defense in depth.
Related TiDB hardening issue: pingcap/tidb#70685. TiDB cancellation reliability does not fix this confirmed cancel-delivered TiFlash stall.
The restart-time HandleHttpRequestReadyz SIGSEGV is a separate recovery-stage defect and is intentionally excluded from this MPP retention RCA.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with dbms/src/Flash/Mpp/MPPTask.cpp, dbms/src/Flash/Executor/PipelineExecutor.cpp, dbms/src/Flash/Executor/PipelineExecutorContext.cpp, and the Columnar path in StorageDisaggregatedColumnar.cpp; trace cancellation through executor drain and the listed blocking operations. Use a regression test that cancels during Columnar IO and verify executor drain, task-end statistics, unregister, and TMT monitor removal.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- databases, distributed-systems
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100