Skip to content

[feature](be) Add OLAP scan filter profile counters#64165

Open
BiteTheDDDDt wants to merge 1 commit into
apache:masterfrom
BiteTheDDDDt:codex/scan-filter-profile
Open

[feature](be) Add OLAP scan filter profile counters#64165
BiteTheDDDDt wants to merge 1 commit into
apache:masterfrom
BiteTheDDDDt:codex/scan-filter-profile

Conversation

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor

@BiteTheDDDDt BiteTheDDDDt commented Jun 6, 2026

What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary: Existing OLAP scan profiles only expose aggregate filter counters, making it hard to understand the selectivity of each conjunct, runtime filter, key range, and index-related filtering step. This PR adds scan-local filter IDs and materializes detailed ScanFilterInfo and KeyRangeInfo profile sections. Per-filter counters now show input rows, output rows, filtered rows, stage participation, source expression or runtime filter debug text, and runtime filter wait/always-true data where applicable. Runtime filter dynamic partition pruning is exposed as a separate ScanFilterInfo child because it prunes partitions/tablets before segment-level row filtering. Empty mechanism-specific fields are omitted when they do not participate, and runtime filter IDs remain distinct from scan filter IDs.

Release note

Add detailed OLAP scan filter profile counters in query profile.

Check List (For Author)

  • Test: Manual test
    • ./build.sh --be
    • git diff --cached --check
  • Behavior changed: Yes. Query profile includes detailed OLAP ScanFilterInfo and KeyRangeInfo counters.
  • Does this need documentation: No

Copilot AI review requested due to automatic review settings June 6, 2026 03:28
@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?

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

This PR introduces a new BE-side profiling framework to expose detailed, per-filter selectivity for OLAP scans, aiming to make each conjunct/runtime filter/key-range/index/execution-stage filtering step visible in the query profile via ScanFilterInfo and KeyRangeInfo.

Changes:

  • Add ScanFilterProfile/ScanFilterStats infrastructure and materialize per-filter/stage counters into RuntimeProfile.
  • Plumb scan-local filter handles through scan operator normalization, storage predicates, index filtering (zone map/bloom/dict/inverted/ANN), and execution-stage conjunct evaluation.
  • Extend runtime filter consumer profiling to publish RF wait/always-true/debug metadata into the unified scan-filter profile structure.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
design/scan-filter-profile.md Design draft describing the intended per-filter/stage profiling model and semantics.
be/src/runtime/scan_filter_profile.h Declares scan-local filter kinds/stages, stats, handles, and profile materialization API.
be/src/runtime/scan_filter_profile.cpp Implements stats recording, snapshots, sorting, and profile materialization to ScanFilterInfo/KeyRangeInfo.
be/src/exec/operator/scan_operator.h Adds ScanFilterProfile ownership on scan local state and registration helper.
be/src/exec/operator/scan_operator.cpp Creates scan filter profile based on profile_level, attaches handles to conjuncts/pushdowns, and materializes on close.
be/src/exec/operator/olap_scan_operator.h Adds key-range synthetic filter handle storage in OLAP scan local state.
be/src/exec/operator/olap_scan_operator.cpp Registers synthetic key-range filter and tracks source filter lineage for key ranges.
be/src/exec/scan/olap_scanner.cpp Plumbs scan filter profile and key-range handle into tablet reader params.
be/src/storage/tablet/tablet_reader.h Extends reader params to carry scan filter profile + key-range synthetic handle.
be/src/storage/tablet/tablet_reader.cpp Propagates scan filter fields into reader context; attaches handles to function-filter predicates.
be/src/storage/rowset/rowset_reader_context.h Plumbs scan filter profile + key-range handle through rowset reader context.
be/src/storage/rowset/beta_rowset_reader.cpp Propagates scan filter fields into storage read options for segment iterators.
be/src/storage/iterators.h Adds scan filter profile + key-range handle fields to StorageReadOptions.
be/src/storage/predicate/column_predicate.h Adds ScanFilterHandle attachment/storage on column predicates.
be/src/storage/predicate/block_column_predicate.h Exposes scan-filter handle from block predicates; adds scan-filter-aware AND evaluation methods.
be/src/storage/predicate/block_column_predicate.cpp Implements scan-filter-aware evaluation wrappers that record per-predicate stage stats.
be/src/storage/segment/segment_iterator.h Avoids emitting old predicate debug sections when scan-filter profiling is enabled.
be/src/storage/segment/segment_iterator.cpp Records per-filter stats for key range, inverted/ANN, vector/short-circuit predicates, and common expr execution.
be/src/storage/segment/column_reader.h Extends zone-map filtering helper API to accept input row ranges.
be/src/storage/segment/column_reader.cpp Adds per-page input-row counting and per-filter stage recording for zone map/bloom/dict paths.
be/src/exprs/vexpr_context.h Adds scan-filter handle attachment; adds optional stage parameter to conjunct execution/filter helpers.
be/src/exprs/vexpr_context.cpp Records per-conjunct stage input/output rows during conjunct execution when stage is provided.
be/src/exec/scan/scanner.cpp Passes residual stage tag into VExprContext::filter_block() for profiling.
be/src/exec/runtime_filter/runtime_filter_consumer.h Adds API to publish runtime-filter stats into scan-filter profiling.
be/src/exec/runtime_filter/runtime_filter_consumer.cpp Implements publishing RF wait/always-true/debug stats to ScanFilterProfile.
be/src/exec/runtime_filter/runtime_filter_consumer_helper.h Extends realtime profile collection API to optionally target scan-filter profiling.
be/src/exec/runtime_filter/runtime_filter_consumer_helper.cpp Publishes RF acquire time + per-RF stats to ScanFilterProfile when provided.

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

Comment thread be/src/exprs/vexpr_context.cpp
Comment thread be/src/storage/segment/segment_iterator.cpp
Comment thread be/src/storage/segment/column_reader.cpp
Comment thread be/src/exec/operator/olap_scan_operator.cpp
Comment thread be/src/storage/segment/column_reader.cpp Outdated
Comment thread be/src/storage/segment/column_reader.cpp Outdated
@BiteTheDDDDt BiteTheDDDDt force-pushed the codex/scan-filter-profile branch from 5f54636 to 62dd05d Compare June 6, 2026 03:59
@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

/review

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

run buildall

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 observability regression in the new profile path. Existing review comments about scan-filter overhead, pass-through zone-map accounting, missing handles, and incremental inverted-index stats appear addressed in the current head, so I did not repeat them.

Critical checkpoint conclusions:

  • Goal/test: The PR adds per-filter OLAP scan profile counters and mostly wires them through key range, index, vector, short-circuit, common-expr, residual, and runtime-filter paths. The runtime-filter profile fallback is incomplete for filters that do not become scan predicates. The PR currently reports only manual build/check testing; no automated test validates profile materialization or the timeout/disabled runtime-filter case.
  • Scope/clarity: The implementation is focused on profile plumbing and keeps data-path behavior mostly unchanged, but it replaces the old runtime-filter profile output when scan-filter profiling is enabled.
  • Concurrency/lifecycle: New shared profile state uses a mutex for descriptor vectors and relaxed atomics for counters, which is appropriate for statistics. Scanner shutdown happens before final materialization. No new lock-order issue found.
  • Compatibility/config: No storage/protocol/config compatibility issue found. Profile output changes are user-visible.
  • Parallel paths: OLAP scan storage, residual expression, common expr, runtime filter, and key-range paths were reviewed. Non-OLAP scan types still use the base scan profile behavior.
  • Data correctness/transactions: No data visibility, transaction, delete-bitmap, or persistence impact found.
  • Performance: The previously raised no-handle overhead paths are now guarded in current head. Some profile-enabled counting remains expected overhead.
  • Observability: The new profile improves per-filter visibility, but currently drops per-runtime-filter details for runtime filters that never materialize as scan filter expressions.

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

Comment thread be/src/exec/runtime_filter/runtime_filter_consumer_helper.cpp
@BiteTheDDDDt BiteTheDDDDt force-pushed the codex/scan-filter-profile branch from 62dd05d to a4737f3 Compare June 6, 2026 04:58
@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

/review

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

run buildall

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 correctness issue in the new scan-filter profiling path.

Critical checkpoint conclusions:

  • Goal/test: the PR adds unified scan-filter profiling for pushed-down, index, key-range, runtime-filter, and residual scan predicates. The implementation covers the main paths, but the late-arrival runtime-filter path can miss newly appended filters when cost-based conjunct ordering is enabled. No test coverage was found for late-arriving RFs with conjunct reordering and profile-level scan-filter output.
  • Scope/minimality: the change is mostly focused on profiling, but it touches hot scan/storage paths; previously raised overhead issues are already covered by existing threads.
  • Concurrency/lifecycle: scan-filter stats use relaxed atomics for statistics, which is appropriate. Late RF append is protected by _conjuncts_lock, but the registration logic uses unstable vector indexes after sorting.
  • Config/compatibility: no new config or storage/protocol compatibility issue found.
  • Parallel paths: OLAP residual/index/key-range paths are covered, but the late-arrival RF path is a distinct missed path.
  • Tests: no additional user focus was provided; for that focus, no extra issue applies. The missing late-arrival/reorder profile test is the main coverage gap.
  • Observability: the issue is an observability regression in the new profile itself, because the RF can execute as a residual predicate while being shown as not applied/no per-stage stats.
  • Data correctness/transactions/memory: no data visibility, transaction, MoW delete-bitmap, or memory-safety blocking issue found in the reviewed changes.

Please fix the late-arrival handle assignment so newly appended RF contexts are registered before any full-vector reorder, or track the appended contexts directly instead of relying on [conjuncts_before, size) after sorting.

Comment thread be/src/exec/operator/scan_operator.cpp
### What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary: Existing OLAP scan profiles only expose aggregate filter counters, making it hard to understand the selectivity of each conjunct, runtime filter, key range, and index-related filtering step. This change adds scan-local filter ids and materializes detailed ScanFilterInfo and KeyRangeInfo profile sections. Per-filter counters now show input rows, output rows, filtered rows, stage participation, source expression or runtime filter debug text, and runtime filter wait or always-true data where applicable. Runtime filter dynamic partition pruning is exposed as a separate ScanFilterInfo child because it prunes partitions/tablets before segment-level row filtering. Empty mechanism-specific fields are omitted when they do not participate, and runtime filter ids remain distinct from scan filter ids.

### Release note

Add detailed OLAP scan filter profile counters in query profile.

### Check List (For Author)

- Test: Manual test
    - ./build.sh --be
    - git diff --cached --check
- Behavior changed: Yes. Query profile includes detailed OLAP ScanFilterInfo and KeyRangeInfo counters.
- Does this need documentation: No
@BiteTheDDDDt BiteTheDDDDt force-pushed the codex/scan-filter-profile branch from a4737f3 to dd78f25 Compare June 6, 2026 05:41
@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

/review

@BiteTheDDDDt
Copy link
Copy Markdown
Contributor Author

run buildall

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 remaining blocking issue in the current PR diff: native file-scan predicates that are applied inside Parquet/ORC readers are not integrated with the new scan-filter profiling stages, so their real filtering is invisible and later residual counters are misleading.

Critical checkpoint conclusions:

  • Goal/test coverage: the scan-filter profiling goal is only partially achieved; OLAP paths are covered more deeply, but native file-reader pushed-down predicate paths are not proven and currently report incorrect stage rows.
  • Scope/minimality: the scan-filter changes are focused, but one parallel scan path was missed.
  • Concurrency/lifecycle: no new blocking concurrency or lifecycle issue found beyond already-known review threads.
  • Configuration/compatibility: no new config or storage/protocol compatibility concern found.
  • Parallel paths: native file readers (Parquet/ORC) are a parallel predicate-evaluation path and need the same scan-filter accounting.
  • Data correctness: no query-result correctness issue found in the remaining PR diff.
  • Observability/performance: observability is materially wrong for file scans with reader-local pushed-down predicates. Existing review threads already cover the other scan-filter overhead/counter issues, so I did not duplicate them.

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

auto old_rows = block->rows();
Status st = VExprContext::filter_block(_conjuncts, block, block->columns());
Status st = VExprContext::filter_block(_conjuncts, block, block->columns(),
ScanFilterStage::EXEC_RESIDUAL);
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.

Recording all remaining scanner conjuncts as EXEC_RESIDUAL here makes file-scan ScanFilterInfo wrong when native readers already applied pushed-down predicates. For Parquet, RowGroupReader filters _filter_conjuncts inside vparquet_group_reader.cpp before this block reaches the scanner, and ORC has the same pattern around its reader-local execute_conjuncts() calls. Those reader-local filters do not receive a scan-filter stage, so the real filtered rows are invisible; this residual pass then sees only surviving rows and often reports FilteredRows=0. Please record the reader-local predicate evaluations with an appropriate stage, or avoid attributing predicates already applied by the reader as residual-only work.

@hello-stephen
Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 7.05% (65/922) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 53.70% (21067/39231)
Line Coverage 37.42% (200254/535222)
Region Coverage 33.44% (156940/469298)
Branch Coverage 34.46% (68711/199369)

@hello-stephen
Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 7.05% (65/922) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 53.70% (21067/39231)
Line Coverage 37.41% (200236/535222)
Region Coverage 33.46% (157034/469298)
Branch Coverage 34.47% (68717/199369)

@hello-stephen
Copy link
Copy Markdown
Contributor

TPC-H: Total hot run time: 29683 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit dd78f25d5e779b96137fa21dc202ac9feee81fd5, 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	17712	4040	4024	4024
q2	q3	10800	1501	862	862
q4	4685	512	362	362
q5	7651	909	593	593
q6	190	170	136	136
q7	788	837	643	643
q8	9433	1630	1716	1630
q9	5878	4591	4583	4583
q10	6752	1820	1528	1528
q11	437	268	251	251
q12	646	429	297	297
q13	18146	3561	2785	2785
q14	262	262	245	245
q15	q16	826	770	703	703
q17	969	907	882	882
q18	7179	5795	5705	5705
q19	1299	1277	1074	1074
q20	537	415	283	283
q21	6587	2987	2779	2779
q22	469	375	318	318
Total cold run time: 101246 ms
Total hot run time: 29683 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	5100	4890	4818	4818
q2	q3	4925	5212	4767	4767
q4	2142	2249	1388	1388
q5	4870	4893	4808	4808
q6	222	178	128	128
q7	1872	1870	1528	1528
q8	2425	2133	2100	2100
q9	8006	7907	7512	7512
q10	4763	4698	4244	4244
q11	540	387	355	355
q12	738	741	529	529
q13	3073	3438	2818	2818
q14	275	281	249	249
q15	q16	682	704	617	617
q17	1312	1267	1257	1257
q18	7295	7003	6959	6959
q19	1131	1106	1135	1106
q20	2245	2228	1944	1944
q21	5334	4607	4446	4446
q22	532	448	408	408
Total cold run time: 57482 ms
Total hot run time: 51981 ms

@hello-stephen
Copy link
Copy Markdown
Contributor

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

query5	4322	626	485	485
query6	451	211	181	181
query7	4817	555	301	301
query8	384	230	218	218
query9	8791	4023	4057	4023
query10	448	321	258	258
query11	5959	2317	2217	2217
query12	155	108	102	102
query13	1276	617	428	428
query14	6427	5323	5034	5034
query14_1	4377	4381	4372	4372
query15	209	192	172	172
query16	990	456	446	446
query17	940	689	566	566
query18	2440	480	338	338
query19	195	187	150	150
query20	114	107	108	107
query21	210	139	120	120
query22	13593	13506	13347	13347
query23	17413	16591	16238	16238
query23_1	16672	16679	16632	16632
query24	7536	1796	1321	1321
query24_1	1340	1333	1324	1324
query25	581	476	406	406
query26	1320	315	169	169
query27	2681	538	343	343
query28	4555	2071	2054	2054
query29	1109	648	508	508
query30	315	229	205	205
query31	1146	1111	946	946
query32	123	68	72	68
query33	552	330	265	265
query34	1220	1140	659	659
query35	756	802	690	690
query36	1393	1403	1212	1212
query37	159	110	95	95
query38	3209	3150	3066	3066
query39	949	938	895	895
query39_1	890	906	886	886
query40	227	126	105	105
query41	73	82	78	78
query42	98	98	98	98
query43	319	330	283	283
query44	
query45	203	191	186	186
query46	1137	1238	785	785
query47	2399	2380	2277	2277
query48	397	415	289	289
query49	647	488	363	363
query50	998	360	263	263
query51	4486	4299	4268	4268
query52	93	94	82	82
query53	243	273	201	201
query54	288	235	227	227
query55	83	78	72	72
query56	244	255	228	228
query57	1416	1425	1350	1350
query58	264	222	226	222
query59	1602	1679	1465	1465
query60	303	265	249	249
query61	205	158	153	153
query62	719	650	587	587
query63	233	200	187	187
query64	2562	784	608	608
query65	
query66	1795	462	341	341
query67	29865	29642	29517	29517
query68	
query69	420	301	272	272
query70	992	964	957	957
query71	309	226	215	215
query72	3008	2714	2617	2617
query73	872	810	462	462
query74	5114	4961	4725	4725
query75	2661	2601	2248	2248
query76	2331	1189	772	772
query77	354	383	277	277
query78	12408	12580	11999	11999
query79	1514	1040	780	780
query80	1171	500	380	380
query81	510	292	256	256
query82	561	159	123	123
query83	354	278	250	250
query84	304	141	112	112
query85	929	547	460	460
query86	413	287	292	287
query87	3385	3339	3183	3183
query88	3708	2802	2823	2802
query89	431	384	328	328
query90	1860	174	178	174
query91	179	163	138	138
query92	64	61	59	59
query93	1525	1502	943	943
query94	624	351	318	318
query95	664	380	360	360
query96	1064	798	351	351
query97	2709	2723	2588	2588
query98	218	207	212	207
query99	1160	1185	1057	1057
Total cold run time: 253186 ms
Total hot run time: 170320 ms

@hello-stephen
Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 87.96% (811/922) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.75% (28266/38327)
Line Coverage 57.79% (307568/532177)
Region Coverage 54.66% (257703/471503)
Branch Coverage 56.05% (111913/199652)

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.

4 participants