Skip to content

[fix](bm25) Collect collection statistics over the whole collection, not one scanner's slice#66047

Open
airborne12 wants to merge 2 commits into
apache:masterfrom
airborne12:fix-collection-statistics-full-scope
Open

[fix](bm25) Collect collection statistics over the whole collection, not one scanner's slice#66047
airborne12 wants to merge 2 commits into
apache:masterfrom
airborne12:fix-collection-statistics-full-scope

Conversation

@airborne12

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

BM25 idf and avg_dl are properties of the whole collection, but OlapScanner built them from
_tablet_reader_params.rs_splits — the slice of the read source that this scanner was handed:

// be/src/exec/scan/olap_scanner.cpp (before)
if (_tablet_reader_params.score_runtime) {
    _tablet_reader_params.collection_statistics = std::make_shared<CollectionStatistics>();
    RETURN_IF_ERROR(_tablet_reader_params.collection_statistics->collect(
            _state, _tablet_reader_params.rs_splits, ...));   // <-- only this scanner's rowsets
}

ParallelScannerBuilder divides a tablet's entire_read_source.rs_splits into several
partitial_read_source.rs_splits, one per scanner. So as soon as parallel scan produced more than
one scanner for a tablet, each scanner computed idf over a different subset of rowsets, and
score() depended on how the tablet happened to be split — rows served by a scanner owning few
rowsets scored differently from identical rows served by a scanner owning many.

This affects any inverted index format that supports BM25 scoring.

Fix. The read source is captured before it is sliced, and statistics are collected once per
tablet over all of its rowsets. CollectionStatisticsBuildState performs the sharing: the first
scanner to arrive builds while the others wait on a condition variable, then each receives its own
clone. The collected counts live in an immutable block that clones share; only the lazily memoized
idf/avg_dl maps are per-scanner, so the read path needs no locking. A failed or throwing build
is recorded and handed to the waiters rather than retried once per scanner.

The sharing is not incidental — without it this would be a straight regression, since every scanner
would repeat the full-collection walk that previously covered only its own slice.

Release note

Fix score() returning different BM25 scores for the same row depending on how many scanners the
tablet's read source was split into. Collection statistics are now computed over the entire
collection and shared by all scanners of a tablet.

Check List (For Author)

  • Test

    • Regression test

      regression-test/suites/inverted_index_p0/test_bm25_score_parallel_scan.groovy loads a V3
      inverted index table as several rowsets, then compares ORDER BY score() LIMIT 20 with
      enable_parallel_scan = false against the same query forced onto many scanners
      (parallel_scan_min_rows_per_scanner = 16, parallel_scan_max_scanners_count = 16).
      The two result sets must be identical.

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

      score() values change for queries that were previously served by more than one scanner per
      tablet. The new values are the correct collection-wide ones; the old values varied with the
      scanner split. Relative ranking within a single scanner was already consistent, so this mainly
      affects absolute scores and cross-scanner ordering.

  • Does this need documentation?

    • No.

Check List (For Reviewer who merge this PR)

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

…not one scanner's slice

BM25 idf and avg_dl are collection-level quantities. OlapScanner built them from
`_tablet_reader_params.rs_splits`, which is the slice of the read source that scanner was handed --
and ParallelScannerBuilder divides one tablet's read source among several scanners. So as soon as
parallel scan produced more than one scanner per tablet, each scanner derived idf from a different
subset of rowsets, and score() depended on how the tablet happened to be split. Rows served by a
scanner that owned few rowsets scored differently from identical rows served by a scanner that
owned many.

The read source is now captured before it is sliced, and the statistics are collected once per
tablet over all of its rowsets. CollectionStatisticsBuildState does the sharing: the first scanner
to arrive builds while the rest wait on a condition variable, then every scanner receives its own
clone. The collected counts live in an immutable block the clones share; only the lazily memoized
idf/avg_dl maps are per-scanner, so no locking is needed on the read path. A failed or throwing
build is recorded and handed to the waiters instead of being retried once per scanner.

Without the sharing this would be a straight regression: every scanner would repeat the
full-collection walk that previously covered only its own slice.
@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?

@airborne12

Copy link
Copy Markdown
Member Author

run buildall

airborne12 added a commit to airborne12/apache-doris that referenced this pull request Jul 25, 2026
…n change

The full-collection scope and CollectionStatisticsBuildState were bundled into the SNII branch, but
neither is SNII-specific: OlapScanner collected BM25 statistics from the scanner's own rs_splits
while ParallelScannerBuilder divides a tablet's read source among scanners, so idf depended on how
the tablet was split for every inverted index format, not just SNII. Fixing that here made the SNII
change carry an unrelated correctness fix and made both harder to review.

The scope fix and the single-flight sharing now live in apache#66047 against master, together
with the three concurrency tests that covered them. This branch drops its copy: the exec/scan files
go back to master byte for byte, and collection_statistics loses clone_for_scanner(),
freeze_for_scanners(), the shared base block and the build state.

Nothing SNII-specific is lost. collect() remains a thin adapter over collect_full_collection(), so
every SNII segment path still runs; SNII simply inherits master's per-scanner behaviour until
apache#66047 lands, at which point this branch picks the fix up from master on the next rebase.
…rency

Three tests for what the sharing has to guarantee when several scanners of one tablet reach it at
once, each driving the real threads through a barrier rather than simulating the interleaving:

- eight scanners arrive together and exactly one builder runs, and all eight get usable statistics;
- a build that fails is reported to every waiter, and the failure is not rebuilt per scanner;
- clones share the collected counts but keep their own lazily memoized avg_dl, so one scanner
  seeding its cache cannot change what another scanner computes.
@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17602	4227	4078	4078
q2	2207	343	206	206
q3	10090	1394	813	813
q4	4715	478	336	336
q5	7717	866	573	573
q6	269	169	139	139
q7	776	825	612	612
q8	10694	1525	1667	1525
q9	6091	4351	4337	4337
q10	6849	1751	1483	1483
q11	516	356	334	334
q12	763	584	456	456
q13	18115	3799	2734	2734
q14	267	272	241	241
q15	q16	795	792	712	712
q17	969	1067	1001	1001
q18	6953	5827	5651	5651
q19	1163	1224	1060	1060
q20	797	687	583	583
q21	5587	2674	2329	2329
q22	426	360	300	300
Total cold run time: 103361 ms
Total hot run time: 29503 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4455	4429	4464	4429
q2	298	323	215	215
q3	4549	4953	4413	4413
q4	2072	2182	1374	1374
q5	4387	4253	4312	4253
q6	224	180	124	124
q7	2336	1885	1623	1623
q8	2507	2106	2115	2106
q9	8119	7758	7825	7758
q10	4672	4631	4171	4171
q11	561	555	372	372
q12	744	762	541	541
q13	3411	3536	3025	3025
q14	313	339	275	275
q15	q16	711	717	649	649
q17	1393	1338	1355	1338
q18	8075	7419	6955	6955
q19	1111	1099	1123	1099
q20	2229	2226	1919	1919
q21	5264	4552	4451	4451
q22	551	453	406	406
Total cold run time: 57982 ms
Total hot run time: 51496 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 178318 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 3c4fef3c624377bdc86de0a071c44dea21e52648, data reload: false

