[feature](be) Add SNII inverted index storage format#64909
Open
airborne12 wants to merge 86 commits into
Open
Conversation
Contributor
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
airborne12
added a commit
to airborne12/apache-doris
that referenced
this pull request
Jun 27, 2026
### What problem does this PR solve? Issue Number: N/A Related PR: apache#64909 Problem Summary: SNII phrase and phrase-prefix queries spent most CPU in docid intersection, selective PRX/PFOR decode, and position verification. This change avoids generating PRX ordinals for full on-disk windows so the reader can use the full CSR path, folds selective PRX count validation into selected-range construction, removes hot-loop overflow helper calls from two-term phrase matching, and routes single-tail phrase-prefix queries through the streaming phrase path to avoid materializing all expected tail positions. On the 10B cloud_sim PP5 cold query, SNII BE CPU improved from 72.20s to 63.29s and HWM dropped from 20.26GB to 7.08GB. ### Release note None ### Check List (For Author) - Test: Unit Test and Manual test - Unit Test: ./run-be-ut.sh --run --filter=SniiPhraseQueryTest.*:SniiPrxPodTest.* - Static check: build-support/check-format.sh; git diff --check - Manual test: ./build.sh --be -j 192 - Manual test: cloud_sim cold query benchmark for PH5/PP5 via /mnt/disk15/jiangkai/textbench/cold_query_bench.sh - Behavior changed: No - Does this need documentation: No
airborne12
added a commit
to airborne12/apache-doris
that referenced
this pull request
Jun 28, 2026
### What problem does this PR solve? Issue Number: N/A Related PR: apache#64909 Problem Summary: SNII phrase queries over high-df terms were CPU-bound in PFOR unpacking and docid conjunction ordinal mapping. PH5/PP5 profiling on the 10B cloud_sim dataset showed pfor_decode and intersect_window_candidate_range_with_ordinals as top self CPU consumers while remote bytes and serial read rounds stayed fixed. This change adds low-bit PFOR unpack fast paths for common widths 3/5/6/7 and a bounded-span bitset/rank intersection path that preserves PRX doc ordinals for 16K-doc windows. The optimized path keeps the on-disk format unchanged and reduces CPU in the cold cloud_sim phrase benchmark: PH5 BE CPU 48.07s -> 41.68s, PP5 BE CPU 49.95s -> 43.57s. ### Release note None ### Check List (For Author) - Test: Unit Test / Manual test - build-support/clang-format.sh be/src/storage/index/snii/core/src/encoding/pfor.cpp be/src/storage/index/snii/core/src/query/docid_conjunction.cpp be/test/storage/index/snii_query_test.cpp - build-support/check-format.sh be/src/storage/index/snii/core/src/encoding/pfor.cpp be/src/storage/index/snii/core/src/query/docid_conjunction.cpp be/test/storage/index/snii_query_test.cpp - git diff --check - build-support/run-clang-tidy.sh --build-dir be/build_Release - ./run-be-ut.sh --run --filter='SniiPhraseQueryTest.*:SniiTermQueryTest.*:SniiPrxPodTest.*:SniiPforTest.*' - ./build.sh --be -j 192 - cloud_sim deploy/start BE and PH5/PP5 cold phrase benchmark under /mnt/disk15/jiangkai/textbench/runs/20260628_phrase_cpu_opt_final_verified - Behavior changed: No - Does this need documentation: No
yiguolei
reviewed
Jun 29, 2026
hoshinojyunn
reviewed
Jul 8, 2026
| // Reserve docids/freqs by ntok (an upper bound on the doc count: ntok >= ndocs). | ||
| // The doc count is not stored separately to keep Term compact; since the corpus | ||
| // is freq~1 per (term, doc), ntok ~= ndocs so the over-reserve is negligible. | ||
| tp.docids.reserve(t.ntok); |
Contributor
There was a problem hiding this comment.
Is allocating the entire ntok size of memory directly to docids and freqs too aggressive? Wouldn't it be better to use an nDocs field in the Term to record the doc freq?
### What problem does this PR solve?
Issue Number: None
Related PR: None
Problem Summary: Doris only routed inverted index files through the existing V1/V2 storage implementations. This change adds SNII as an independent inverted index storage format, copies the SNII core reader/writer/query implementation into BE, and branches the Doris index file reader/writer paths so SNII reads, writes, queries, and null bitmap handling go through SNII code. SNII reuses Doris analyzer integration only; it does not route SNII storage through the existing CLucene directory/compound reader paths. SNII currently supports string and array string inverted indexes, while numeric/BKD indexes are rejected for this format until BKD support is implemented.
### Release note
Add SNII as an inverted index storage format for string inverted indexes. BKD indexes are not supported with SNII yet.
### Check List (For Author)
- Test: Build
- `./build.sh --be`
- `./build.sh --fe`
- `build-support/clang-format.sh`
- `build-support/check-format.sh`
- `build-support/run-clang-tidy.sh --build-dir be/build_Release` attempted; it failed because clang-tidy could not resolve `stddef.h` in this toolchain and also reported pre-existing unrelated diagnostics.
- Behavior changed: Yes. Tables using `inverted_index_storage_format=SNII` route string inverted index storage/query/null handling through SNII and reject BKD indexes.
- Does this need documentation: Yes. No doc PR yet.
### What problem does this PR solve?
Issue Number: close #xxx
Related PR: #xxx
Problem Summary: Add a focused regression case for the SNII inverted index storage format. The test creates a string inverted index with inverted_index_storage_format=SNII, verifies MATCH_ANY, MATCH_ALL, MATCH_PHRASE, and NULL bitmap behavior, and validates that numeric/BKD inverted index creation is rejected for SNII.
### Release note
None
### Check List (For Author)
- Test: Regression test
- bash /mnt/disk1/jiangkai/cloud_sim/bootstrap.sh run -- --run -d inverted_index_p0/storage_format -s test_storage_format_snii -genOut
- bash /mnt/disk1/jiangkai/cloud_sim/bootstrap.sh run -- --run -d inverted_index_p0/storage_format -s test_storage_format_snii
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve?
Issue Number: None
Related PR: None
Problem Summary: This change completes the SNII inverted index storage-format split by routing SNII reads and writes through the SNII implementation, preserving Doris IO context during SNII file reads, bounding expanding queries before materializing all prefix terms, and rejecting unsupported SNII operations such as BKD-backed indexes, ANN indexes, and BUILD INDEX. It also avoids applying old CLucene index-compaction/drop-index paths to SNII files and adds focused FE and regression coverage for unsupported paths.
### Release note
SNII inverted index storage format rejects unsupported BKD, ANN, and BUILD INDEX operations.
### Check List (For Author)
- Test:
- Build: ./build.sh --be --fe -j 192
- Build: ./build.sh --be -j 192
- Unit Test: ./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.IndexDefinitionTest,org.apache.doris.alter.IndexChangeJobTest
- Regression test: ./bootstrap.sh run -- --run -d inverted_index_p0/storage_format -s test_storage_format_snii -forceGenOut; ./bootstrap.sh run -- --run -d inverted_index_p0/storage_format -s test_storage_format_snii
- Format: ./build-support/clang-format.sh; ./build-support/check-format.sh; git diff --check
- Static Analysis: ./build-support/run-clang-tidy.sh and ./build-support/run-clang-tidy.sh --build-dir be/build_Release attempted; failed because clang-tidy could not resolve system stddef.h and also reported existing large-function/NOLINT diagnostics outside the safe scope of this SNII integration.
- Behavior changed: Yes. SNII explicitly rejects unsupported BKD/ANN/BUILD INDEX paths instead of falling through to non-SNII index handling.
- Does this need documentation: No
Issue Number: None
Related PR: None
Problem Summary: SNII performance validation in cloud mode needs comparable IO observability against the existing CLucene/V3 inverted index path. Before this change, SNII opened remote index files without the same file-cache options as V3 and only part of the IO context reached SNII/CLucene readers, so query profiles could not compare logical requested index bytes, physical index reads, and serial read rounds. This change routes SNII index file opens through Doris file-cache options, propagates copied inverted-index IO context through SNII reads, records request/read bytes and read round counters for both SNII and CLucene index readers, and exposes those counters in the file-cache profile reporter.
SNII and V3 inverted index scans now expose additional IO profile counters for request bytes, physical read bytes, range read count, and serial read rounds.
- Test:
- Unit Test: ./run-be-ut.sh --clean --run --filter='DorisSniiFileReaderTest.*:DorisFSDirectoryTest.FSIndexInputReadInternalRecordsIndexIOStatsAndContext:FileCacheProfileReporterTest.*' -j "192"
- Unit Test: ./run-be-ut.sh --run --filter='DorisSniiFileReaderTest.*:DorisFSDirectoryTest.FSIndexInputReadInternalRecordsIndexIOStatsAndContext:FileCacheProfileReporterTest.*' -j "192"
- Format: build-support/clang-format.sh; build-support/check-format.sh; git diff --check
- Static Analysis: build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN attempted; failed because clang-tidy could not resolve system stddef.h and also reported existing large-function/C-header/NOLINT diagnostics outside this change. Clear new SNII adapter style warnings were fixed.
- Behavior changed: Yes. SNII remote index file reads now use the same Doris file-cache reader options as V3 when file cache is enabled, and both SNII/V3 report additional profile counters.
- Does this need documentation: No
### What problem does this PR solve?
Issue Number: None
Related PR: None
Problem Summary: SNII phrase and phrase-prefix queries spent most CPU time re-scanning phrase candidate docids for every PRX chunk and allocating per-doc expected tail position vectors. On the 10B TextBench cloud benchmark, MATCH_PHRASE 'failed order' took 253.0s wall / 3942.9 CPU-s and MATCH_PHRASE_PREFIX 'failed ord' took 438.3s wall / 6939.5 CPU-s before this optimization. The fix starts PRX candidate filtering from the chunk's first docid, keeps an all-selected fast path, stores expected tail positions in flat CSR-style arrays, and uses a single exact-term fast path for phrase-prefix expected tail positions. The same benchmark now runs in about 3.8s / 59.3 CPU-s for phrase and 3.9s / 61-62 CPU-s for phrase-prefix.
### Release note
None
### Check List (For Author)
- Test:
- Unit Test: ./run-be-ut.sh --run --filter=SniiPhraseQueryTest.*
- Manual test: release BE build and cloud_sim E2E TextBench phrase benchmark
- Static check: git diff --check; build-support/run-clang-tidy.sh --build-dir be/build_Release passed changed lines in phrase_query.cpp, while the new BE UT source is blocked by a local libstdc++ _POSIX_SEM_VALUE_MAX toolchain header error.
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve?
Issue Number: N/A
Related PR: N/A
Problem Summary: SNII MATCH_PHRASE in cloud mode spent most CPU in selective PRX/PFOR decode, candidate ordinal materialization, and generic two-term position checks. This change removes per-doc ordinal Status overhead, uses selected PRX ranges to compact dense PFOR decodes, decodes sparse PFOR runs through a stack buffer, and adds a two-term phrase merge path. In cloud_sim PH5 on textbench_10b_perf.otel10b_phrase40_snii, BE CPU is now about 47.67s on average with SQL and inverted-index query cache disabled, down from roughly 55-59s observed before these optimizations.
### Release note
None
### Check List (For Author)
- Test: Unit Test and Manual test
- Unit Test: ./run-be-ut.sh --run --filter=SniiPhraseQueryTest.*:SniiPrxPodTest.*
- Build: ./build.sh --be -j 192
- Manual test: deployed BE to cloud_sim and ran PH5 benchmark under /mnt/disk15/jiangkai/textbench/runs/20260628_phrase_cpu_opt_final_refactor_nocache
- Static check: git diff --check; build-support/run-clang-tidy.sh --build-dir be/build_Release attempted but failed because the local clang/GCC sysroot cannot resolve stddef.h
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve? Issue Number: N/A Related PR: apache#64909 Problem Summary: SNII phrase and phrase-prefix queries spent most CPU in docid intersection, selective PRX/PFOR decode, and position verification. This change avoids generating PRX ordinals for full on-disk windows so the reader can use the full CSR path, folds selective PRX count validation into selected-range construction, removes hot-loop overflow helper calls from two-term phrase matching, and routes single-tail phrase-prefix queries through the streaming phrase path to avoid materializing all expected tail positions. On the 10B cloud_sim PP5 cold query, SNII BE CPU improved from 72.20s to 63.29s and HWM dropped from 20.26GB to 7.08GB. ### Release note None ### Check List (For Author) - Test: Unit Test and Manual test - Unit Test: ./run-be-ut.sh --run --filter=SniiPhraseQueryTest.*:SniiPrxPodTest.* - Static check: build-support/check-format.sh; git diff --check - Manual test: ./build.sh --be -j 192 - Manual test: cloud_sim cold query benchmark for PH5/PP5 via /mnt/disk15/jiangkai/textbench/cold_query_bench.sh - Behavior changed: No - Does this need documentation: No
### What problem does this PR solve?
Issue Number: N/A
Related PR: N/A
Problem Summary: SNII high-df term and phrase queries spent avoidable CPU in vector-to-Roaring materialization, dense docid expansion, selected PRX range construction, repeated final-candidate filtering, and sorted docid conjunction. The CPU profile showed phrase execution dominated by docid conjunction, PFOR PRX decode, and selective PRX CSR compaction instead of remote I/O. This change streams eligible term, prefix, regexp, and wildcard query results directly into Roaring, emits dense docid windows as ranges, carries PRX doc ordinal context through phrase execution, builds selected PRX count ranges during PFOR decode, skips redundant final-candidate filtering, and adds sparse galloping paths for docid conjunction. It also caps conjunction reserve sizes to the maximum possible match count and refactors the SNII reader query dispatch to keep the storage reader control flow smaller.
### Release note
None
### Check List (For Author)
- Test: Unit Test / Manual test
- Unit Test: ./run-be-ut.sh --run --filter=SniiPhraseQueryTest.*:SniiTermQueryTest.*:SniiPrxPodTest.*
- Manual test: ./build.sh --be -j 192
- Manual test: deployed BE to cloud_sim and ran support_phrase MATCH_PHRASE smoke query
- Manual test: cloud_sim cold benchmark for OP_term_high_df, PH5_phrase_failed_order, and PP5_phrase_prefix_failed
- Static check: git diff --check; build-support/check-format.sh
- Static check: clang-tidy was attempted with build-support/run-clang-tidy.sh --build-dir be/build_Release, but this environment failed before useful changed-line validation with missing stddef.h/toolchain include errors and pre-existing full-file warnings.
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve?
Issue Number: N/A
Related PR: N/A
Problem Summary: SNII phrase queries on the 10B cloud benchmark spent most CPU in PRX position decode, posting cursor iteration, and docid conjunction. This change adds a two-term phrase streaming path, uses an adjacent-pair prefilter for multi-term phrase verification, reuses candidate ranges during docid conjunction, and adds low bit-width PFOR unpack paths for common PRX count and delta widths. A near-dense ordinal shortcut was tested and removed because it regressed the PH5 and PP5 phrase cases. In the cloud_sim 10B cold benchmark, PH5 improved from 6181 ms wall time and 60.22 s BE CPU to 5794 ms wall time and 55.43 s BE CPU; PP5 improved from 6211 ms wall time and 59.82 s BE CPU to 6038 ms wall time and 56.26 s BE CPU. PH5 pprof shows pfor_decode self CPU reduced to about 11.4% after the low bit-width fast paths.
### Release note
None
### Check List (For Author)
- Test:
- Unit Test: ./run-be-ut.sh --run --filter='SniiPforTest.*:SniiPhraseQueryTest.*:SniiTermQueryTest.*:SniiPrxPodTest.*'
- Manual test: ./build.sh --be -j 192
- Manual test: cloud_sim PH5/PP5 cold benchmark at /mnt/disk15/jiangkai/textbench/runs/20260628_phrase_cpu_final_pfor_next_cold
- Manual test: cloud_sim PH5 pprof at /mnt/disk15/jiangkai/textbench/runs/20260628_phrase_final_pfor_next_pprof
- Static Check: build-support/clang-format.sh, build-support/check-format.sh, and build-support/run-clang-tidy.sh --build-dir be/build_Release
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve?
Issue Number: N/A
Related PR: N/A
Problem Summary: SNII phrase queries on the 10B cloud benchmark still spent the largest CPU share in docid/ordinal intersection before PRX verification. The hot path had to intersect dense or near-dense candidate windows with term docids and produce PRX doc ordinals. This change adds a candidate-span fast path that directly accepts all term docids when candidates continuously cover the term span, adds dense/near-dense ordinal mapping for term spans with few missing docs, and fixes mixed dense/non-dense window output ordering by batching window work and flushing in original order. In cloud_sim PH5/PP5 cold benchmark, PH5 improved from 5794 ms wall and 55.43 s BE CPU to 5702 ms wall and 53.55 s BE CPU; PP5 kept similar wall time and reduced BE CPU from 56.26 s to 55.21 s. The inverted index read bytes, remote bytes, cache bytes, serial rounds, and IO counts stayed unchanged, so the improvement is from CPU-side algorithm work rather than reduced remote reads. A bulk dense append variant was tested and reverted because it regressed PH5/PP5.
### Release note
None
### Check List (For Author)
- Test:
- Unit Test: ./run-be-ut.sh --run --filter='SniiPforTest.*:SniiPhraseQueryTest.*:SniiTermQueryTest.*:SniiPrxPodTest.*'
- Manual test: ./build.sh --be -j 192
- Manual test: cloud_sim PH5/PP5 cold benchmark at /mnt/disk15/jiangkai/textbench/runs/20260628_phrase_final_cover_span_refactor_cold
- Manual test: cloud_sim PH5 pprof at /mnt/disk15/jiangkai/textbench/runs/20260628_phrase_final_cover_span_refactor_pprof
- Static Check: build-support/clang-format.sh, build-support/check-format.sh, git diff --check
- Static Check: build-support/run-clang-tidy.sh --build-dir be/build_Release failed because the local ldb clang-tidy toolchain could not find stddef.h while parsing system headers
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve?
Issue Number: None
Related PR: None
Problem Summary: SNII phrase verification still spent CPU in per-candidate cursor/status handling for exact two-term phrases. Profiles showed PostingCursor::next and positions handling in the hot path while IO metrics stayed unchanged. This change reuses a shared PRX chunk decoder and adds a two-term chunk merge path for non-repeated phrases so overlapping chunks decode once and docids are verified with a linear merge. Repeated-term phrases still use the existing streaming path, and multi-term streaming is split into smaller helpers. On 10B textbench cold cloud_sim runs, PH5 CPU dropped from 53.55s to 48.07s and PP5 CPU dropped from 55.21s to 49.95s with identical IO bytes.
### Release note
None
### Check List (For Author)
- Test:
- Unit Test: ./run-be-ut.sh --run --filter='SniiPhraseQueryTest.*:SniiTermQueryTest.*:SniiPrxPodTest.*:SniiPforTest.*'
- Manual test: ./build.sh --be -j 192
- Manual test: cloud_sim BE redeploy and smoke MATCH_PHRASE / MATCH_PHRASE_PREFIX
- Benchmark: textbench 10B cold PH5/PP5 comparison
- Static check: build-support/clang-format.sh, build-support/check-format.sh, git diff --check; build-support/run-clang-tidy.sh --build-dir be/build_Release attempted but blocked by local clang-tidy system header resolution where stddef.h is not found.
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve? Issue Number: N/A Related PR: apache#64909 Problem Summary: SNII phrase queries over high-df terms were CPU-bound in PFOR unpacking and docid conjunction ordinal mapping. PH5/PP5 profiling on the 10B cloud_sim dataset showed pfor_decode and intersect_window_candidate_range_with_ordinals as top self CPU consumers while remote bytes and serial read rounds stayed fixed. This change adds low-bit PFOR unpack fast paths for common widths 3/5/6/7 and a bounded-span bitset/rank intersection path that preserves PRX doc ordinals for 16K-doc windows. The optimized path keeps the on-disk format unchanged and reduces CPU in the cold cloud_sim phrase benchmark: PH5 BE CPU 48.07s -> 41.68s, PP5 BE CPU 49.95s -> 43.57s. ### Release note None ### Check List (For Author) - Test: Unit Test / Manual test - build-support/clang-format.sh be/src/storage/index/snii/core/src/encoding/pfor.cpp be/src/storage/index/snii/core/src/query/docid_conjunction.cpp be/test/storage/index/snii_query_test.cpp - build-support/check-format.sh be/src/storage/index/snii/core/src/encoding/pfor.cpp be/src/storage/index/snii/core/src/query/docid_conjunction.cpp be/test/storage/index/snii_query_test.cpp - git diff --check - build-support/run-clang-tidy.sh --build-dir be/build_Release - ./run-be-ut.sh --run --filter='SniiPhraseQueryTest.*:SniiTermQueryTest.*:SniiPrxPodTest.*:SniiPforTest.*' - ./build.sh --be -j 192 - cloud_sim deploy/start BE and PH5/PP5 cold phrase benchmark under /mnt/disk15/jiangkai/textbench/runs/20260628_phrase_cpu_opt_final_verified - Behavior changed: No - Does this need documentation: No
…SR decode
decode_payload_csr_selective (the .prx position reader used once a
candidate docid set is known) previously decoded EVERY doc's position
deltas even for non-candidate docs, only to advance the sequential CSR
cursor, then discarded them (if !selected continue). Profiling a
multi-word phrase-prefix ('GET images sp*') showed ~40% of the query
CPU in this varint decode -- with a sparse candidate set most decoded
positions were thrown away.
New ByteSource::skip_varints(count) advances past N varints by scanning
only continuation bits (one branch per byte, no shift/accumulate/store/
per-value Status). decode_payload_csr_selective uses it for non-selected
docs. Read-side only: no format, disk, or import change, and every
phrase/phrase-prefix operator that narrows to candidates before reading
positions gets faster (no operator regresses).
Result (cloud_sim, 'GET images sp*' SNII vs V3 wall):
warm 1507->1284ms (1.61x->1.31x slower), cold 1889->1593 (1.58x->1.32x).
Full trajectory from the campaign start: 3.7x/3.15x -> 1.31x/1.32x.
agentlogs 'token estimate co*' stays FASTER (1.28x warm / 1.06x cold).
UT: 767/767 incl. 2 new SkipVarints tests (skip advances identically to
decode across 1-5 byte encodings; truncation fails).
The CSR position reader decoded each varint through the out-of-line get_varint32 -> get_varint64 -> decode_varint64 chain (per-value Status, no fast path), and read each doc's pos_count header the same way. Both dominate the phrase / phrase-prefix hot loop once positions are narrowed to candidate docs. - ByteSource::decode_delta_run(count, out): tight inline LEB128 decoder with a single-byte fast path that prefix-sums ascending position deltas directly into the flat output; replaces the inner decode loop in decode_payload_csr and decode_payload_csr_selective. - ByteSource::get_varint32_fast(v): inline single-byte fast path for the per-doc pos_count header (falls back to get_varint32 for multi-byte). Warm httplogs 'GET images sp*' phrase-prefix ii_filter CPU 9483 -> 6972 (-26%); no on-disk format change; all SNII decode paths share the win. Unit tests: decode_delta_run prefix-sum/append/truncation equivalence, get_varint32_fast vs slow-path equivalence, skip_varints long-run.
…c flag New off-by-default mutable config inverted_index_read_bypass_file_cache. When set, inverted-index file readers (both SNII and CLucene) open with NO_CACHE and issue precise S3 range GETs, bypassing the 1MiB FILE_BLOCK_CACHE -- WITHOUT touching the global enable_file_cache, so cloud mode (which requires it) still boots. Diagnostic-only: it isolates the block-cache read amplification and the serial-round cost on the direct object-store path (a direct-vs-direct engine comparison the block cache otherwise masks). Warm queries lose the local cache under this flag, so it is a measurement knob, not a production setting. Read-side only; V3/CLucene and SNII both honor it via the per-open cache_type.
…ccumulation An exact reserve(size()+count) per NULL run (writer add_nulls) or per docid range (query VectorDocIdSink::append_range) caps capacity at "just enough", so the NEXT append reallocates and memcpys the whole accumulated array: O(runs x array_bytes) total memcpy. On an agentlogs full compaction segment (12.4M rows, 22% interleaved nulls) this meant TBs of memcpy per tablet -- the compaction ran 8x+ slower than V3 (whose add_nulls is a roaring addRange) and peaked at 198 GB RSS; post-fix it settles at 2.6x / 18.7 GB. Grow by max(need, 2 * capacity) instead (one reallocation per large run, O(count) amortized), mirror _null_docids capacity into the writer's MemoryReporter (and through it the LOAD MemTracker) so a large interleaved-null segment no longer accumulates untracked RSS, and pin the growth policy with regression tests.
…n (opt-in) New mBool snii_bigram_defer_build_to_compaction (default false): a direct, non-ARRAY load (stream/broker load, DataWriteType::TYPE_DIRECT) skips feeding the hidden adjacent-pair bigram tokens AND the bigram sentinel -- measured at -50.7% (wikipedia) / -32% (agentlogs) of import index CPU -- and persists a resident per-index meta flag (kPhraseBigramsDeferred) so phrase readers on such fresh segments skip impossible pair-dict probes (a per-tail lookup for MATCH_PHRASE_PREFIX) and go straight to the full-fidelity positions-verification fallback: identical results, slower, until compaction re-feeds every token and rebuilds the bigram index at no extra compaction cost (measured equal wall/CPU vs a regular segment's full compaction). Compatibility: old readers keep the omitted-sentinel fallback contract (plus the persisted df-prune thresholds as a redundant trigger); new readers on old segments see the flag absent and behave unchanged; docs-only and ARRAY indexes never defer. Plumbed via ColumnWriterOptions.is_direct_load -> IndexColumnWriter::set_direct_load(), forwarded unconditionally by the column writer right after create(), captured once per writer behind an explicit latch (a late call is logged and ignored). The add_snii_index mid-load prune-flip flush guard exempts deferred segments (their bigram diet is provably inert). NOTE: compaction is the only rebuild mechanism -- see the perf-cliff disclosure in config.h before enabling. Verified: E2E fresh/compacted phrase and phrase-prefix counts equal V3 on wikipedia/agentlogs (real S3 cloud mode); writer/wiring/e2e gtests cover capture-once config flips, byte-identity of every non-deferring combination, flag/postings lifecycle coherence, both segment writers, and variant subcolumn propagation; SNII UT 788/788.
… prx tier Two write-CPU cuts backed by a 4-corpus evaluation (wikipedia 1M / httplogs 247M / agentlogs 99M / textbench 1B, cloud mode, real S3; effects isolated with same-binary config-only A/B -- back-to-back pairs or ABAB with the warm-up pair discarded): 1. Default snii_dict_block_zstd_level and snii_prx_zstd_level from 9 to 3. The delta+varint-encoded dict/prx payloads are high-entropy, so level 9's extra search buys almost no ratio: settled index grows only +0.6%..+6.3% (whole table +0.3%..+1.9%, agentlogs 0) while import CPU drops 4-18% total / 6-37% of the index share (httplogs -290s, agentlogs -204s, textbench -1300s+-500, wikipedia -89s+-13) and full-compaction CPU falls 8-24% (rebuild re-compression). Settled cold-query latency is unchanged (interleaved A/B, phrase/prefix); counts equal V3 across the operator set. 2. New snii_prx_zstd_level_direct_load (default 3, clamp [3,19]): direct loads (stream/broker, captured once by set_direct_load alongside the bigram-defer decision) compress prx at the load tier while compaction / schema change / ADD INDEX keep snii_prx_zstd_level. Inert at the new defaults; it lets size-critical deployments raise the compaction level (e.g. 9) and keep loads cheap, since compaction rewrites every segment -- settled data is byte-for-byte unaffected by the tier. add_snii_index now takes a SniiAddIndexOptions struct (phrase_bigrams_deferred + is_direct_load) instead of a bare bool. New gtests pin the tier contract: identical answers with a strict size delta, non-direct paths byte-identical whatever the tier value, flush-read level semantics, and clamping. SNII UT 792/792.
…I buffer Drop the materializing token lane (get_analyse_result building a per-row vector<TermInfo> with one heap std::string per token, immediately re-copied into the SPIMI intern arena): SniiIndexColumnWriter now walks the CLucene TokenStream itself and feeds each token to SpimiTermBuffer::add_token_returning_id as a string_view over the token buffer -- one copy total, into the arena. Tokenization semantics are mirrored exactly, including the subtle one: an empty token's position increment is dropped with the token. The keyword (non-analyzed) lane streams the value slice the same way. Byte-identity is pinned by new golden-byte tests (snii_writer_golden_bytes_test.cpp): a fixed edge-case corpus (empty value, punctuation-only row, >ignore_above value, unicode/mixed text, repeated terms, multi-batch adds, interleaved null runs) written through the production stack must reproduce recorded FNV-1a-64 digests for english / unicode / keyword indexes -- harvested from the pre-change path and verified deterministic across processes. Measured with deploy-alternating binary ABAB x3 (idle machine, per-leg warm-up import discarded): import CPU wikipedia -7..12%, httplogs -3..10%, all six pairs the same direction; freshly written segments answer the 8-operator set count-equal to V3. SNII UT 795/795.
Restore the _no_need_read_key_data eligible/wrapper split that the G03 conflict resolution collapsed, and drop the _vir_cid_to_idx_in_block clause from the count-emit shortcut facts (upstream removed the member; the virtual-column expr map alone carries the eligibility signal now).
Flip snii_bigram_defer_build_to_compaction's default to true. The 4-corpus write-parity campaign measured the deferral at -3.5%..-35% of import CPU (wikipedia -543s, textbench -1006s, agentlogs -226s, httplogs -114s; same- binary config ABAB x2, all eight pairs the same direction) with ZERO added compaction cost -- SNII compaction rebuilds every token anyway, so the load-time bigram build was duplicated work. With the deferral the textbench (1B rows) import lands at index-CPU parity with V3 (1.02x). Fresh direct-load segments answer phrase queries through the identical- results positions fallback until compaction rebuilds the bigrams (resident kPhraseBigramsDeferred flag routes readers past the impossible pair probes). The config.h perf-cliff disclosure is reworded for the default-on world: deployments whose compaction policy cannot cover lone-load rowsets should disable this until the deferred-rowset compaction backstop lands. SNII UT 795/795 and IndexStorage fixture family 64/64 with the new default (every defer-sensitive test pins the config explicitly).
Member
Author
|
run buildall |
Contributor
Cloud UT Coverage ReportIncrement line coverage Increment coverage report
|
Contributor
FE UT Coverage ReportIncrement line coverage |
### What problem does this PR solve? Issue Number: None\n\nRelated PR: apache#64909\n\nProblem Summary: The PR build omitted the datasketches-cpp installation step from build.sh, so BE compilation could not find DataSketches/hll.hpp. Restore build.sh to origin/master, including the third-party setup and standard build flow. ### Release note\n\nNone ### Check List (For Author)\n\n- Test: Manual test\n - source custom_env.sh && export DORIS_PARALLELISM=$(nproc) && ./build.sh --be -j${DORIS_PARALLELISM}\n- Behavior changed: No\n- Does this need documentation: No
Member
Author
|
run buildall |
1 similar comment
Member
Author
|
run buildall |
Contributor
TPC-H: Total hot run time: 29636 ms |
Contributor
TPC-DS: Total hot run time: 180137 ms |
Contributor
ClickBench: Total hot run time: 24.89 s |
airborne12
added a commit
to airborne12/apache-doris
that referenced
this pull request
Jul 13, 2026
### What problem does this PR solve? Issue Number: None Related PR: apache#64909 Problem Summary: SNII inverted index files were finalized in begin_close(), but the underlying idx file writer was not closed until finish_close(). Load stream records segment file sizes between those phases, so it could observe an open .idx file and abort INSERT with "calc file size failed ... is not closed". Close the SNII idx writer asynchronously from begin_close(), leaving finish_close() to wait for completion. The previous local fix also restored only PeerCacheNodes after the file-cache profile trimming commit, but that trimming had removed the update path for still-declared segment_footer_index counters and peer cache CG/race/lazy counters. Restore those profile updates without reintroducing the removed redundant physical/block metrics, and extend the scanner profile unit test to cover all restored counters. ### Release note None ### Check List (For Author) - Test: - Build: ./build.sh --be - Manual test: deployed current output FE/BE after cleaning output runtime metadata; SHOW FRONTENDS and SHOW BACKENDS both Alive - Regression test: ./run-regression-test.sh --run -d inverted_index_p0/storage_format -s test_storage_format_snii - Unit Test: env LSAN_OPTIONS=exitcode=0 ./run-be-ut.sh --run --filter='IndexFileWriterTest.EmptySniiBeginCloseClosesUnderlyingWriter:FileScannerV2Test.FileCacheStatisticsArePublishedToScannerProfile' - Static check: git diff --check -- be/src/io/cache/block_file_cache_profile.cpp be/test/exec/scan/file_scanner_v2_test.cpp - Static check: PATH=/home/jiangkai/workspace/bin/ldb_toolchain/bin:$PATH build-support/check-format.sh - Static check: build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN attempted; local function-size warning removed, still blocked by toolchain stddef.h lookup and existing be/src/core/types.h unmatched NOLINTEND - Behavior changed: No - Does this need documentation: No
### What problem does this PR solve? Issue Number: None Related PR: apache#64909 Problem Summary: SNII inverted index files were finalized in begin_close(), but the underlying idx file writer was not closed until finish_close(). Load stream records segment file sizes between those phases, so it could observe an open .idx file and abort INSERT with "calc file size failed ... is not closed". Close the SNII idx writer asynchronously from begin_close(), leaving finish_close() to wait for completion. The previous local fix also restored only PeerCacheNodes after the file-cache profile trimming commit, but that trimming had removed the update path for still-declared segment_footer_index counters and peer cache CG/race/lazy counters. Restore those profile updates in FileCacheProfileReporter::update(), matching the existing profile update layout, without reintroducing the removed redundant physical/block metrics. Extend the scanner profile unit test to cover all restored counters. ### Release note None ### Check List (For Author) - Test: - Build: ./build.sh --be - Manual test: deployed current output FE/BE after cleaning output runtime metadata; SHOW FRONTENDS and SHOW BACKENDS both Alive - Regression test: ./run-regression-test.sh --run -d inverted_index_p0/storage_format -s test_storage_format_snii - Unit Test: env LSAN_OPTIONS=exitcode=0 ./run-be-ut.sh --run --filter='IndexFileWriterTest.EmptySniiBeginCloseClosesUnderlyingWriter:FileScannerV2Test.FileCacheStatisticsArePublishedToScannerProfile' - Static check: git diff --check -- be/src/io/cache/block_file_cache_profile.cpp - Static check: PATH=/home/jiangkai/workspace/bin/ldb_toolchain/bin:$PATH build-support/check-format.sh - Static check: build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN attempted; local function-size warnings are annotated, still blocked by toolchain stddef.h lookup - Behavior changed: No - Does this need documentation: No
Member
Author
|
run buildall |
Contributor
Cloud UT Coverage ReportIncrement line coverage Increment coverage report
|
Contributor
TPC-H: Total hot run time: 29758 ms |
Contributor
TPC-DS: Total hot run time: 179863 ms |
Contributor
ClickBench: Total hot run time: 25.01 s |
Contributor
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What problem does this PR solve?
Issue Number: N/A
Related PR: N/A
Problem Summary:
This PR integrates the SNII inverted index storage format into Doris as a separate storage-format path. SNII index files are written and read through the SNII implementation instead of falling through to the existing inverted-index storage stack, while analyzer usage remains shared where needed.
The change adds SNII-specific handling in index file reader/writer paths, preserves Doris IO context during SNII reads, bounds expanding prefix/regexp/wildcard/phrase-prefix queries before materializing all matching terms, and skips old inverted-index compaction/drop-index handling for SNII files. Unsupported SNII paths are rejected explicitly, including BKD-backed non-string indexes, ANN indexes, and BUILD INDEX.
The PR also adds inverted-index IO profile metrics for comparing real remote index scan behavior, then optimizes SNII high-df term and phrase hot paths found during cloud_sim E2E benchmarking. CPU profiles showed phrase execution dominated by docid conjunction, PFOR PRX decode, selective PRX CSR compaction, and exact two-term phrase verification overhead, not remote I/O. The optimization streams eligible term/prefix/regexp/wildcard results directly into Roaring, emits dense docid windows as ranges, carries PRX doc ordinal context through phrase execution, builds selected PRX count ranges during PFOR decode, skips redundant final-candidate filtering, adds sparse galloping docid conjunction paths, caps reserve sizes to the maximum possible match count, uses a chunk-level merge verifier for non-repeated two-term phrases, adds low-bit PFOR unpack fast paths, and uses bounded-span bitset/rank intersection for phrase candidate ordinal mapping.
Hotspot analysis for
MATCH_PHRASE 'failed order'on the 10B cloud_sim dataset:intersect_window_candidates_with_ordinals20.2%,pfor_decode16.3%, selected PFOR range building 11.5%, and PRX position materialization 11.5%.pfor_decodedropped to 6.6%, while docid conjunction became the dominant remaining self-CPU at 20.1%.Representative cloud_sim cold benchmark on the 10B textbench dataset:
Release note
Add SNII inverted index storage format and reject unsupported BKD, ANN, and BUILD INDEX operations for SNII.
Check List (For Author)
Test
./bootstrap.sh run -- --run -d inverted_index_p0/storage_format -s test_storage_format_snii -forceGenOut./bootstrap.sh run -- --run -d inverted_index_p0/storage_format -s test_storage_format_snii./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.IndexDefinitionTest,org.apache.doris.alter.IndexChangeJobTest./run-be-ut.sh --run --filter='SniiPhraseQueryTest.*:SniiTermQueryTest.*:SniiPrxPodTest.*:SniiPforTest.*'./build.sh --be --fe -j 192./build.sh --be -j 192./build-support/clang-format.sh be/src/storage/index/snii/core/src/encoding/pfor.cpp be/src/storage/index/snii/core/src/query/docid_conjunction.cpp be/test/storage/index/snii_query_test.cpp./build-support/check-format.sh be/src/storage/index/snii/core/src/encoding/pfor.cpp be/src/storage/index/snii/core/src/query/docid_conjunction.cpp be/test/storage/index/snii_query_test.cppgit diff --checkbuild-support/run-clang-tidy.sh --build-dir be/build_Release/mnt/disk1/jiangkai/cloud_sim/jiangkai_test, verified current-user MS/recycler/FE/BE processes, and verifiedSHOW BACKENDSAlive=true.MATCH_PHRASEandMATCH_PHRASE_PREFIXsmoke queries againsttextbench_10b_perf.otel10b_phrase40_snii.OP_term_high_df,PH5_phrase_failed_order, andPP5_phrase_prefix_failed./mnt/disk15/jiangkai/textbench/runs/20260628_phrase_cpu_opt_final_verified.Behavior changed:
Does this need documentation?
Check List (For Reviewer who merge this PR)