Skip to content

[fix](be) Enforce the sort cursor block invariant in release builds - #68495

Open
yiguolei wants to merge 1 commit into
apache:masterfrom
yiguolei:f3
Open

yiguolei wants to merge 1 commit into
apache:masterfrom
yiguolei:f3

Conversation

@yiguolei

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: BlockSupplierSortCursorImpl requires every supplied block to contain rows unless EOF is set. The existing DCHECK disappears in release builds, allowing execution to continue if this contract is violated. Replace it with DORIS_CHECK so release builds throw through the existing exception handling instead of allowing an invalid cursor to reach the merge heap. The check runs per input block, not per row. This is an invariant enforcement change; the reported production crash has not been reproduced or attributed to an empty input block.

Release note

Sorted merge queries report an error in release builds if a block supplier returns an empty block without EOF. Debug builds retain fatal checking.

Check List (For Author)

  • Test: Manual checks
    • Passed clang-format 16.0.5 via check-format.sh on an isolated copy of the changed file
    • Passed build-support/check-build-hygiene.sh and git diff --check
    • Attempted ./run-be-ut.sh --run --filter='SortMergerTest.*'; stopped during Java environment validation because no Java Runtime is available
    • BE build and clang-tidy not run; no compilation database is available
  • Behavior changed: Yes; enforce the existing input invariant in release builds
  • Does this need documentation: No

What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

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: None

Related PR: None

Problem Summary: BlockSupplierSortCursorImpl requires every supplied block to
contain rows unless EOF is set. The existing DCHECK disappears in release
builds, allowing execution to continue if this contract is violated. Replace
it with DORIS_CHECK so release builds throw through the existing exception
handling instead of allowing an invalid cursor to reach the merge heap.
The check runs per input block, not per row. This is an invariant enforcement
change; the reported production crash has not been reproduced or attributed
to an empty input block.

### Release note

Sorted merge queries report an error in release builds if a block supplier
returns an empty block without EOF. Debug builds retain fatal checking.

### Check List (For Author)

- Test: Manual checks
    - Passed clang-format 16.0.5 via check-format.sh on an isolated copy of the changed file
    - Passed build-support/check-build-hygiene.sh and git diff --check
    - Attempted ./run-be-ut.sh --run --filter='SortMergerTest.*'; stopped during Java environment validation because no Java Runtime is available
    - BE build and clang-tidy not run; no compilation database is available
- Behavior changed: Yes; enforce the existing input invariant in release builds
- 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?

@yiguolei

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17626	3941	3827	3827
q2	2236	388	319	319
q3	10041	1391	794	794
q4	4677	473	349	349
q5	7455	835	544	544
q6	178	171	136	136
q7	752	807	620	620
q8	9446	1443	1464	1443
q9	6000	4235	4219	4219
q10	6920	1341	1016	1016
q11	443	269	244	244
q12	644	427	293	293
q13	18094	2666	2003	2003
q14	274	261	233	233
q15	q16	739	719	689	689
q17	1883	1056	1010	1010
q18	6626	5609	5545	5545
q19	1876	1264	989	989
q20	504	407	271	271
q21	5786	2949	2726	2726
q22	433	351	294	294
Total cold run time: 102633 ms
Total hot run time: 27564 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4295	4113	4053	4053
q2	723	552	516	516
q3	4500	4929	4273	4273
q4	2227	2329	1444	1444
q5	4209	4110	4069	4069
q6	225	171	128	128
q7	1734	1615	1478	1478
q8	2232	1901	1885	1885
q9	6951	6849	6842	6842
q10	3657	3562	3099	3099
q11	525	379	363	363
q12	724	722	515	515
q13	2300	2592	2020	2020
q14	277	293	270	270
q15	q16	669	695	609	609
q17	7325	6747	6709	6709
q18	11920	11129	11772	11129
q19	1138	1001	1063	1001
q20	2217	2219	1934	1934
q21	4962	4202	4299	4202
q22	514	453	429	429
Total cold run time: 63324 ms
Total hot run time: 56968 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 153443 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 1e312344e94a16464f6c764a6fbccee63143b6b5, data reload: false