query5	4333	629	483	483
query6	467	226	204	204
query7	4902	584	333	333
query8	365	197	172	172
query9	8783	4098	4132	4098
query10	507	377	313	313
query11	5897	2352	2124	2124
query12	158	103	102	102
query13	1268	643	416	416
query14	6271	5184	4906	4906
query14_1	4242	4265	4231	4231
query15	220	211	184	184
query16	1007	489	511	489
query17	1164	740	571	571
query18	2457	497	351	351
query19	215	191	154	154
query20	121	114	109	109
query21	246	164	137	137
query22	13539	13510	13379	13379
query23	17473	16594	16187	16187
query23_1	16240	16230	16232	16230
query24	7752	1782	1289	1289
query24_1	1301	1302	1292	1292
query25	580	469	397	397
query26	1350	385	212	212
query27	2612	585	389	389
query28	4439	2043	2033	2033
query29	1090	639	514	514
query30	352	278	237	237
query31	1146	1117	1002	1002
query32	115	62	64	62
query33	548	338	265	265
query34	1217	1179	656	656
query35	823	844	672	672
query36	1190	1162	1050	1050
query37	165	108	94	94
query38	1922	1724	1658	1658
query39	899	894	850	850
query39_1	841	842	867	842
query40	300	163	150	150
query41	64	62	62	62
query42	90	91	91	91
query43	332	338	299	299
query44	1502	784	762	762
query45	196	183	178	178
query46	1130	1275	745	745
query47	2135	2061	2023	2023
query48	411	397	295	295
query49	590	438	315	315
query50	1054	426	341	341
query51	10778	10728	11096	10728
query52	89	87	74	74
query53	270	279	207	207
query54	281	245	225	225
query55	76	73	67	67
query56	307	303	298	298
query57	1324	1270	1210	1210
query58	296	258	249	249
query59	1624	1711	1448	1448
query60	328	276	239	239
query61	144	147	146	146
query62	551	495	432	432
query63	244	203	193	193
query64	2860	1019	895	895
query65	4675	4604	4643	4604
query66	1866	531	373	373
query67	29254	29094	29003	29003
query68	3238	1596	984	984
query69	448	317	269	269
query70	1080	960	960	960
query71	383	341	323	323
query72	3147	2786	2377	2377
query73	873	761	415	415
query74	5101	4926	4700	4700
query75	2689	2598	2155	2155
query76	2317	1281	770	770
query77	374	407	289	289
query78	11915	11837	11196	11196
query79	1557	1189	755	755
query80	1273	545	464	464
query81	530	343	291	291
query82	702	162	121	121
query83	378	330	310	310
query84	289	163	134	134
query85	996	622	545	545
query86	495	280	290	280
query87	1851	1833	1744	1744
query88	3835	2827	2777	2777
query89	446	390	327	327
query90	1908	199	200	199
query91	206	193	165	165
query92	68	61	59	59
query93	1782	1526	994	994
query94	757	351	323	323
query95	787	528	572	528
query96	1064	772	360	360
query97	2670	2620	2494	2494
query98	214	216	204	204
query99	1151	1122	980	980
Total cold run time: 265960 ms
Total hot run time: 178318 ms

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17661	4090	4085	4085
q2	2008	309	195	195
q3	10349	1393	830	830
q4	4676	467	335	335
q5	7481	849	553	553
q6	183	179	144	144
q7	754	832	609	609
q8	9369	1537	1531	1531
q9	5944	4344	4311	4311
q10	6806	1730	1488	1488
q11	504	356	325	325
q12	738	571	455	455
q13	18472	3391	2743	2743
q14	271	262	238	238
q15	q16	777	775	702	702
q17	958	987	1064	987
q18	6766	5601	5505	5505
q19	1376	1232	1016	1016
q20	755	675	580	580
q21	5685	2660	2310	2310
q22	436	360	297	297
Total cold run time: 101969 ms
Total hot run time: 29239 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4434	4333	4337	4333
q2	299	320	217	217
q3	4595	4932	4380	4380
q4	2097	2172	1381	1381
q5	4417	4297	4244	4244
q6	228	178	129	129
q7	1712	1697	1896	1697
q8	2536	2125	2125	2125
q9	7746	7790	7697	7697
q10	4730	4667	4162	4162
q11	573	440	377	377
q12	774	766	550	550
q13	3540	3577	2924	2924
q14	325	312	296	296
q15	q16	742	731	685	685
q17	1388	1302	1309	1302
q18	8210	7476	6777	6777
q19	1117	1059	1095	1059
q20	2210	2207	1932	1932
q21	5277	4601	4428	4428
q22	534	456	403	403
Total cold run time: 57484 ms
Total hot run time: 51098 ms

@hello-stephen

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

