airborne12 opened a new pull request, #70260:
URL: https://github.com/apache/doris/pull/70260

   > Part of the SNII / text analysis / ANN CLucene removal series (index at 
the end). This PR depends on no other PR of the series. Its commits: 
`6100f8ea9f2` [test](be) Add a tokenizer benchmark that also digests every 
row's tokens, `3aa8e31f180` [fix](be) Fix three analyzer bugs that keep the 
analysis output from being frozen, `6e41ceb8f93` [test](be) Freeze the output 
of every text analyzer and ANN index file.
   
   ### What problem does this PR solve?
   
   Issue Number: None
   
   Related PR: None
   
   Problem Summary:
   
   **[test](be) Add a tokenizer benchmark that also digests every row's 
tokens** (`6100f8ea9f2`)
   
   The SNII text analysis is moving off CLucene one analyzer at a
   time, and every step has to show two things against the code before it: that 
it
   is faster, and that every row still gives the same tokens. There was no tool 
for
   either.
   
   benchmark_analyzer.hpp measures every builtin parser and a few custom chains 
on
   the path the SNII writer takes for a row (a new stream per row) and with one
   stream across rows, over corpora given as `.rows` files. With
   ANALYZER_BENCH_DIGEST set it registers only the benchmark "digest", which 
writes
   one line per analyzer, corpus and row: the number of terms and a digest of 
their
   bytes and positions. Diffing the files of two binaries names every row whose
   tokens changed, on real text and with the real kuromoji dictionary, which the
   unit tests cannot hold.
   
   analyzer_ab.py runs two binaries in alternating rounds and reports the 
median of
   the per-round throughput ratios, because the host load moves a single
   measurement by 10 to 20 percent. analyzer_corpus.py builds the corpora; the
   Chinese and Japanese ones are cut from the Wikipedia CirrusSearch dumps of
   2025-12-29, so the same date gives the same corpus. analyzer_benchmark.md 
lists
   the steps.
   
   The analysis rewrite is measured against this commit, branch
   snii-analysis-bench-baseline.
   
   **[fix](be) Fix three analyzer bugs that keep the analysis output from being 
frozen** (`3aa8e31f180`)
   
   Freezing the output of every text analyzer, in the next commit,
   ran into three bugs that no frozen digest can hold.
   
   1. The word_delimiter filter threw std::out_of_range for a token holding a 
NUL
      byte when catenate_words, catenate_numbers or catenate_all was set.
      WordDelimiterConcatenation::append called std::string::append(const char*,
      pos, n), which resolves to the string_view overload and measures the text 
up
      to its first NUL, so for "foo\0bar" the second part started past the end.
      The SNII and V1/V2 writers catch only CLuceneError and doris::Exception, 
so
      the error left the writer. The part is now appended from its offset.
   2. IKAnalyzer::tokenStream created its tokenizer with new and then called
      reset(), which throws when a value starts with a character cut short, 
such as
      "\xE4\xB8". The tokenizer and its buffers, about 100 KB, leaked once per 
such
      value, on the query path and on the SNII write path, which build a stream 
per
      value. A unique_ptr now owns it until reset() returns.
   3. The IK dictionary was loaded under std::call_once, which is used up even 
when
      loading fails. After one failure, for example a dictionary path that is 
fixed
      later, every IK analyzer kept failing until the BE restarted, and in the 
unit
      tests the result of the IK tests depended on whether a test with a bad 
path
      ran first. Dictionary::initial now publishes the dictionary only after it 
has
      loaded, and the next call tries again. reload() and getSingleton(cfg), 
which
      only existed to recover from that state, are removed.
   
   **[test](be) Freeze the output of every text analyzer and ANN index file** 
(`6e41ceb8f93`)
   
   The SNII text analysis is about to be rewritten without
   CLucene. A change in what an analyzer emits raises no error anywhere: queries
   just stop finding the rows indexed with the old tokens. So the output is 
frozen
   before any implementation changes, and every later step has to keep it.
   
   AnalysisGolden runs 155 analyzer configurations over 13 corpus families. The
   configurations are written the way users write them, as index properties and
   analyzer, normalizer, tokenizer and filter policies, so the matrix does not 
move
   when the implementation behind it does: every builtin parser with its 
lower_case
   and stopwords values, every custom tokenizer, token filter and char filter 
with
   its settings (pairwise covering arrays for the 16 pinyin and 9 word_delimiter
   switches), the index's own char filter, normalizers, and the normalize() of
   prefix and wildcard queries. The families aim at known risks: token windows 
at
   255 bytes and at 255 UTF-16 code units, batch sizes of 255 to 513 tokens,
   characters across 4096-byte chunks, invalid UTF-8, sampled jieba and IK
   dictionary words, unknown Han strings, every pair of word break classes, 
sampled
   BMP code points, NUL bytes and long lines. Each configuration runs with a new
   stream per line, as queries and the SNII writer read values, and with one 
stream
   across lines, as the V1/V2 writers do. Both must give the same XXH3 digest, 
the
   one frozen in analysis_golden.txt; on the current code no configuration
   disagrees. ANALYSIS_GOLDEN_FULL=1 scans every code point against
   analysis_golden_full.txt. A test checks that every component 
AnalysisFactoryMgr
   registers is in some configuration, and that the ICU analyzers give the same
   output with Turkish as the default locale. The kuromoji configurations use a
   small dictionary the test builds, because the real one is not in the
   repository. The 4030 jobs run on up to 16 threads.
   
   AnalysisDifferential compares the production english, standard, unicode,
   custom standard and chinese analyzers with their old implementations, which 
stay
   linked into the unit tests, on the same families and on one second of random
   input, and shows that an injected difference is caught.
   
   AnalysisWriteGolden freezes what the writers make of the tokens: the bytes of
   the SNII file, and, because V1/V2 files carry timestamps, the terms with 
their
   documents and positions read back, for chinese, ik, icu and a custom chain.
   AnnWriteGolden freezes every sub-file of HNSW, IVF and IVF_ON_DISK indexes 
in V2
   and in SNII, built on one OpenMP thread from vectors whose distances are 
exact
   in float, and written twice to show the bytes repeat.
   
   Lines of the digest files are only added: ANALYSIS_GOLDEN_REGEN=<commit> 
appends
   the missing ones and never rewrites one; ANALYSIS_GOLDEN_DUMP writes the 
token
   streams to diff two binaries.
   
   check-header-deps.py gets two rules for the code moving off CLucene (snii/,
   analysis/, query/, common/ and ann/ under storage/index): no file there, 
sources
   included, and no project header it reaches may include a CLucene header, and
   the code there may not name a CLucene symbol once comments and strings are
   stripped, because the PCH makes CLucene visible without an include. What 
still
   breaks the rules is listed by the file that breaks it, 31 and 51 entries; a
   missing or stale entry fails the gate, so the lists only shrink.
   
   ### Release note
   
   Fix the word_delimiter filter failing on values with a NUL byte when a 
catenate
   option is set, and a memory leak of the IK analyzer on values it cannot 
decode.
   
   ### Check List (For Author)
   
   - Test <!-- At least one of them must be included. -->
       - [ ] Regression test
       - [x] Unit Test
       - [x] 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 <!-- Add your reason?  -->
   
       Details:
       - `6100f8ea9f2` [test](be) Add a tokenizer benchmark that also digests 
every row's tokens:
           - BUILD_TYPE=RELEASE DORIS_BENCHMARK_HEADER=benchmark_analyzer.hpp
             ./build.sh --benchmark builds benchmark_test.
           - The digest mode over six corpora of 8 MB (four mixed ones, zh_wiki 
and
             ja_wiki) writes 5,240,060 lines in 67 s. A build with the separate
             dynamic_cast fix of the tokenizer base class, which keeps every 
token,
             gives a byte-identical file.
           - analyzer_ab.py, five rounds: that fix measures 1.84 on basic and 
1.64 on
             custom standard + lowercase. The A/A run stayed within 0.94 to 
1.08 per
             analyzer on a host at load 140 to 280, more than the 5% the method
             allows, so finer comparisons need a quieter host.
       - `3aa8e31f180` [fix](be) Fix three analyzer bugs that keep the analysis 
output from being frozen:
           - New: WordDelimiterFilterTest.CatenatePartsAcrossNul, and
             IKTokenizerTest.TestUndecodableValueThrowsWithoutLeakingTheStream, 
whose
             leak LeakSanitizer reports when the binary exits. Changed:
             IKTokenizerTest.TestDictionaryExceptionHandling checks that a 
failed load
             is retried and that the first loaded configuration stays.
           - ./run-be-ut.sh --run 
--filter='WordDelimiterFilterTest*:IKTokenizerTest*'
             passes on ASAN, also three times with GTEST_SHUFFLE=1 together 
with the
             golden tests of the next commit.
       - `6e41ceb8f93` [test](be) Freeze the output of every text analyzer and 
ANN index file:
           - ./run-be-ut.sh --run 
--filter='*AnalysisGolden*:*AnalysisDifferential*:*AnalysisWriteGolden*:*AnnWriteGolden*:SniiWriterGoldenBytes*:SniiGoldenCorpus*'
             passes on ASAN. The default set takes 33 s of wall time on 16 
threads
             for 515 s of work; ANALYSIS_GOLDEN_FULL=1 takes 920 s.
           - GTEST_SHUFFLE=1 with '*AnalysisGolden*:*IK*' passes three times, 
seeds
             56420, 13481 and 56363.
           - The same tests pass on a RELEASE build, and both builds write
             byte-identical digests (ANALYSIS_GOLDEN_WRITE, 2065 lines). The ANN
             vectors have small integer coordinates for that: with fractional 
ones
             the two builds wrote different HNSW graphs, because -O0 and -O3 
round
             faiss's distance sums differently.
           - build-support/run-clang-tidy.sh --base origin/master reports no 
warning
             on the changed lines.
           - Mutations, each built, run and reverted: SimpleTokenizer<char> 
cutting
             english tokens at 254 bytes instead of 255 fails the 4 english
             configurations; basic splitting tokens over 255 bytes instead of
             truncating them fails the 6 basic ones; IK lexemes ending one 
character
             later fail the 10 IK ones; jieba without its lowercasing fails the 
2
             chinese ones with lower_case=true. Removing only the first-byte 
condition
             of that lowercasing changes no digest: no token of the shipped
             dictionaries starts with a non-ASCII byte and holds an ASCII 
letter.
           - build-support/tests/test-build-hygiene-header-deps.sh passes, with 
new
             cases for an angle include two hops from a root, a root source 
file,
             stale entries in both tables, and CLucene words in comments and 
strings.
   
   - Behavior changed:
       - [ ] No.
       - [x] Yes.
           `3aa8e31f180`: A value with a NUL byte under word_delimiter with a 
catenate option is indexed instead of failing; a failed IK dictionary load is 
retried by the next IK analyzer instead of failing until a restart.
   
   - Does this need documentation?
       - [x] No.
       - [ ] Yes.
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to