query5	4301	604	487	487
query6	441	228	206	206
query7	4818	579	297	297
query8	327	178	169	169
query9	8857	4064	4007	4007
query10	493	313	265	265
query11	5858	3529	3229	3229
query12	142	88	87	87
query13	1273	586	434	434
query14	6484	4577	4291	4291
query14_1	3966	4039	3967	3967
query15	201	194	189	189
query16	993	450	424	424
query17	913	668	565	565
query18	2456	471	341	341
query19	208	181	140	140
query20	104	84	80	80
query21	230	140	123	123
query22	13089	13126	12797	12797
query23	14086	12971	12372	12372
query23_1	12521	12583	12488	12488
query24	7233	1149	631	631
query24_1	653	708	753	708
query25	571	443	390	390
query26	1293	305	176	176
query27	2685	570	337	337
query28	4546	1993	2005	1993
query29	1610	744	542	542
query30	316	223	202	202
query31	918	760	631	631
query32	159	104	97	97
query33	550	327	267	267
query34	1222	1166	665	665
query35	758	763	658	658
query36	796	815	714	714
query37	146	113	96	96
query38	1847	1765	1695	1695
query39	709	708	674	674
query39_1	660	672	653	653
query40	233	130	105	105
query41	74	70	69	69
query42	105	96	98	96
query43	365	377	342	342
query44	1393	723	750	723
query45	191	181	166	166
query46	1074	1159	758	758
query47	1491	1488	1404	1404
query48	405	430	305	305
query49	603	410	296	296
query50	909	344	274	274
query51	10494	10468	10429	10429
query52	90	93	78	78
query53	243	266	186	186
query54	283	229	197	197
query55	84	80	73	73
query56	259	229	219	219
query57	1413	1331	1321	1321
query58	323	263	269	263
query59	2059	2087	1888	1888
query60	289	264	244	244
query61	177	175	169	169
query62	470	318	270	270
query63	210	185	186	185
query64	2797	1034	827	827
query65	3510	3416	3448	3416
query66	1818	421	309	309
query67	20068	20261	20000	20000
query68	3413	1562	928	928
query69	441	324	266	266
query70	935	848	832	832
query71	307	241	213	213
query72	2712	2502	2321	2321
query73	816	734	404	404
query74	4693	4501	4292	4292
query75	2288	2370	1958	1958
query76	2385	1126	783	783
query77	382	405	305	305
query78	9097	9112	8533	8533
query79	1348	1207	765	765
query80	770	476	370	370
query81	555	320	282	282
query82	841	176	129	129
query83	285	226	193	193
query84	319	142	112	112
query85	995	516	397	397
query86	385	243	234	234
query87	2014	1983	1850	1850
query88	3703	2748	2727	2727
query89	372	279	245	245
query90	1799	194	184	184
query91	174	157	128	128
query92	108	90	92	90
query93	1512	1413	856	856
query94	617	344	266	266
query95	684	370	334	334
query96	1059	776	341	341
query97	2439	2436	2365	2365
query98	168	150	144	144
query99	719	729	610	610
Total cold run time: 237856 ms
Total hot run time: 153443 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.10	0.05	0.04
query3	0.27	0.13	0.13
query4	1.61	0.14	0.13
query5	0.24	0.22	0.22
query6	1.18	0.96	0.89
query7	0.05	0.01	0.01
query8	0.06	0.03	0.04
query9	0.39	0.32	0.33
query10	0.54	0.57	0.54
query11	0.20	0.15	0.14
query12	0.18	0.14	0.14
query13	0.46	0.46	0.47
query14	0.98	0.95	0.94
query15	0.62	0.57	0.58
query16	0.31	0.30	0.30
query17	1.04	1.04	1.07
query18	0.21	0.20	0.20
query19	1.98	1.94	1.89
query20	0.03	0.01	0.01
query21	15.46	0.18	0.15
query22	4.97	0.06	0.05
query23	16.09	0.31	0.12
query24	3.06	0.40	0.34
query25	0.11	0.04	0.04
query26	0.76	0.20	0.14
query27	0.05	0.03	0.04
query28	3.52	0.84	0.37
query29	12.53	3.96	3.18
query30	0.28	0.16	0.15
query31	2.77	0.55	0.31
query32	3.22	0.59	0.48
query33	3.21	3.12	3.29
query34	15.50	3.92	3.27
query35	3.24	3.24	3.21
query36	0.57	0.42	0.41
query37	0.09	0.06	0.07
query38	0.05	0.04	0.04
query39	0.04	0.02	0.03
query40	0.17	0.14	0.16
query41	0.10	0.03	0.03
query42	0.04	0.04	0.04
query43	0.05	0.04	0.03
Total cold run time: 96.34 s
Total hot run time: 23.74 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (1/1) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 64.09% (29819/46526)
Line Coverage 48.71% (310805/638039)
Region Coverage 44.21% (250595/566851)
Branch Coverage 45.78% (116503/254507)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (1/1) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.30% (34376/45051)
Line Coverage 61.25% (386307/630750)
Region Coverage 57.59% (325150/564555)
Branch Coverage 58.42% (148232/253714)