query5	4324	641	476	476
query6	465	241	207	207
query7	4884	637	344	344
query8	330	189	169	169
query9	8800	4111	4070	4070
query10	516	371	308	308
query11	5908	2318	2117	2117
query12	162	105	100	100
query13	1266	624	439	439
query14	6390	5308	4985	4985
query14_1	4351	4350	4279	4279
query15	222	208	184	184
query16	1016	475	458	458
query17	1163	675	535	535
query18	2433	484	335	335
query19	199	185	141	141
query20	107	115	106	106
query21	232	158	130	130
query22	13689	13580	13359	13359
query23	17070	16487	16126	16126
query23_1	16127	16137	16205	16137
query24	7753	1721	1283	1283
query24_1	1308	1282	1289	1282
query25	537	444	358	358
query26	1341	357	220	220
query27	2595	623	375	375
query28	4446	2001	1967	1967
query29	1079	628	471	471
query30	340	264	226	226
query31	1105	1113	974	974
query32	108	60	60	60
query33	519	310	244	244
query34	1160	1157	647	647
query35	774	772	696	696
query36	1189	1169	1033	1033
query37	151	106	91	91
query38	1877	1716	1668	1668
query39	879	862	843	843
query39_1	813	844	851	844
query40	249	170	138	138
query41	66	64	63	63
query42	93	92	91	91
query43	319	323	278	278
query44	1482	773	762	762
query45	195	173	176	173
query46	1047	1208	720	720
query47	2047	2092	1972	1972
query48	400	436	279	279
query49	579	419	298	298
query50	1066	432	326	326
query51	11135	11167	10874	10874
query52	86	86	79	79
query53	254	283	205	205
query54	283	240	218	218
query55	74	69	70	69
query56	299	285	274	274
query57	1276	1281	1196	1196
query58	292	267	252	252
query59	1563	1588	1387	1387
query60	315	271	254	254
query61	148	152	147	147
query62	548	496	421	421
query63	243	207	202	202
query64	2849	1031	835	835
query65	4734	4659	4632	4632
query66	1831	503	370	370
query67	29344	29166	29026	29026
query68	3224	1565	990	990
query69	412	310	271	271
query70	1027	946	963	946
query71	385	335	335	335
query72	2987	2796	2551	2551
query73	817	796	428	428
query74	5066	4945	4746	4746
query75	2533	2532	2136	2136
query76	2325	1221	838	838
query77	368	387	302	302
query78	11638	11749	11163	11163
query79	1224	1221	727	727
query80	667	603	496	496
query81	447	347	289	289
query82	248	159	123	123
query83	329	332	307	307
query84	294	166	131	131
query85	1002	690	630	630
query86	318	284	288	284
query87	1802	1815	1762	1762
query88	3761	2786	2734	2734
query89	424	381	339	339
query90	2206	205	203	203
query91	206	194	161	161
query92	64	60	57	57
query93	1486	1677	1015	1015
query94	555	349	312	312
query95	800	584	479	479
query96	1072	801	335	335
query97	2617	2630	2537	2537
query98	227	206	202	202
query99	1060	1111	962	962
Total cold run time: 262199 ms
Total hot run time: 177908 ms

@hello-stephen

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

query1	0.01	0.01	0.00
query2	0.09	0.05	0.04
query3	0.26	0.14	0.14
query4	1.61	0.13	0.14
query5	0.24	0.22	0.22
query6	1.27	1.06	1.09
query7	0.03	0.01	0.00
query8	0.05	0.04	0.03
query9	0.38	0.31	0.31
query10	0.57	0.59	0.59
query11	0.20	0.13	0.13
query12	0.18	0.15	0.14
query13	0.48	0.47	0.49
query14	1.02	1.03	1.00
query15	0.61	0.60	0.61
query16	0.32	0.33	0.32
query17	1.11	1.14	1.11
query18	0.22	0.21	0.21
query19	2.05	1.97	2.02
query20	0.02	0.01	0.02
query21	15.43	0.21	0.14
query22	4.91	0.06	0.06
query23	16.12	0.33	0.12
query24	2.94	0.41	0.34
query25	0.10	0.05	0.04
query26	0.73	0.21	0.15
query27	0.05	0.04	0.03
query28	3.61	0.96	0.53
query29	12.54	4.19	3.29
query30	0.27	0.15	0.15
query31	2.77	0.60	0.32
query32	3.21	0.59	0.48
query33	3.20	3.24	3.17
query34	15.73	4.20	3.49
query35	3.54	3.52	3.53
query36	0.54	0.43	0.44
query37	0.09	0.07	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.19	0.16	0.16
query41	0.08	0.04	0.03
query42	0.04	0.03	0.03
query43	0.05	0.04	0.03
Total cold run time: 96.95 s
Total hot run time: 25.12 s

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