Skip to content

[fix](fe) Guard fragment cleanup until dispatch completes - #66767

Open
Mryange wants to merge 2 commits into
apache:masterfrom
Mryange:fix-query-context-finished-race
Open

[fix](fe) Guard fragment cleanup until dispatch completes#66767
Mryange wants to merge 2 commits into
apache:masterfrom
Mryange:fix-query-context-finished-race

Conversation

@Mryange

@Mryange Mryange commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Production stress-test logs captured the lifecycle race for a load query. At
13:35:52.506, BE received FINISHED and destroyed Query Context
6aa1098800c457e-bad0398f81aa610f. About 61 ms later, its phase-two
exec_plan_fragment_start RPC arrived and failed with Failed to get query fragments context,
causing the INSERT SELECT to fail. Root cause: FE could broadcast successful cleanup as soon as
execution completion was reported, without waiting for all fragment dispatch RPCs to finish. This
change delays FINISHED cleanup until both execution and fragment dispatch complete. It also
ensures that if the dispatch deadline has already expired after RPC futures were submitted, FE
records the error and actively cancels the coordinator so prepared or running backend contexts are
not left behind until backend timeout.

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
  • 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

### What problem does this PR solve?

Issue Number: N/A

Problem Summary: A load query could receive a FINISHED cleanup request while one backend was still waiting for its phase-two fragment start RPC. The backend then removed the Query Context before processing the start request, causing Failed to get query fragments context. Delay the FINISHED broadcast until query execution and all fragment dispatch RPCs have completed.

### Release note

None

### Check List (For Author)

- Test: sh run-fe-ut.sh --run org.apache.doris.qe.AbstractJobProcessorTest

- Behavior changed: No

- Does this need documentation: No
@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?

@Mryange

Mryange commented Aug 14, 2026

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.

Request changes.

The successful-dispatch handoff is directionally correct: the task is published before RPC submission, the two monotonic atomics cannot lose a wakeup, the final CAS prevents duplicate FINISHED fan-out, and the marker follows all applicable phase responses. Two blocking points remain: an expired-deadline exit can bypass backend cleanup after RPC submission, and the added tests do not exercise the production phase-two boundary.

Critical checkpoint conclusions

  • Goal and correctness: successful one-phase and two-phase ordering now prevents premature FINISHED cleanup, but the pre-wait leftTimeMs <= 0 exit leaves the new success marker unreachable and does not invoke the real cancellation path.
  • Scope and parallel paths: the patch is small and focused. Modern query/load, one-/two-phase, ordinary/internal query, insert/TVF/broker load, cloud routing, and the legacy Coordinator path were traced. Legacy Coordinator is intentionally unaffected because it holds its dispatch lock through both phases and does not use this early top-fragment FINISHED handoff.
  • Concurrency and lifecycle: report threads race the coordinator dispatch thread; AtomicBoolean ordering, task publication, the final CAS, and coordinator-to-backend-task lock order are sound, with no deadlock found. The expired-deadline branch is the remaining lifecycle defect because prepared or running BE query contexts can outlive FE failure/transaction abort until backend timeout.
  • Error handling and data writes: per-future non-OK results, execution failures, interruption, and timeouts publish an error and synchronously cancel. Only the pre-loop deadline branch throws after future submission without equivalent cleanup. No other transaction, persistence, EditLog, or data-visibility change was found.
  • Compatibility, configuration, and observability: no FE-BE wire field, storage format, persisted state, dynamic configuration, or rolling-upgrade change is introduced. Existing query/backend identifiers, latency profiles, failure metrics, and cancel logs are adequate; the added atomics are not a material hot-path cost.
  • Tests: the new tests prove the two Boolean orders and idempotence, but they mock PipelineExecutionTask and call the marker directly, so they remain green if the sole production marker is deleted or moved before phase two. No builds or tests were run because the review bundle forbids them; current CI shows CheckStyle passing but does not expose an FE unit-test result.
  • User focus: no additional user-provided focus was supplied; the complete PR was reviewed.

Review completion: converged after two rounds. All normal and risk-focused reviewers in round 2 returned NO_NEW_VALUABLE_FINDINGS; every candidate was independently validated, deduplicated, accepted, or dismissed. The final inline set contains the two issues above.

if (coordinatorContext.twoPhaseExecution()) {
sendAndWaitPhaseTwoRpc();
}
coordinatorContext.getJobProcessor().markFragmentDispatchCompleted();

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.

[P1] Cancel already-launched fragments when dispatch exits early

Both phase helpers launch all RPC futures before waitPipelineRpc checks leftTimeMs, but its leftTimeMs <= 0 branch throws without the updateStatusIfOk/cancelSchedule used by every per-future failure. For a load, a fast top fragment can report completion after one-phase submission or while phase-two starts are in flight; this patch records executionFinished, but this success-only marker is never reached. Load callers can then unregister or abort without cancelling the coordinator, leaving prepared or running BE query contexts until backend timeout. Please route the expired-deadline exit through the same real-error cancellation path before throwing and cover partial phase-one/phase-two dispatch in a test.

processor.tryFinishSchedule();
Mockito.verifyNoInteractions(fragmentsTask);

processor.markFragmentDispatchCompleted();

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] Exercise the production phase-two boundary

These tests call tryFinishSchedule() and markFragmentDispatchCompleted() directly while PipelineExecutionTask is only a mock, so they still pass if the sole production marker call is deleted, moved before phase two, or skipped by the load path. That means they do not reproduce the reported race. Please drive PipelineExecutionTask.execute() with controllable phase-two futures: report load completion while one start future is pending, assert no FINISHED cleanup, complete the final start response, and then assert exactly one cleanup.

@Mryange

Mryange commented Aug 14, 2026

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.

Follow-up review: no new non-duplicate inline findings.

The current head addresses the previously reported dispatch-deadline cleanup defect: the pre-wait timeout now records a real coordinator error and cancels already-launched/prepared backend contexts before throwing. The existing production phase-two test concern remains applicable and unresolved, so I have not duplicated it.

Critical checkpoint conclusions

  • Goal and correctness: the code now accomplishes the lifecycle goal. PipelineExecutionTaskBuilder publishes the task before RPC launch, the dispatch marker follows every applicable phase response, the two monotonic atomics cannot lose a crossing notification, and the final CAS permits exactly one FINISHED fan-out. The BE start response is produced only after its query context is found and made runnable.
  • Scope and clarity: the production change is small and focused: two completion signals, one interface method/call site, and cancellation in the previously uncovered pre-wait deadline branch. No unrelated source change was found.
  • Concurrency: BE report RPC threads can race the coordinator dispatch thread. Both signal arrival orders, repeated reports, external/error cancellation, monitor reentrancy, and task-level cancellation serialization were traced. The atomics provide the needed visibility, coordinator-to-backend-task lock order is consistent, and no deadlock or duplicate broadcast was found.
  • Lifecycle: success cleanup is withheld until all phase-one work and, when applicable, all phase-two start responses complete. Error/timeout paths retain first-error-wins coordinator state, release the load latch or query receiver, and cancel every backend task. No lost terminal wakeup or backend-context leak remains on a contractually reachable changed path.
  • Configuration: no configuration item is added or changed; dynamic-reload behavior is not applicable.
  • Compatibility: no function symbol, persisted representation, storage format, thrift/protobuf field, or FE-BE variable changes. Existing start/cancel RPCs are reused, so no rolling-upgrade compatibility fence is required.
  • Parallel paths: SQL query, insert/load, broker load, one-phase and two-phase Nereids dispatch, report handling, external cancellation, and the legacy Coordinator contract were checked. Query completion already follows synchronous dispatch; load completion is the intended racing path. The legacy implementation is not changed and does not use this Nereids handoff.
  • Conditional checks: the new two-flag condition directly represents the required invariant. The leftTimeMs <= 0 branch now matches the established per-future failure behavior by recording failure and cancelling before throwing.
  • Test coverage: the Boolean-order/idempotence tests and one-phase expired-deadline test cover their local mechanisms. They still do not drive PipelineExecutionTask.execute() with a pending phase-two future and a concurrent load-completion report; that exact gap is already recorded in the existing thread linked above.
  • Test results: no build or test was run because the authoritative review bundle forbids builds. Current PR checks show CheckStyle passing, but no FE unit-test result is listed, so runtime test execution is not independently verified here.
  • Observability: existing query/backend identifiers, timeout logs, RPC latency profile data, failure counters, blacklist handling, and cancel logs cover the changed paths. No additional metric or INFO log is needed for the two atomic state transitions.
  • Transactions and persistence: no EditLog, replay, metadata persistence, transaction commit, or master-failover behavior is modified.
  • Data writes: load transactionality and commit decisions remain outside this cleanup gate. A real dispatch error remains in FE status and releases/cancels the load path; a late FINISHED cleanup cannot convert it into a successful commit.
  • FE-BE state propagation: no new state crosses the FE-BE boundary, and all existing dispatch/cancel send points remain intact.
  • Performance: the change adds only constant-time atomic operations and a one-shot existing cleanup traversal; no meaningful hot-path CPU, memory, or allocation regression was found.
  • Other issues: all ordinary exits after RPC launch either reach the success marker after full dispatch or invoke real cancellation. No additional correctness, lifecycle, compatibility, performance, or coverage issue could be substantiated beyond the existing test thread.
  • User focus: no additional user-provided review focus was supplied; the complete PR was reviewed.

Review completion: converged after Round 1. Both normal full-review agents and the separate risk-focused agent returned NO_NEW_VALUABLE_FINDINGS after missed-area rechecks. Every suspicious point was independently verified or mapped to existing review context. This is intentionally a comment-only review with zero new inline comments.

@Mryange

Mryange commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17565	3003	3007	3003
q2	1905	240	156	156
q3	10453	894	507	507
q4	4668	241	192	192
q5	7694	566	385	385
q6	141	116	93	93
q7	529	496	380	380
q8	9245	875	962	875
q9	3407	2351	2370	2351
q10	6502	840	723	723
q11	443	250	234	234
q12	686	373	321	321
q13	17900	1874	1531	1531
q14	160	148	142	142
q15	q16	445	398	365	365
q17	842	814	785	785
q18	3094	2254	2239	2239
q19	1257	901	784	784
q20	629	534	461	461
q21	5636	1701	1902	1701
q22	337	278	230	230
Total cold run time: 93538 ms
Total hot run time: 17458 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3371	3297	3297	3297
q2	203	204	155	155
q3	2244	2266	2129	2129
q4	1188	1151	871	871
q5	2177	2153	2109	2109
q6	166	121	86	86
q7	1017	926	895	895
q8	1578	1380	1377	1377
q9	3058	3021	3013	3013
q10	1863	1772	1600	1600
q11	356	264	246	246
q12	461	444	343	343
q13	1822	1829	1559	1559
q14	177	169	158	158
q15	q16	386	384	352	352
q17	1045	1017	1013	1013
q18	4826	4348	4729	4348
q19	863	831	875	831
q20	937	919	779	779
q21	3708	3131	3251	3131
q22	419	354	334	334
Total cold run time: 31865 ms
Total hot run time: 28626 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 85436 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 09fd7b1effe0edac82253016b06323b1249e3cfb, data reload: false

query5	4233	410	321	321
query6	402	168	148	148
query7	4907	449	272	272
query8	292	126	114	114
query9	8687	2913	2908	2908
query10	408	262	214	214
query11	5383	1046	903	903
query12	123	70	66	66
query13	1182	454	311	311
query14	5947	1989	1846	1846
query14_1	1761	1759	1764	1759
query15	173	116	108	108
query16	948	402	368	368
query17	806	473	372	372
query18	2329	340	256	256
query19	176	141	117	117
query20	72	70	72	70
query21	228	116	100	100
query22	5306	5367	5316	5316
query23	7246	6572	6630	6572
query23_1	6628	6495	6754	6495
query24	7286	1093	773	773
query24_1	763	764	788	764
query25	436	315	266	266
query26	1247	259	163	163
query27	2729	429	289	289
query28	4642	1523	1511	1511
query29	956	455	371	371
query30	274	178	145	145
query31	966	664	611	611
query32	107	52	49	49
query33	477	203	163	163
query34	993	836	506	506
query35	388	395	330	330
query36	550	524	521	521
query37	122	91	73	73
query38	998	835	797	797
query39	532	532	512	512
query39_1	516	536	490	490
query40	220	123	126	123
query41	60	51	49	49
query42	73	74	84	74
query43	250	246	219	219
query44	1028	572	586	572
query45	111	104	102	102
query46	788	826	521	521
query47	982	978	969	969
query48	329	318	226	226
query49	534	245	199	199
query50	829	349	258	258
query51	8263	8207	8020	8020
query52	67	69	60	60
query53	207	215	163	163
query54	225	181	171	171
query55	73	59	51	51
query56	234	232	222	222
query57	626	614	622	614
query58	223	202	209	202
query59	1107	1074	999	999
query60	238	216	199	199
query61	116	128	125	125
query62	347	220	190	190
query63	180	154	156	154
query64	2630	653	603	603
query65	1543	1540	1555	1540
query66	1809	303	246	246
query67	9655	9728	9634	9634
query68	2765	1248	795	795
query69	340	233	198	198
query70	628	596	600	596
query71	297	248	238	238
query72	2378	1752	1570	1570
query73	665	622	365	365
query74	1570	1232	1122	1122
query75	1216	1134	995	995
query76	2298	762	564	564
query77	250	257	208	208
query78	5111	4763	4396	4396
query79	2239	881	581	581
query80	1578	373	336	336
query81	476	196	169	169
query82	634	133	107	107
query83	323	253	236	236
query84	297	124	105	105
query85	840	456	398	398
query86	385	183	164	164
query87	997	980	885	885
query88	2831	2157	2161	2157
query89	308	233	204	204
query90	1830	152	142	142
query91	157	152	128	128
query92	53	45	43	43
query93	1433	1135	763	763
query94	640	263	239	239
query95	631	375	352	352
query96	828	559	285	285
query97	1095	1048	1047	1047
query98	142	145	132	132
query99	416	341	299	299
Total cold run time: 179012 ms
Total hot run time: 85436 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.08	0.04	0.04
query3	0.26	0.11	0.10
query4	1.60	0.10	0.11
query5	0.17	0.17	0.15
query6	1.26	0.58	0.62
query7	0.03	0.01	0.00
query8	0.05	0.03	0.03
query9	0.30	0.21	0.20
query10	0.35	0.35	0.35
query11	0.15	0.11	0.11
query12	0.15	0.12	0.11
query13	0.30	0.30	0.30
query14	0.47	0.45	0.44
query15	0.35	0.34	0.35
query16	0.21	0.23	0.21
query17	0.73	0.69	0.68
query18	0.17	0.15	0.16
query19	1.20	1.17	1.19
query20	0.01	0.01	0.01
query21	15.44	0.16	0.11
query22	5.10	0.04	0.04
query23	16.18	0.25	0.09
query24	3.26	0.31	0.24
query25	0.11	0.04	0.03
query26	0.72	0.16	0.11
query27	0.03	0.03	0.03
query28	3.63	0.58	0.26
query29	12.42	3.16	2.55
query30	0.25	0.12	0.12
query31	2.76	0.37	0.16
query32	3.54	0.31	0.22
query33	1.36	1.39	1.43
query34	15.42	2.15	1.76
query35	1.72	1.71	1.72
query36	0.46	0.30	0.29
query37	0.07	0.04	0.04
query38	0.05	0.03	0.03
query39	0.03	0.03	0.02
query40	0.12	0.07	0.08
query41	0.08	0.03	0.03
query42	0.04	0.02	0.02
query43	0.04	0.03	0.03
Total cold run time: 90.68 s
Total hot run time: 14.42 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 78.57% (11/14) 🎉
Increment coverage report
Complete coverage report

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.

2 participants