@yiguolei

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.

Static review conclusion: request changes — one blocking exception-boundary issue.

Critical checkpoints:

  • Goal and correctness: the one-line change is focused and enforces the supplier invariant on ordinary release paths, but it does not universally turn violations into query errors. A later sorted-Iceberg spill refill can throw outside any catch and terminate the BE.
  • Parallel paths: exchange, local merge sort, and generic spill sort refills run beneath the pipeline scheduler's exception boundary; the asynchronous Iceberg writer close path does not. All current supplier registrations and both merger refill sites were traced.
  • Concurrency and lifecycle: no new shared state, locking, ownership, or static-initialization issue was found. The blocking issue is exception lifecycle across the generic async-writer thread, which also bypasses writer-status publication and ready-to-finish signaling.
  • Configuration, compatibility, persistence, and data contracts: no configuration, FE/BE protocol, function symbol, storage format, transaction, journal/replay, or rolling-upgrade change is introduced.
  • Performance and observability: the success-path predicate runs once per supplied block and has negligible cost. Fatal status/stack information is adequate when caught, but the accepted path has no boundary that can publish it as a query status.
  • Tests: existing merger tests cover valid ready/pending and EOF flows, and DorisCheckTest covers build-mode behavior, but there is no valid-first/empty-non-EOF refill test for the async caller. Exact-head CI reports compile and BE UT success; this review was static only and did not run builds or tests, as required by the review environment.
  • User focus: no additional focus points were supplied.

The single accepted issue is attached inline; no other substantiated or unresolved point remains after two converged review rounds.

block->clear();
THROW_IF_ERROR(_block_supplier(block.get(), &_is_eof));
DCHECK(!block->empty() or _is_eof);
DORIS_CHECK(!block->empty() || _is_eof);

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] Preserve a catch boundary for refill checks

DORIS_CHECK throws a doris::Exception in release builds. Initial cursor fetches are caught by VSortedRunMerger::prepare, and pipeline refill calls are caught by TaskScheduler, but sorted Iceberg partition close runs _combine_files_output() / _do_intermediate_merge() from AsyncResultWriter's generic thread-pool task. Those paths call VSortedRunMerger::get_next() for later refills without any catch, and neither the writer lambda nor ThreadPool catches exceptions. If this invariant fires there (for example, a valid first spill block followed by a malformed zero-length entry), the exception crosses the thread entry and invokes std::terminate, crashing the BE instead of reporting the query error described by this PR. Please catch and convert refill exceptions centrally in VSortedRunMerger::get_next() (covering both refill sites), and add a release-mode test with a valid first block followed by an empty non-EOF block.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants