Skip to content

[fix](be) Avoid signaling unbuilt shared hash table#63345

Open
BiteTheDDDDt wants to merge 1 commit into
apache:masterfrom
BiteTheDDDDt:fix/hashjoin-unbuilt-shared-table-signal
Open

[fix](be) Avoid signaling unbuilt shared hash table#63345
BiteTheDDDDt wants to merge 1 commit into
apache:masterfrom
BiteTheDDDDt:fix/hashjoin-unbuilt-shared-table-signal

Conversation

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary:

For shared hash table hash joins, the builder task may leave process_build_block() before the hash table is actually built, for example when the build side returns an error before the EOS build step completes. In that state the shared hash table variant can still be monostate.

HashJoinBuildSinkLocalState::close() previously set _signaled = true whenever the builder was not terminated. That wakes non-builder tasks even when the shared hash table was never built, so they can reach the shared hash table copy path and report a misleading Hash table type mismatch when share hash table instead of preserving the original build-side error.

This PR gates _signaled on the actual shared hash table state. Non-builder tasks are signaled only when the builder is not terminated and the shared hash table variant is no longer monostate. If the builder closes after an error before the hash table is built, _signaled stays false and non-builders take the existing EOF guard instead of visiting an unbuilt hash table.

Release note

None

Check List (For Author)

  • Test: Unit Test
    • ./run-be-ut.sh --run --filter=SharedHashTableSignalTest.UnbuiltHashTableDoesNotSignal -j 8
    • build-support/check-format.sh
    • build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN was attempted; it reports pre-existing complexity diagnostics in the touched hash join files and jni-util.h static assertions.
  • Behavior changed: No
  • Does this need documentation: No

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: When a shared hash table builder exits before process_build_block() finishes, for example after an error on the build side, the hash table variant can remain monostate while the builder is not marked terminated. The close() path still set _signaled and woke non-builder tasks, which could mask the original error with a misleading shared hash table type mismatch.

This change only signals non-builder tasks after the shared hash table has actually been built, so failure paths keep _signaled false and non-builders return EOF instead of visiting a monostate hash table.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - ./run-be-ut.sh --run --filter=SharedHashTableSignalTest.UnbuiltHashTableDoesNotSignal -j 8
    - build-support/check-format.sh
    - build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN (failed on pre-existing hashjoin_build_sink.cpp/hashjoin_build_sink_test.cpp complexity diagnostics and jni-util.h static assertions)
- Behavior changed: No
- Does this need documentation: No

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 18, 2026 07:07
@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?

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

run buildall

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a misleading error from shared-hash-table joins where non-builder tasks were woken up even when the builder closed before constructing the hash table, causing a confusing "Hash table type mismatch" error instead of preserving the original build-side error.

Changes:

  • Add a hash_table_built() helper that checks whether the shared hash table variant is no longer monostate.
  • Gate _signaled = true on both !_terminated and hash_table_built(_shared_state) so non-builders aren't woken into an unbuilt hash table.
  • Add unit test UnbuiltHashTableDoesNotSignal verifying _signaled stays false and non-builders receive EOF in this scenario.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
be/src/exec/operator/hashjoin_build_sink.cpp Gate _signaled on actual hash-table-built state via new helper.
be/test/exec/operator/hashjoin_build_sink_test.cpp New test covering the unbuilt-hash-table close path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

/review

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

I found one blocking correctness issue in the shared hash table failure path.

Critical checkpoint conclusions:

  • Goal/test: the PR addresses the monostate shared-hash-table wakeup case and adds a targeted unit test, but the current predicate still allows signaling from failed builder paths after method_variant has been initialized.
  • Scope: the change is small and focused, but the readiness predicate is not precise enough for all failure windows.
  • Concurrency: the dependency wakeup path remains lock-protected by the operator mutex, but waking non-builders after an unsuccessful builder close can expose partially initialized shared state.
  • Lifecycle: no new static/global lifecycle issue found.
  • Config/compatibility: no config, protocol, storage format, or rolling-upgrade compatibility concern found.
  • Parallel paths: this applies to the shared broadcast hash table path; I did not find another required code path change.
  • Special checks: the new check needs to distinguish fully successful build completion from merely non-monostate hash method initialization.
  • Tests: the added test covers the monostate-at-close case, but not failure after _hash_table_init()/before full build-side completion.
  • Observability/transactions/data writes/FE-BE variables: not applicable.
  • Performance: no performance regression found.

User focus: no additional user-provided review focus was supplied.

if (!_terminated) {
// Do not wake non-builders into a monostate hash table after early termination/errors.
const auto should_signal = !_terminated && hash_table_built(_shared_state);
if (should_signal) {
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.

This still signals non-builder tasks when the builder is closing after a failed build path as long as _hash_table_init() has already changed the first variant away from std::monostate. For example, in shared broadcast mode _hash_table_init() initializes variants before process_build_block()/build_asof_index() fully complete; if a later variant initialization fails, or build_asof_index() returns an error after the hash table variant is initialized, close(exec_status) will run with _terminated == false and hash_table_built(_shared_state) == true, set _signaled, and wake non-builders to copy/probe shared state from a failed builder. That can still mask the original failure with the shared-table type mismatch or expose incomplete ASOF state. Please gate signaling on successful builder completion as well, for example by checking exec_status.ok() or by setting an explicit build_completed flag only after all build-side steps succeed.

@hello-stephen
Copy link
Copy Markdown
Contributor

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

------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	17640	3904	3866	3866
q2	q3	10838	1362	791	791
q4	4681	484	345	345
q5	7577	2258	2107	2107
q6	246	179	138	138
q7	966	757	635	635
q8	9363	1693	1591	1591
q9	5127	4906	4912	4906
q10	6374	2070	1804	1804
q11	441	277	244	244
q12	638	420	297	297
q13	18132	3389	2792	2792
q14	260	256	233	233
q15	q16	819	769	695	695
q17	999	1012	985	985
q18	7036	5840	5539	5539
q19	1313	1298	1095	1095
q20	492	439	357	357
q21	6359	2859	2685	2685
q22	464	371	432	371
Total cold run time: 99765 ms
Total hot run time: 31476 ms

----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL	
============================================
q1	4572	4631	4556	4556
q2	q3	4874	5326	4602	4602
q4	2128	2203	1405	1405
q5	4910	4624	4632	4624
q6	224	174	127	127
q7	1923	1675	1531	1531
q8	2384	2054	2064	2054
q9	7720	7397	7252	7252
q10	4504	4428	4035	4035
q11	541	376	353	353
q12	790	736	517	517
q13	2972	3391	2761	2761
q14	271	273	253	253
q15	q16	671	700	619	619
q17	1259	1237	1235	1235
q18	7574	6909	6954	6909
q19	1101	1096	1088	1088
q20	2208	2193	1941	1941
q21	5339	4666	4501	4501
q22	503	447	433	433
Total cold run time: 56468 ms
Total hot run time: 50796 ms

@hello-stephen
Copy link
Copy Markdown
Contributor

TPC-DS: Total hot run time: 170385 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 fd8064f6c8442e789af4639037b21407d161c31b, data reload: false

query5	4337	664	530	530
query6	334	216	197	197
query7	4440	570	303	303
query8	331	243	225	225
query9	8819	4068	4017	4017
query10	463	339	303	303
query11	5798	2369	2231	2231
query12	182	129	126	126
query13	1327	592	438	438
query14	5888	5338	5021	5021
query14_1	4344	4314	4312	4312
query15	206	201	181	181
query16	990	447	421	421
query17	971	728	578	578
query18	2437	473	352	352
query19	224	195	175	175
query20	135	132	129	129
query21	228	143	120	120
query22	13755	13497	13432	13432
query23	17324	16404	16096	16096
query23_1	16271	16175	16302	16175
query24	7697	1817	1299	1299
query24_1	1302	1283	1300	1283
query25	598	510	451	451
query26	1340	321	177	177
query27	2724	558	348	348
query28	4467	1951	1950	1950
query29	1022	671	534	534
query30	307	242	199	199
query31	1109	1075	947	947
query32	93	85	77	77
query33	564	370	302	302
query34	1182	1156	640	640
query35	780	792	687	687
query36	1370	1395	1189	1189
query37	160	108	94	94
query38	3221	3146	3069	3069
query39	938	928	913	913
query39_1	893	881	869	869
query40	242	156	138	138
query41	75	69	70	69
query42	118	115	114	114
query43	325	330	288	288
query44	
query45	223	205	212	205
query46	1062	1228	737	737
query47	2342	2350	2288	2288
query48	410	415	303	303
query49	655	520	410	410
query50	1032	353	253	253
query51	4349	4348	4316	4316
query52	108	107	101	101
query53	255	291	210	210
query54	324	286	269	269
query55	96	91	93	91
query56	334	332	327	327
query57	1417	1419	1347	1347
query58	327	293	284	284
query59	1576	1661	1469	1469
query60	315	333	308	308
query61	155	158	157	157
query62	681	636	562	562
query63	247	209	203	203
query64	2413	800	633	633
query65	
query66	1765	483	355	355
query67	30101	30017	29878	29878
query68	
query69	466	349	304	304
query70	1005	1027	1000	1000
query71	313	271	269	269
query72	2926	2702	2457	2457
query73	808	734	430	430
query74	5052	4922	4742	4742
query75	2673	2652	2249	2249
query76	2328	1144	752	752
query77	396	411	351	351
query78	12137	12281	11683	11683
query79	1456	1038	736	736
query80	642	547	451	451
query81	466	283	238	238
query82	1345	161	126	126
query83	350	274	251	251
query84	313	140	111	111
query85	900	560	475	475
query86	388	337	297	297
query87	3435	3346	3243	3243
query88	3505	2687	2629	2629
query89	446	402	335	335
query90	1944	182	193	182
query91	185	169	138	138
query92	82	78	73	73
query93	1625	1455	890	890
query94	531	366	314	314
query95	693	407	354	354
query96	1055	782	312	312
query97	2743	2739	2556	2556
query98	243	233	227	227
query99	1142	1097	986	986
Total cold run time: 254126 ms
Total hot run time: 170385 ms

@hello-stephen
Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (8/8) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 71.85% (27151/37790)
Line Coverage 55.25% (289349/523705)
Region Coverage 52.54% (241616/459836)
Branch Coverage 53.73% (103887/193356)

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.

3 participants