Skip to content

[improvement](be) Skip reading zonemap always-true columns for count on index#65242

Open
mrhhsg wants to merge 2 commits into
apache:masterfrom
mrhhsg:fix/skip-zonemap-always-true-count
Open

[improvement](be) Skip reading zonemap always-true columns for count on index#65242
mrhhsg wants to merge 2 commits into
apache:masterfrom
mrhhsg:fix/skip-zonemap-always-true-count

Conversation

@mrhhsg

@mrhhsg mrhhsg commented Jul 6, 2026

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: None

Problem Summary:

COUNT_ON_INDEX can still read predicate columns even after segment zone maps have proven all predicates on those columns are always true and removed them before SegmentIterator initialization. This makes count queries with range predicates over high-cardinality columns scan unnecessary column data.

This PR records columns whose predicates are fully removed by segment zone map pruning, then lets SegmentIterator reuse the existing no-need-read path for non-output columns or COUNT_ON_INDEX columns. It keeps the existing output-column protection and adds a common-expression guard so expression materialization does not skip required data.

Release note

None

Check List (For Author)

  • Test:
    • Unit Test: ./run-be-ut.sh --run --filter=SegmentIteratorNoNeedReadDataTest.*
    • Regression test: generated and verified fault_injection_p0/test_zonemap_always_true_count_on_index on a local single-node cluster
    • Format: build-support/clang-format.sh; build-support/check-format.sh
    • Static check: attempted build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN, but clang-tidy could not analyze this checkout because the current toolchain reported stddef.h file not found and a pre-existing clang-tidy-nolint issue in be/src/core/types.h
  • Behavior changed: Yes. COUNT_ON_INDEX scans can skip data reads for predicate columns proven always true by segment zone maps.
  • Does this need documentation: No

…on index

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: COUNT_ON_INDEX can still read predicate columns even when segment zone maps have proven all predicates on those columns are always true and removed them before SegmentIterator initialization. This makes count queries with range predicates over high-cardinality columns scan unnecessary column data. This change records columns whose predicates are fully removed by segment zone map pruning and lets SegmentIterator reuse the existing no-need-read path for non-output columns or COUNT_ON_INDEX columns, while preserving output and common-expression safeguards.

### Release note

None

### Check List (For Author)

- Test:
    - Unit Test: ./run-be-ut.sh --run --filter=SegmentIteratorNoNeedReadDataTest.*
    - Regression test: generated and verified fault_injection_p0/test_zonemap_always_true_count_on_index on a local single-node cluster
    - Format: build-support/clang-format.sh; build-support/check-format.sh
    - Static check: attempted build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN, but clang-tidy could not analyze this checkout because the current toolchain reported 'stddef.h' file not found and a pre-existing clang-tidy-nolint issue in be/src/core/types.h
- Behavior changed: Yes. COUNT_ON_INDEX scans can skip data reads for predicate columns proven always true by segment zone maps.
- 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?

@mrhhsg

mrhhsg commented Jul 6, 2026

Copy link
Copy Markdown
Member 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.

Requesting changes for one correctness issue in the BE segment iterator path.

Checkpoint conclusions:

  • Goal: the PR tries to skip physical data reads for predicate columns proven always true by segment zonemaps, especially for COUNT_ON_INDEX. The non-key path is covered, but the key-range path is not safe yet.
  • Scope: the code change is small and focused, but it misses a parallel read path used by short-key range lookup.
  • Concurrency/lifecycle/config/compatibility: no new concurrency, lifecycle, persistence, wire-format, or dynamic config concerns found.
  • Data correctness: MAIN-001 is blocking. A key predicate removed by segment zonemap pruning can still leave the pushed key range active, and _get_row_ranges_by_keys() can then compare default placeholder values instead of real key values.
  • Tests: the added BE unit test and fault-injection regression cover the intended non-key COUNT_ON_INDEX behavior, but not the key-column predicate plus pushed key-range ordering case.
  • Performance/observability: the optimization goal is reasonable, and no extra observability requirement was found.
  • User focus: no additional user-provided focus points were present.

Subagent conclusions:

  • tests-session-config proposed TSC-001; I verified it and accepted it as MAIN-001 for an inline comment.
  • optimizer-rewrite recorded OR-DUP-001 as a duplicate confirmation of TSC-001; it was merged into MAIN-001.
  • Final convergence round 1 ended with both live subagents replying NO_NEW_VALUABLE_FINDINGS for the same current ledger/comment set.

Validation note: this runner is a static-review environment for this PR. .worktree_initialized, thirdparty/installed, and thirdparty/installed/bin/protoc are absent, so I did not run BE unit or regression tests here. Existing inline review threads were empty, so there were no duplicates to suppress.

cid < _is_common_expr_column.size() && _is_common_expr_column[cid];
const bool no_need_read_filter_column =
(_need_read_data_indices.contains(cid) && !_need_read_data_indices[cid]) ||
(_opts.zonemap_always_true_pred_cols.contains(cid) && !used_by_common_expr);

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 also affects key predicates. When a segment zonemap proves a key predicate always true, this code records the key cid in zonemap_always_true_pred_cols, but the original key_ranges are still present. _lazy_init() applies _get_row_ranges_by_keys() before lazy materialization setup, and the short-key binary search goes through _seek_and_peek() -> _seek_columns() / _read_columns(). Those now call _need_read_data() / _prune_column(), so a key cid in this set can be skipped and filled with default placeholders; _compare_short_key_with_seek_block() then compares the scan key against the placeholder instead of the real segment key.

For example, a DUP-key segment with key values [10, 20] and a pushed range k >= 5 AND k < 30 can have the k predicates removed as zonemap-always-true, while the short-key lookup still needs the real k values to compute row bounds. Please keep key-range seek reads independent of this zonemap no-read marker, or disable/remove the matching key range when the segment zonemap has already proven it fully true.

### What problem does this PR solve?

Issue Number: None

Related PR: apache#65242

Problem Summary: Segment zone map pruning can remove predicates that are always true for the current segment and mark those columns as no-need-read. Applying that marker to key columns is unsafe because key_ranges are still evaluated by short-key seeks before lazy materialization; the seek path must read real key values to compute row bounds. This change keeps the zonemap no-read marker restricted to non-key columns and adds coverage for a COUNT_ON_INDEX scan with an always-true key range predicate.

### Release note

None

### Check List (For Author)

- Test:
    - Unit Test: ./run-be-ut.sh --run --filter=SegmentIteratorNoNeedReadDataTest.*
    - Regression test: doris-local-regression --network 10.26.20.3/24 all -d fault_injection_p0 -s test_zonemap_always_true_count_on_index -forceGenOut
    - Regression test: doris-local-regression --network 10.26.20.3/24 all -d fault_injection_p0 -s test_zonemap_always_true_count_on_index
    - Format: build-support/clang-format.sh; build-support/check-format.sh
    - Static check: build-support/run-clang-tidy.sh --build-dir be/build_Release attempted, but failed on existing/toolchain diagnostics such as missing stddef.h and pre-existing clang-tidy warnings in changed files.
- Behavior changed: Yes. Key columns are excluded from the segment zonemap no-read marker so key-range seeks always read real key values.
- Does this need documentation: No
@mrhhsg

mrhhsg commented Jul 6, 2026

Copy link
Copy Markdown
Member 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.

Automated review completed for PR #65242 at head 7fc8fcdebba4989b5fc0c71f5956c08c877a67ff.

I did not find a new distinct inline issue to raise. The existing key-range concern is already covered by the live thread on SegmentIterator::_need_read_data(), and the current head addresses it by keeping key columns out of zonemap_always_true_pred_cols in Segment::new_iterator() and by checking !column.is_key() again in _need_read_data(). The added regression also covers the COUNT_ON_INDEX key-range case.

Critical checkpoint conclusions:

  • Goal and test proof: the PR narrows data-page reads for non-key predicate columns whose segment zonemap predicates were already removed as always true. The changed BE unit test covers _need_read_data() for non-key, key, output, and COUNT_ON_INDEX cases; the regression covers the non-key skip path and the key-range path.
  • Scope: the implementation is small and localized to StorageReadOptions, segment iterator setup, _need_read_data(), and focused tests.
  • Concurrency/lifecycle: no new shared mutable state, locking, thread lifecycle, or persistent state is introduced.
  • Compatibility/config: no new config item or protocol/storage-format change is introduced. Existing session flags are only used by tests.
  • Parallel paths: key-range seeks, common-expression columns, output columns, delete predicates, and COUNT_ON_INDEX paths were checked. The key-column path is guarded after the existing review thread; common-expression and delete-predicate guards still force real reads.
  • Tests/results: the new .out matches the added qt_count and qt_count_with_key_range tags. Its final blank separator matches the regression framework writer format and existing generated outputs, so I did not treat git diff --check's generated-output blank-EOF warning as a useful inline review issue.
  • Observability/performance: no additional observability appears required for this small IO-skip optimization; the change removes unnecessary reads after segment-level predicate pruning.

User focus: no additional user-provided focus was present in .code-review.K2YjX4/review_focus.txt.

Subagent conclusions: optimizer-rewrite found no new candidate and marked the previous key-range issue as duplicate-covered by the existing thread. tests-session-config proposed TSC-1 for the generated .out final blank line; I dismissed it with framework/repository-format evidence. Convergence round 1 ended with both live subagents replying NO_NEW_VALUABLE_FINDINGS for the same ledger and empty proposed comment set.

Validation limits: this checkout is grafted/shallow, so GitHub PR file metadata was used as the authoritative six-file scope. .worktree_initialized, thirdparty/installed, and thirdparty/installed/bin/protoc are missing, and the available clang-format is version 18 rather than the required v16, so I did not run BE unit tests, regression tests, or repository clang-format locally.

@mrhhsg

mrhhsg commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (19/19) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 73.01% (29205/40000)
Line Coverage 56.57% (315143/557125)
Region Coverage 53.29% (263199/493931)
Branch Coverage 54.17% (115233/212721)

@mrhhsg mrhhsg marked this pull request as ready for review July 7, 2026 00:04
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Jul 7, 2026
@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17742	4233	4154	4154
q2	2075	330	201	201
q3	10253	1450	839	839
q4	4682	471	336	336
q5	7616	887	580	580
q6	183	180	138	138
q7	763	875	627	627
q8	9330	1553	1737	1553
q9	5613	4405	4407	4405
q10	6767	1796	1542	1542
q11	500	343	318	318
q12	714	571	442	442
q13	18134	3563	2727	2727
q14	263	264	247	247
q15	q16	791	779	708	708
q17	1055	966	973	966
q18	6920	5849	5507	5507
q19	1169	1178	1022	1022
q20	796	652	588	588
q21	6033	2904	2553	2553
q22	467	374	319	319
Total cold run time: 101866 ms
Total hot run time: 29772 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5398	4962	4899	4899
q2	303	327	234	234
q3	4836	5334	4813	4813
q4	2075	2155	1394	1394
q5	4844	4892	4624	4624
q6	245	181	132	132
q7	1850	1798	1514	1514
q8	2413	2127	2146	2127
q9	7717	7402	7210	7210
q10	4705	4665	4192	4192
q11	543	387	353	353
q12	752	737	521	521
q13	3059	3394	2784	2784
q14	284	277	257	257
q15	q16	673	694	614	614
q17	1300	1276	1257	1257
q18	7358	7083	6714	6714
q19	1134	1035	1075	1035
q20	2231	2221	1947	1947
q21	5303	4536	4441	4441
q22	512	461	407	407
Total cold run time: 57535 ms
Total hot run time: 51469 ms

@hello-stephen

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

query5	4318	653	485	485
query6	462	221	205	205
query7	4841	583	343	343
query8	339	191	191	191
query9	8766	4004	4018	4004
query10	443	345	310	310
query11	5926	2351	2144	2144
query12	163	107	109	107
query13	1271	631	440	440
query14	6289	5301	4955	4955
query14_1	4364	4293	4294	4293
query15	213	203	183	183
query16	1030	494	462	462
query17	955	765	595	595
query18	2447	492	355	355
query19	218	193	155	155
query20	118	117	110	110
query21	241	161	137	137
query22	13665	13706	13380	13380
query23	17248	16617	16189	16189
query23_1	16472	16274	16379	16274
query24	7619	1768	1301	1301
query24_1	1311	1312	1296	1296
query25	573	486	404	404
query26	1330	373	215	215
query27	2564	575	379	379
query28	4426	2088	2014	2014
query29	1094	658	504	504
query30	358	261	230	230
query31	1140	1086	1000	1000
query32	158	66	62	62
query33	540	330	273	273
query34	1198	1183	656	656
query35	799	799	680	680
query36	1386	1405	1236	1236
query37	168	113	97	97
query38	1889	1729	1655	1655
query39	944	966	935	935
query39_1	883	888	892	888
query40	267	159	140	140
query41	66	62	66	62
query42	91	96	90	90
query43	324	320	284	284
query44	1427	779	779	779
query45	207	191	177	177
query46	1060	1174	729	729
query47	2409	2360	2243	2243
query48	397	410	296	296
query49	576	423	324	324
query50	1030	428	346	346
query51	10768	10805	10549	10549
query52	87	86	79	79
query53	263	275	207	207
query54	280	245	211	211
query55	79	71	69	69
query56	296	276	295	276
query57	1446	1412	1323	1323
query58	298	264	260	260
query59	1577	1621	1521	1521
query60	318	275	247	247
query61	147	149	151	149
query62	715	652	596	596
query63	248	207	206	206
query64	2566	762	627	627
query65	4850	4795	4830	4795
query66	1840	508	399	399
query67	29661	29607	29432	29432
query68	3080	1566	1022	1022
query69	404	307	265	265
query70	1062	911	994	911
query71	349	304	319	304
query72	2898	2611	2273	2273
query73	827	764	424	424
query74	5143	4946	4757	4757
query75	2607	2596	2222	2222
query76	2329	1226	755	755
query77	361	381	278	278
query78	12225	12434	11777	11777
query79	1443	1172	769	769
query80	656	548	457	457
query81	481	320	278	278
query82	1050	158	126	126
query83	415	322	290	290
query84	340	163	133	133
query85	948	614	499	499
query86	403	292	298	292
query87	1835	1815	1782	1782
query88	3722	2777	2752	2752
query89	530	400	353	353
query90	1961	205	200	200
query91	207	195	163	163
query92	67	60	56	56
query93	1581	1565	933	933
query94	553	363	317	317
query95	808	516	567	516
query96	1064	810	355	355
query97	2694	2653	2566	2566
query98	213	206	197	197
query99	1186	1182	1032	1032
Total cold run time: 265323 ms
Total hot run time: 179995 ms

@hello-stephen

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

query1	0.01	0.00	0.01
query2	0.09	0.05	0.04
query3	0.25	0.14	0.13
query4	1.61	0.14	0.14
query5	0.28	0.23	0.23
query6	1.25	1.08	1.09
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.38	0.31	0.30
query10	0.57	0.55	0.56
query11	0.20	0.14	0.14
query12	0.18	0.15	0.14
query13	0.49	0.48	0.49
query14	1.01	1.04	1.02
query15	0.63	0.60	0.60
query16	0.32	0.33	0.33
query17	1.11	1.09	1.12
query18	0.23	0.20	0.21
query19	2.06	1.90	1.88
query20	0.02	0.02	0.01
query21	15.45	0.23	0.15
query22	4.74	0.05	0.05
query23	16.14	0.30	0.13
query24	3.01	0.45	0.36
query25	0.14	0.05	0.05
query26	0.72	0.21	0.15
query27	0.04	0.04	0.03
query28	3.51	0.93	0.54
query29	12.51	4.12	3.27
query30	0.26	0.14	0.16
query31	2.77	0.60	0.32
query32	3.22	0.59	0.48
query33	3.16	3.30	3.16
query34	15.53	4.31	3.56
query35	3.54	3.52	3.53
query36	0.57	0.44	0.44
query37	0.12	0.07	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.19	0.17	0.16
query41	0.08	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.04
Total cold run time: 96.66 s
Total hot run time: 25.15 s

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

Labels

approved Indicates a PR has been approved by one committer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants