Skip to content

[improvement](be) Name the task of every resource context still alive at graceful exit - #68942

Merged
hello-stephen merged 2 commits into
apache:masterfrom
morningman:debug-lsan-live-resource-ctx
Oct 14, 2026
Merged

hello-stephen merged 2 commits into
apache:masterfrom
morningman:debug-lsan-live-resource-ctx

Conversation

@morningman

@morningman morningman commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary:

In short. The NonConcurrent regression pipeline fails a few percent of its runs with Lsan fail while every case passes. LeakSanitizer finds, at the BE's graceful exit, one stream load's ResourceContext that was never released, but its report only shows allocation stacks, which are the same for every stream load, so nobody can tell which load it was. This PR makes the BE print, at graceful exit in memleak-check mode, every ResourceContext still alive with its task id, the references still held, its memory tracker and workload group, and whether its QueryContext is already gone. Nothing changes unless enable_graceful_exit_check=true.

Background.

  • With enable_graceful_exit_check=true (memleak-check mode, used by the regression pipelines on the ASAN build), doris_main stops every service and runs ExecEnv::destroy() instead of _exit(), so that LeakSanitizer checks the heap when the process exits. The NonConcurrent pipeline greps be.out for detected memory leaks after stop_be.sh --grace and reports Lsan fail.
  • Every query or load has a ResourceContext: CPU/memory/IO contexts, a task controller, the query memory tracker and the workload group. The QueryContext owns it, and the components that work for the task (load channel, memtables, scanner context, RuntimeQueryStatisticsMgr, threads attached to the task, ...) hold references of their own while they run.

The problem, and what it cost.

How this PR fixes it.

  • In memleak-check mode each ResourceContext registers itself in a process-wide set when it is constructed and removes itself when it is destroyed. The set stores the addresses bitwise inverted, so LeakSanitizer does not take it for a reference and still reports a leaked context.
  • ResourceContext::describe_live_contexts() returns one line per context still alive: address, strong references still held, task id, finished flag, running time, memory tracker (label, consumption, peak), workload group and, for a query, whether its QueryContext is still alive.
  • doris_main calls ResourceContext::log_live_contexts() right after ExecEnv::destroy() on the graceful exit path. It writes the description to be.WARNING and to be.out, where the LeakSanitizer report follows once main() returns, or one INFO line when no context is alive. The printed address falls inside one of the Objects leaked above ranges of that report.
  • The description is built on a short-lived thread, and the main thread only handles the resulting text. Describing a context touches it; done on the main thread, it could leave a pointer to a leaked context in a dead slot of the main thread's stack, which LeakSanitizer scans at exit — it would then take the context for referenced and not report it, so the run that reproduces the leak could pass. The stack of a finished thread is not scanned.

Results. What the next Lsan fail will tell, compared with today:

Before After
Which query or load leaked unknown task_id=…; its label, table and test are then in be.INFO
How many references are left unknown references=N
Whether the QueryContext is gone only by pairing ~20k log lines query_context=destroyed
Where to look the LeakSanitizer report in be.out the same be.out, just before that report (the build log's head -n 100 be.out shows it)
Cost with enable_graceful_exit_check=false (default) — one boolean check per ResourceContext construction and destruction

A line looks like this (values made up):

1 resource contexts are still alive. LeakSanitizer reports a leaked context as the memory block that holds it, so match the addresses below against the ranges it prints under 'Objects leaked above'.
  0x7c0000001010: references=1, task_id=1eac0c4ec4ec0000-0000000000000003, finished=true, running_ms=7569, mem_tracker=Load#Id=1eac0c4ec4ec0000-0000000000000003 (consumption=0, peak=17.18 MB), workload_group=normal(1), query_context=destroyed

Classes, and how they call each other.

  • ResourceContext (resource_context.{h,cpp}): registers in the constructor and unregisters in the destructor when memleak-check mode is on; describe_live_contexts() formats the live ones; log_live_contexts() runs that on its own thread and logs the result.
  • QueryTaskController: new is_query_context_alive().
  • doris_main: one call to log_live_contexts() after ExecEnv::destroy().
doris_main (graceful exit, enable_graceful_exit_check=true)
  ├─ ExecEnv::destroy()
  └─ ResourceContext::log_live_contexts() ──► be.WARNING + be.out (or one INFO line)
     └─ [short-lived thread] describe_live_contexts(100)
        ├─ registry of disguised addresses ◄── ResourceContext() / ~ResourceContext()
        └─ per live context:
              ├─ TaskController: task id, finished, running time
              ├─ MemoryContext → MemTrackerLimiter: label, consumption, peak
              ├─ WorkloadGroup: name, id
              └─ QueryTaskController::is_query_context_alive()

No holder of ResourceContext changes, and neither do the LeakSanitizer options or suppressions. Once the leaked load is known, the fix for the leak itself will be a separate PR.

Release note

None

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason

    Added ResourceContextLeakCheckTest (be/test/runtime/workload_management/resource_context_test.cpp): a context is listed with its task id, references and tracker until it is released, both by describe_live_contexts() and by log_live_contexts(); contexts created outside memleak-check mode are not tracked; a query's context shows query_context=destroyed once its QueryContext is gone; workload group and missing tracker are shown; the output is capped. On the first revision, NonConcurrent logged No resource context is alive after shutdown at both graceful exits (no live context in a normal run) and took 141 minutes, the median of the 43 previous full runs. Locally: clang-format 16, build-support/check-build-hygiene.sh, and -fsyntax-only of every changed file with the BE UT ASAN flags against regenerated gensrc. The BE UT binary was not built or run locally; the BE UT pipeline runs the new test.

  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

… at graceful exit

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary:

The NonConcurrent regression pipeline fails a few percent of runs with
"Lsan fail" although every case passes. At the BE's graceful exit
(enable_graceful_exit_check=true), LeakSanitizer finds one stream load's
ResourceContext that was never released, together with what it keeps alive:
its CPU/memory/IO contexts, QueryTaskController and memory trackers, the
std::make_shared block of its QueryContext (kept allocated by the weak_ptr in
QueryTaskController), and the "normal" workload group (kept alive by
ResourceContext::_workload_group). The QueryContext itself was destroyed:
every "query_id: ... use wg" line in be.INFO has its "Query ... deconstructed".
So one reference to the load's ResourceContext is never released, from
somewhere LeakSanitizer does not scan (every leak is reported as indirect).

The report cannot say which of the ~430 HTTP stream loads of a run it was:
the allocation stacks are the same for all of them, and the identifying data
(the tracker label holding the load id) is inside the leaked memory, which
LeakSanitizer does not print.

In memleak-check mode, every ResourceContext now registers itself in a
process-wide set when constructed and removes itself when destroyed. The set
keeps the addresses bitwise inverted, so LeakSanitizer does not count it as a
reference and still reports a leaked context. Right after ExecEnv::destroy()
on the graceful exit path, doris_main prints one line per context still alive
(address, references still held, task id, finished, running time, memory
tracker, workload group, and for a query whether its QueryContext is gone) to
be.WARNING and to be.out, where the LeakSanitizer report follows. The address
falls inside one of the "Objects leaked above" ranges of that report.

Nothing changes when enable_graceful_exit_check is false (the default) except
one boolean check per ResourceContext construction and destruction.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - Added ResourceContextLeakCheckTest.
    - Locally: clang-format 16, build-support/check-build-hygiene.sh, and
      -fsyntax-only of every changed file with the BE UT ASAN flags against
      regenerated gensrc. The BE UT binary was not built or run locally; the
      BE UT pipeline runs the new test.
- Behavior changed: No (adds logging in memleak-check mode only)
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29216 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 120a444a79f718ad84840f2a9c4cc94ef79d442a, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17591	5649	5554	5554
q2	2202	312	260	260
q3	10156	1436	866	866
q4	4675	479	350	350
q5	7447	821	549	549
q6	188	189	158	158
q7	795	800	619	619
q8	9248	1379	1155	1155
q9	5787	4510	4644	4510
q10	6825	1292	1034	1034
q11	430	259	248	248
q12	635	422	297	297
q13	18037	3081	2374	2374
q14	272	265	254	254
q15	q16	745	736	670	670
q17	1204	767	586	586
q18	7190	6296	6195	6195
q19	1267	961	620	620
q20	396	359	232	232
q21	5804	2425	2498	2425
q22	363	312	260	260
Total cold run time: 101257 ms
Total hot run time: 29216 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	6196	6186	6136	6136
q2	711	572	533	533
q3	5224	5402	4936	4936
q4	2079	2160	1651	1651
q5	5290	5732	5898	5732
q6	258	200	160	160
q7	2406	2079	1882	1882
q8	3375	3095	2880	2880
q9	8355	8401	8184	8184
q10	3767	3710	3369	3369
q11	671	466	384	384
q12	708	711	529	529
q13	2865	3180	2511	2511
q14	301	315	286	286
q15	q16	709	729	655	655
q17	8699	7739	7574	7574
q18	13212	12366	13195	12366
q19	986	868	871	868
q20	2244	2240	1989	1989
q21	6076	4797	5125	4797
q22	523	451	405	405
Total cold run time: 74655 ms
Total hot run time: 67827 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 152518 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 120a444a79f718ad84840f2a9c4cc94ef79d442a, data reload: false

query5	4310	632	457	457
query6	419	209	216	209
query7	4816	477	230	230
query8	325	180	168	168
query9	8741	4037	4029	4029
query10	471	338	263	263
query11	5915	3453	3169	3169
query12	149	87	83	83
query13	1251	453	335	335
query14	6549	4866	4547	4547
query14_1	4254	4274	4225	4225
query15	214	202	177	177
query16	946	453	424	424
query17	866	680	549	549
query18	2418	441	307	307
query19	203	159	136	136
query20	83	80	82	80
query21	219	144	121	121
query22	13002	13054	12824	12824
query23	13253	12553	12178	12178
query23_1	12312	12267	12251	12251
query24	7081	875	482	482
query24_1	472	483	479	479
query25	565	439	370	370
query26	1247	280	148	148
query27	2747	460	268	268
query28	4601	1959	1954	1954
query29	1564	642	462	462
query30	299	221	194	194
query31	910	781	656	656
query32	171	95	98	95
query33	514	324	244	244
query34	913	901	510	510
query35	771	771	650	650
query36	823	783	723	723
query37	137	119	96	96
query38	1795	1773	1701	1701
query39	758	709	710	709
query39_1	657	651	686	651
query40	225	119	101	101
query41	74	68	72	68
query42	89	88	87	87
query43	383	390	328	328
query44	1384	711	725	711
query45	186	179	171	171
query46	915	1013	606	606
query47	3005	2994	2923	2923
query48	299	318	228	228
query49	571	410	305	305
query50	713	284	214	214
query51	10540	10488	10429	10429
query52	77	84	69	69
query53	197	215	152	152
query54	229	206	174	174
query55	72	67	65	65
query56	269	220	214	214
query57	1618	1568	1550	1550
query58	289	256	249	249
query59	2378	2400	2131	2131
query60	277	262	230	230
query61	152	146	148	146
query62	400	347	285	285
query63	198	171	180	171
query64	2767	977	792	792
query65	3432	3371	3353	3353
query66	1769	428	303	303
query67	20128	20066	19931	19931
query68	2994	1017	640	640
query69	386	294	245	245
query70	945	832	841	832
query71	292	232	197	197
query72	2724	2636	2336	2336
query73	562	557	298	298
query74	4609	4477	4295	4295
query75	2466	2321	1952	1952
query76	2296	1114	642	642
query77	381	408	307	307
query78	9132	8923	8439	8439
query79	989	954	538	538
query80	757	444	337	337
query81	555	328	276	276
query82	1041	143	108	108
query83	310	202	183	183
query84	312	142	98	98
query85	847	426	362	362
query86	346	234	240	234
query87	2032	1958	1805	1805
query88	3692	2730	2693	2693
query89	401	299	257	257
query90	1832	193	181	181
query91	155	144	121	121
query92	96	90	88	88
query93	1044	1027	577	577
query94	518	323	305	305
query95	583	396	322	322
query96	674	540	236	236
query97	2446	2445	2315	2315
query98	163	151	150	150
query99	789	766	663	663
Total cold run time: 235478 ms
Total hot run time: 152518 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.63 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 120a444a79f718ad84840f2a9c4cc94ef79d442a, data reload: false

query1	0.00	0.00	0.01
query2	0.11	0.05	0.04
query3	0.27	0.11	0.12
query4	1.61	0.13	0.13
query5	0.27	0.25	0.24
query6	1.19	0.70	0.68
query7	0.04	0.01	0.00
query8	0.06	0.04	0.04
query9	0.43	0.37	0.37
query10	0.58	0.59	0.60
query11	0.21	0.14	0.14
query12	0.19	0.16	0.15
query13	0.50	0.50	0.50
query14	0.96	0.98	0.97
query15	0.62	0.62	0.61
query16	0.31	0.36	0.34
query17	1.10	1.14	1.16
query18	0.24	0.24	0.24
query19	2.16	2.05	1.93
query20	0.02	0.01	0.01
query21	15.50	0.28	0.15
query22	4.45	0.05	0.05
query23	16.24	0.33	0.11
query24	2.91	0.74	0.42
query25	0.12	0.07	0.06
query26	1.37	0.23	0.15
query27	0.05	0.04	0.04
query28	2.23	0.62	0.43
query29	12.54	4.23	3.32
query30	0.28	0.14	0.14
query31	2.77	0.58	0.34
query32	3.23	0.62	0.52
query33	3.25	3.19	3.23
query34	15.53	3.98	3.37
query35	3.38	3.34	3.32
query36	0.56	0.48	0.42
query37	0.10	0.06	0.06
query38	0.05	0.04	0.04
query39	0.03	0.03	0.03
query40	0.17	0.16	0.15
query41	0.08	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 95.79 s
Total hot run time: 24.63 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (2/2) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 65.65% (31262/47620)
Line Coverage 50.37% (328717/652574)
Region Coverage 45.74% (265431/580287)
Branch Coverage 47.41% (124626/262846)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 77.78% (7/9) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.91% (35456/46098)
Line Coverage 62.15% (401017/645229)
Region Coverage 58.55% (338385/577905)
Branch Coverage 59.50% (155901/262039)

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complete static review of PR #68942 at head 120a444a79f718ad84840f2a9c4cc94ef79d442a, covering all five changed files and their query/load/background-task and shutdown interactions. Two review rounds completed; both normal reviewers and the separate risk reviewer returned NO_NEW_VALUABLE_FINDINGS in round 2. All candidates and initial risks were independently adjudicated, and the final changed-file sweep found no unresolved point.

One new P2 issue is reported inline: a detached manual-compaction worker can construct a ResourceContext during shutdown enumeration, racing the weak-owner initialization that follows constructor registration. This should be addressed before relying on the diagnostic's creator-quiescence assumption. No P0/P1 issue was substantiated. Refreshed existing reviews/inline comments contain no earlier finding to duplicate or carry as an existing blocker. No additional user review focus was supplied.

Critical checkpoint conclusions:

Checkpoint Conclusion
Goal and evidence The patch adds task/address/reference/tracker/query-liveness attribution for shutdown survivors. The ordinary behavior is covered by inspected unit-test assertions; the construction-overlap defect is reported inline. No executed test proves end-to-end shutdown behavior in this review.
Scope and reuse Focused five-file diagnostic change; reuses ResourceContext construction, existing task controllers, trackers and workload groups. No unrelated change found.
Concurrency and synchronization Registry insertion, erasure and lookup share one mutex, but make_shared's later weak-owner initialization does not. Detached manual base/cumulative compaction escapes pool/server draining, producing the reported P2.
Lock cost, order and deadlocks Snapshot reserve/pinning occurs under the registry mutex; formatting and temporary-owner destruction occur outside it. No additional lock inversion or allocation-to-factory recursion substantiated.
Lifecycle and static initialization Existing initialized survivors retain their child contexts, tracker and workload group. Query liveness uses a const weak observer without reviving QueryContext. Destructor erasure precedes weak-base teardown; lazy immortal registry avoids static-order dependencies and permanent owning references. Unsafe constructor publication is the inline finding.
Configuration and dynamic changes No new configuration. Existing nonmutable enable_graceful_exit_check is fixed at startup; default-disabled mode skips registry insertion and uses the existing fast-exit path.
Compatibility and rolling upgrades No wire/storage-format or distributed function-interface change. No source-visible newer libc/TLS dependency found; final GLIBC_2.17 ELF compatibility was not audited because builds are prohibited.
Thrift/protobuf IDs No gensrc definition changed; branch-by-branch field-ID comparison is not applicable.
Parallel paths Shared constructor covers query, standalone/reused load, snapshot and tracker/orphan attachments in cloud and shared-nothing modes. The detached local manual-compaction exception is included in the finding; no other distinct instance substantiated.
Conditional checks and failure handling Mode gate, tracking flag, optional metadata, expired-owner skip, empty snapshot and output cap correspond to supported states. Erase invariant is checked; no new ignored Status or THROW_IF_ERROR boundary found.
Test coverage Four GTest cases cover registration/release, reference counts, finished/task/tracker fields, disabled creation, query alive/destroyed and truncation; recursive runtime glob discovers them. Concurrent publication/final-release and actual LSan/graceful-shutdown behavior are untested here.
Test results No golden/output result file changed. Expected strings agree with inspected implementation; runtime results were not produced or verified.
Observability WARNING plus stderr supplies task IDs, addresses, tracker/workload metadata and state for LSan correlation. Caller limits detailed entries to 100. No running-path metric is required for this shutdown diagnostic.
Transactions and persistence No EditLog/replay, persistent metadata, transaction or master-failover behavior changed; not applicable.
Data writes and crash atomicity No data-write, version-visibility or delete-bitmap semantics changed; not applicable.
FE-BE variable propagation No new transmitted variable or send path; not applicable.
Performance and memory accounting Disabled mode adds an early branch; enabled insert/erase is O(1) average and snapshot is O(N) in live contexts with bounded formatting. Registry stores complemented addresses, snapshot owners expire before main returns, and accounting does not recursively construct contexts. No separate concrete scaling/accounting failure found.
Other BE/module obligations No PCH, gate-table, template-pairing or test-includes-src-cpp change. Pure-text build-hygiene checks passed. No additional substantiated correctness or lifecycle issue remains beyond the inline finding.

Validation was static-only. bash build-support/check-build-hygiene.sh passed header layering/bans/budgets/PCH, extern-template pairing and unity-skip coverage. No Doris build, unit/regression test, live cluster or sanitizer run was attempted; repository source files were not modified.

std::lock_guard<std::mutex> l(live.lock);
contexts.reserve(live.disguised_contexts.size());
for (uintptr_t disguised_context : live.disguised_contexts) {
if (auto reference = undisguise(disguised_context)->weak_from_this().lock()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Synchronize context publication before taking the shutdown snapshot

ExecEnv::destroy() does not ensure that all context creators have stopped: CompactionAction::_handle_run_compaction() detaches manual base/cumulative compaction and waits only two seconds. A worker already owning its tablet and memory tracker can be delayed immediately before SCOPED_ATTACH_TASK(_mem_tracker) in BaseCompaction::execute_compact() / CumulativeCompaction::execute_compact(), then construct a context while this shutdown snapshot runs. The constructor registers this before make_shared initializes enable_shared_from_this's weak pointer; that later write is outside live.lock, so this weak_from_this() read races it (the C++ shared-pointer requirements explicitly make that initialization non-atomic). This introduces undefined behavior in memleak-check shutdown, and can omit the context from the diagnostic. Ensure all creators are joined before this call, or synchronize publication and metadata initialization before exposing a context to the registry; cover this overlap with a concurrency test.

… at graceful exit

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary:

The previous commit made doris_main describe the resource contexts that are
still alive after ExecEnv::destroy(), right before LeakSanitizer checks the
heap. Describing a context touches it, and doing that on the main thread can
leave a pointer to a leaked context in a dead slot of the main thread's
stack. LeakSanitizer scans that stack when the process exits; if it finds the
stale pointer it takes the context for referenced and does not report it, so
the very run that reproduces the leak could pass without "Lsan fail".

ResourceContext::log_live_contexts() now builds the description on a thread
of its own and joins it; the stack of a finished thread is not scanned, and
the main thread only handles the resulting text. It logs to be.WARNING and
stderr (be.out), or one INFO line when no context is alive, and doris_main
just calls it. Moving the logging out of doris_main, whose code after
"Doris main exiting" never shows up in regression coverage, also lets the unit
test cover it; the test now covers the workload group and missing tracker
cases too.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - Extended ResourceContextLeakCheckTest.
    - Locally: clang-format 16, build-support/check-build-hygiene.sh, and
      -fsyntax-only of every changed file with the BE UT ASAN flags. The BE UT
      binary was not built or run locally; the BE UT pipeline runs the test.
- Behavior changed: No (memleak-check mode only)
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complete static review of PR #68942 at c9c422199945c07be28665c590171d19c58fc03f, using the supplied aggregate diff and all five changed files. No additional substantiated inline findings.

The existing P2 publication/shutdown finding remains applicable. Detached manual base/cumulative compaction can create a ResourceContext while the shutdown snapshot runs; constructor registration precedes initialization of its shared-ownership observer and task metadata. The C++ specification confirms that the observer initialization is non-atomic. This issue is already reported and has not been duplicated. No existing P0/P1 comment applies, so existing_blocking_comment_ids is empty. C++ shared-pointer requirements

The reviewed lifecycle is:

enabled graceful exit
  -> stop/join managed services and workers
  -> ExecEnv::destroy()
  -> diagnostic thread: snapshot under mutex -> unlock -> format -> release references
  -> join diagnostic thread -> log returned text -> main returns
Detached manual compaction can overlap the snapshot: existing P2 above.

Checkpoint conclusions:

  1. Goal and proof: the diagnostic identifies residual contexts by address, task id, reference count, finish/runtime state, tracker usage, workload group, and QueryContext lifetime. Ordinary initialized contexts are covered by unit assertions; the existing publication issue prevents an unconditional shutdown-correctness conclusion.
  2. Scope and clarity: changes are focused on the registry, diagnostic methods, weak QueryContext accessor, one shutdown call, and five unit cases.
  3. Concurrency, lock weight, and order: registry insertion, erasure, and snapshot share one mutex. The snapshot reserves and traverses all entries under that mutex; formatting and final reference release occur outside it. No separate lock-order or final-release deadlock was substantiated. Actual constructor/metadata overlap is the existing P2.
  4. Lifecycle and static initialization: lazy function-local registry initialization avoids cross-TU ordering; its intentional process lifetime permits late unregistering. Snapshot strong references protect objects, and QueryTaskController retains a weak QueryContext observer. No new ownership cycle was found. From LLVM's pointer filtering and finished-thread cleanup, the inverted registry and joined helper do not introduce a demonstrated persistent context root; this is a source-based inference, without an executed LSan test. LLVM LSan root scanning, LLVM ASan thread cleanup
  5. Configuration: no new setting. Existing enable_graceful_exit_check is immutable and defaults to false; the ordinary main path exits before this diagnostic. Per-object _tracked keeps unregistering balanced across test-only flag changes.
  6. Compatibility: no storage/wire-format or rolling-upgrade change. The added C++ APIs are internal; no new libc API, dependency, or toolchain requirement was identified. Release ELF/glibc-2.17 compatibility was not tested.
  7. Thrift/protobuf ids: not applicable; no generated protocol definitions changed.
  8. Parallel paths: centralized constructor registration covers the production shared factories for query, load-channel/load-stream, task attachment/orphan fallback, and snapshot-loader contexts. Cloud and shared-nothing modes converge on the same diagnostic call.
  9. Conditional and error handling: disabled tracking, tracked membership, expired observers, missing tracker/workload group, query-specific controller, and output cap are actual documented states. Exact registry removal is asserted; no new ignored Status or silent invariant failure was found.
  10. Test coverage: the five discovered TEST_F cases cover registration/removal, external reference counts, finish/tracker/workload-group fields, disabled-mode construction, QueryContext expiration, and truncation. Fixtures restore configuration and identify their own task without depending on set order. Concurrent shutdown and actual LSan leak preservation are not covered; concurrency coverage is already requested in the existing thread.
  11. Test results: no regression goldens changed. Unit cases were inspected, not executed; author/CI claims are not treated as executed validation here.
  12. Observability: WARNING plus stderr provides task/address/tracker correlation; empty shutdown emits one INFO line. Output is capped at 100 contexts. A new operational metric is unnecessary for this shutdown diagnostic.
  13. Transactions and persistence: not applicable; transaction, EditLog/replay, and failover semantics are unchanged.
  14. Data writes and crash atomicity: not applicable; no data-write, visibility/version, or delete-bitmap logic changed.
  15. FE-BE variables: not applicable; no transmitted variable or sending path changed.
  16. Performance and memory accounting: disabled mode adds a registration check and historical tracking check; enabled registration is average O(1), and shutdown snapshot time/storage are O(live contexts). No allocation-driven factory recursion or distinct contention failure was found. The helper runs after ExecEnv readiness/accounting are disabled and uses ordinary diagnostic allocations without accessing deleted managers.
  17. Other concerns: all changed files and initial risk points were rechecked; no additional substantiated defect remains to report. The pure-text build-support/check-build-hygiene.sh passed header layering/bans/budgets/PCH, extern pairing, and unity-skip coverage.

No additional user-provided review focus was supplied. Review analysis is COMPLETE: both normal reviewers and the separate risk reviewer returned NO_NEW_VALUABLE_FINDINGS in round 1; the main agent independently verified/deduplicated every observation and completed the final changed-file/candidate sweep. No builds, unit/regression tests, or source edits were performed.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29132 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit c9c422199945c07be28665c590171d19c58fc03f, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17634	5620	5583	5583
q2	2098	310	271	271
q3	10249	1451	862	862
q4	4675	489	352	352
q5	7441	814	560	560
q6	186	186	159	159
q7	780	815	623	623
q8	9265	1351	1179	1179
q9	5813	4527	4488	4488
q10	6824	1311	1017	1017
q11	451	254	232	232
q12	629	407	291	291
q13	18049	3079	2384	2384
q14	274	274	248	248
q15	q16	740	729	666	666
q17	1211	751	577	577
q18	7136	6248	6185	6185
q19	1272	955	625	625
q20	391	346	228	228
q21	5313	2323	2466	2323
q22	373	322	279	279
Total cold run time: 100804 ms
Total hot run time: 29132 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	6160	6139	6127	6127
q2	697	591	538	538
q3	5290	5469	4967	4967
q4	2040	2139	1562	1562
q5	5253	5563	5909	5563
q6	249	229	159	159
q7	2410	2114	1851	1851
q8	3336	3047	2851	2851
q9	8397	8281	8364	8281
q10	3832	3698	3324	3324
q11	656	442	453	442
q12	703	741	508	508
q13	2909	3255	2511	2511
q14	297	301	277	277
q15	q16	739	726	796	726
q17	8447	7527	7485	7485
q18	13162	12383	13016	12383
q19	992	880	890	880
q20	2214	2223	1960	1960
q21	6109	4746	4963	4746
q22	495	478	418	418
Total cold run time: 74387 ms
Total hot run time: 67559 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 151843 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit c9c422199945c07be28665c590171d19c58fc03f, data reload: false

query5	4298	581	455	455
query6	417	212	187	187
query7	4799	455	227	227
query8	308	186	165	165
query9	8781	3945	3881	3881
query10	478	309	265	265
query11	5975	3429	3166	3166
query12	146	88	83	83
query13	1258	441	316	316
query14	6533	4784	4481	4481
query14_1	4225	4223	4206	4206
query15	208	209	179	179
query16	932	434	419	419
query17	855	652	539	539
query18	2423	423	318	318
query19	191	173	146	146
query20	82	81	78	78
query21	207	136	114	114
query22	13044	12957	12845	12845
query23	13192	12760	12037	12037
query23_1	12230	12357	12165	12165
query24	7088	833	458	458
query24_1	475	470	490	470
query25	538	425	384	384
query26	1251	264	140	140
query27	2759	442	271	271
query28	4624	1943	1931	1931
query29	1572	603	459	459
query30	303	232	199	199
query31	901	769	655	655
query32	158	96	96	96
query33	514	306	253	253
query34	935	853	511	511
query35	747	765	645	645
query36	860	815	733	733
query37	129	103	92	92
query38	1791	1745	1681	1681
query39	706	701	689	689
query39_1	650	676	685	676
query40	219	122	101	101
query41	87	84	68	68
query42	86	87	82	82
query43	378	388	332	332
query44	1310	722	704	704
query45	187	175	167	167
query46	845	970	576	576
query47	3034	2570	2955	2570
query48	291	303	215	215
query49	560	401	286	286
query50	692	281	208	208
query51	10586	10610	10633	10610
query52	79	76	77	76
query53	194	209	157	157
query54	229	187	188	187
query55	73	67	64	64
query56	234	217	211	211
query57	1627	1577	1557	1557
query58	276	258	259	258
query59	2362	2397	2117	2117
query60	285	236	225	225
query61	146	146	148	146
query62	390	346	285	285
query63	196	158	159	158
query64	2761	951	774	774
query65	3438	3372	3357	3357
query66	1787	438	302	302
query67	20277	20145	20104	20104
query68	3226	1002	611	611
query69	390	297	243	243
query70	926	804	834	804
query71	299	238	212	212
query72	2806	2566	2386	2386
query73	549	547	292	292
query74	4598	4466	4312	4312
query75	2356	2308	1918	1918
query76	2404	1012	614	614
query77	372	405	305	305
query78	9054	9037	8491	8491
query79	956	908	513	513
query80	798	417	338	338
query81	576	313	267	267
query82	718	135	112	112
query83	302	216	173	173
query84	314	122	104	104
query85	924	417	365	365
query86	351	239	235	235
query87	1990	1953	1837	1837
query88	3614	2727	2703	2703
query89	344	294	257	257
query90	1762	177	183	177
query91	155	150	126	126
query92	95	85	89	85
query93	989	979	573	573
query94	516	298	283	283
query95	583	344	319	319
query96	676	514	234	234
query97	2456	2403	2347	2347
query98	160	147	144	144
query99	769	752	650	650
Total cold run time: 234876 ms
Total hot run time: 151843 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.32 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit c9c422199945c07be28665c590171d19c58fc03f, data reload: false

query1	0.01	0.01	0.01
query2	0.10	0.04	0.05
query3	0.27	0.12	0.12
query4	1.62	0.12	0.12
query5	0.27	0.25	0.24
query6	1.17	0.67	0.68
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.42	0.35	0.37
query10	0.59	0.59	0.58
query11	0.20	0.14	0.13
query12	0.18	0.15	0.15
query13	0.48	0.48	0.49
query14	0.97	0.97	0.96
query15	0.62	0.61	0.60
query16	0.35	0.35	0.36
query17	1.20	1.13	1.10
query18	0.22	0.21	0.20
query19	2.10	2.01	1.94
query20	0.01	0.01	0.01
query21	15.54	0.26	0.14
query22	4.44	0.05	0.05
query23	16.23	0.32	0.12
query24	3.24	0.55	0.37
query25	0.11	0.06	0.06
query26	1.05	0.26	0.15
query27	0.04	0.04	0.04
query28	1.89	0.63	0.40
query29	12.56	4.22	3.27
query30	0.28	0.13	0.13
query31	2.77	0.58	0.33
query32	3.22	0.62	0.53
query33	3.13	3.17	3.30
query34	15.42	3.97	3.36
query35	3.35	3.36	3.31
query36	0.56	0.46	0.44
query37	0.10	0.06	0.06
query38	0.05	0.04	0.04
query39	0.04	0.04	0.03
query40	0.18	0.15	0.14
query41	0.08	0.03	0.03
query42	0.03	0.02	0.02
query43	0.04	0.03	0.04
Total cold run time: 95.23 s
Total hot run time: 24.32 s

@hello-stephen
hello-stephen merged commit ccbd46d into apache:master Oct 14, 2026
41 of 42 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Oct 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

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

Labels

approved Indicates a PR has been approved by one committer. reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants