Skip to content

2.0.0#31

Merged
jordansrowles merged 265 commits into
mainfrom
2.0.0
Jul 14, 2026
Merged

2.0.0#31
jordansrowles merged 265 commits into
mainfrom
2.0.0

Conversation

@jordansrowles

@jordansrowles jordansrowles commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Note

Attempting to use this version with an index created with any previous version, will not work. You need to reindex, and build it again

Important

Now feature frozen. If it hasn't been ticked off in #24 then it's not made cut for this version. Bug fixes, refactorings, and updates to existing features only

  • CodecKit
    • Composable binary codec framework (VarInt, fixed-width, CRC/xxHash integrity, Deflate, version envelopes, immutable registry)
  • LINQ provider
    • LeanQueryable with expression-tree translation, source-gen field descriptors, AOT-compatible. Where/Select/First/Count/Any/Take/Skip/OrderBy
  • Vector quantisation
    • Int8 and binary (BBQ) with distance-aware KNN retrieval.
  • 3 new highlighters
    • PostingsHighlighter, TermVectorHighlighter, HybridHighlighter
  • 5 new similarities
    • BM25+, BM25L, 3 TF-IDF variants, 3 language-model similarities (Jelinek-Mercer, Dirichlet, Absolute Discounting)
  • StoreDocValues flag
    • Per-field opt-out from DocValues population, cutting buffer overhead and flush I/O.
  • Performance
    • Open-addressing BytesRefHash, VarInt delta positions (~32MB GC saved), DIM branchless scoring, SIMD ASCII lowercasing, FST prefix narrowing for wildcards, ConcurrentDictionary query cache, DWPT partition flushing, pooled Zstandard, streaming merge infrastructure.
  • Reliability
    • Dispose timeouts, ConfigureAwait(false) everywhere, swallowed-exception tracing, commit waits for merge, SafeFileHandle P/Invoke, SnowballStemmer base class, ShingleFilter/SynonymGraphFilter now actually work, SharedStandardIndex deduplicates Lucene setup across 10 benchmark suites.

SonarQube Cloud

@jordansrowles jordansrowles self-assigned this Jun 13, 2026
@jordansrowles jordansrowles added this to the 2.0.0 milestone Jun 13, 2026
PGO, Server GC, SkipLocalsInit, SIMD PackedIntCodec and BM25
scoring, QualifiedTermCache, BinaryWriter elimination, ISpanStemmer,
BBQ PopCount, Int8Distance SIMD, metrics false-sharing padding,
AggressiveOptimisation on 10 hot methods, striped TermOffsetCache,
QueryCache Interlocked counters, FST enumeration stackalloc,
stackalloc fast path in filters, Levenshtein AggressiveOptimisation.
- Remove PGO and Server GC from benchmark projects in
  Directory.Build.props to fix zero-allocation measurement
  artifact in 14 LuceneNet benchmarks under Server GC
- Restore inline scoring for selective TermQuery queries
  (docFreq < 256) to avoid 2KB stackalloc + two-pass
  batch overhead; the SIMD batch path is preserved for
  high-frequency terms where it provides benefit

Benchmark config fix verified: GC=Concurrent Workstation
Token filter regressions reduced from +7-16% to +3-6%
Gutenberg search regressions confirmed as environmental
noise (LuceneNet also regressed same amount)
Include Lucene.NET parity for Dirichlet and Jelinek-Mercer, plus Boolean query variants.
…ET term-vector and re-analysis highlighters.
…tringField, NumericField, StoredField and BinaryField default to true; TextField and VectorField default to false. Also fixed a double-byte-copy in AddBinaryDocValue where the string overload encoded to UTF-8 then called the span overload which ToArray'd a second time.
… new scoring models, language-model similarities, highlighters, StoreDocValues flag, profiling project, and indexing hot-path performance work.
…ld boost and presence dictionaries across the first pass; second pass reads values directly from lazy enumeration, keeping peak memory at O(largest single field) instead of O(all fields)
…hannel consumer on a dedicated thread, eliminating thread-pool starvation from lock contention in async continuations; remove AcquireBackpressureSlotAsync with it swallowed ObjectDisposedException
…rmVectors helper; DWPT IndexTextField applies char filters and conditionally stores offsets based on StoreTermVectors; DWPT constructor takes IndexWriterConfig instead of bool storePayloads
…eases through ReleaseSemaphoreSlots to guard against ObjectDisposedException after Dispose drain timeout; call MarkIndexingFailed before breaking out of the drain loop
…ecKit envelope version via TermDictionaryReader.Open + EnumerateAllTerms; add version-check fast path that copies or returns when already current
…onstantExpression, rejecting property access and static member expressions that would execute arbitrary code at query translation time
… escape pairs resolved by string.Create Unescape helper, allowing literal colons in URLs, timestamps, file paths, and operator characters in field values
…ream+BinaryReader using FileShare.Read|FileShare.Delete to avoid mmap file-locking on Windows after Dispose
…tion to handle transient Windows mmap handle release delays after TermDictionaryReader.Open disposes its IndexInput
…try with retry wrappers (Move, Copy, Delete, DeleteDirectory, ReadLines, file/directory/enumeration helpers); replace IndexAtomicFileWriter retry loop and RetryFileOperation helper with FileOpenRetry primitives to eliminate Windows file-locking failures from Defender and mmap handle release delays
@jordansrowles

Copy link
Copy Markdown
Owner Author

"It works on my machine"

Works on my local Linux, local Windows, GitHub Linux
GitHub Windows shows build success, logs show errors

I hate Windows.

…edAccessException so retry actually fires on Windows file-locking; bump retry parameters from 5×10ms to 25×50ms to absorb CI Defender delays; add bufferSize/FileOptions Open overload; route IndexOutput through FileOpenRetry.Open
@jordansrowles

jordansrowles commented Jul 12, 2026

Copy link
Copy Markdown
Owner Author

Pre-Release Benchmark Acceptance

Benchmarks have run. These need sorting before release:

  • deletion-commit
    • Performance is 0.62x worse, and allocation is 0.77x worse
  • indexsort-index
    • Performance is 0.38x worse, but allocation is 1.19x better
  • indexsort-search
    • 1.71x slower, and allocation is 1.24x worse
  • mlt (MoreLikeThis)
    • Broken, needs fixing. Specifically the 'DefaultParams' and 'NoBoost' (7,190 vs 59)
    • Except: 'HighMinDocFreq' is actually 49x times faster, 79x less memory intensive
  • terminset (SetSize's: 5, 20, 100)
    • LeanCorpus_BooleanQuery_Should: Perf superlinear scaling (time growing faster than term count k)
    • All: Allocation is opposite: 5 = 40x less, 10 = 54x less, 100 = 60x less
  • aggregation
    • SearchWithStats 689 vs 127, 143632 B vs 58325 B

There are benchmarks that are close in performance (or Lucene.Net has a very slight gain), but they're minor enough for next minor release. But even all those, we win on allocation (often by a lot). Like TermQuery:"said" which is 1.23x slower, but allocates 30.28x less memory.


On a better note, somethings have a slight performance increase, best I've noticed

  Performance   Allocation  
FuzzyQuery : nohit-edit2 915x better 3.1 ms vs 0.0034 ms 355x better 1.9 MB vs 0.0055 MB

Thanks to work on the FST. We don't need to traverse the whole graph if the root node doesn't have a hit

… cache MLT terms, route deletion-commit by segment

Replace double-search in SearchWithAggregations, SearchWithCollapse, and SearchWithFacets with ISideCollector invoked during postings iteration. Inline FunctionScore scoring for TermQuery inners to avoid TopNCollector(reader.MaxDoc) allocation. Fix ExecuteShouldOnlyHeap to use PopRoot/InsertHeap multi-way merge instead of rebuilding the heap every iteration, and raise HeapThreshold to 64. Cache MLT candidate terms with ConcurrentDictionary generation-swap eviction. Switch MLT to string-based GetDocFreqByQualified for _termOffsetCache hits. Route numeric id-field delete terms to the single segment containing their doc ID, avoiding O(K*S) FST lookups. Add index-sort early termination path when sort matches index sort. Fix LuceneNet aggregation and collapse benchmarks to iterate all matching documents.
…AOT) which dropped VSTest support on .NET 10+
…le locking Please for the love of god Window, please
…s; accept version 0 in NormsReader for migration tests
…perations fail fast, read/move/delete/copy use platform-aware transient retry (5x200ms on Windows, zero on Linux)

FileOpenRetry.Open no longer retries because creating files or acquiring exclusive locks is either an atomic success or a permanent conflict that retrying cannot resolve. This was causing WriteLock_ConcurrentWriterConstruction to time out after 50x100ms=5s per failed thread with 16 threads contending

OpenRead and OpenReadDelete now have their own inline retry loops instead of delegating to the no-retry Open. Move/Delete/Copy/DeleteDirectory use transient retry scoped to the few hundred milliseconds that Defender AV scanning can hold a handle

On Linux, TransientMaxRetries is zero so all transient-retry paths become direct calls with no overhead.

Collateral: TestDirectoryFixture cleanup uses FileOpenRetry.DeleteDirectory for resilience IndexBackup.CopyFileAtomically switched to OpenReadDelete (read path, needs retry).
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3.2% Duplication on New Code (required ≤ 3%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

💡 Need a hand with PR review? Try Gitar by Sonar!

@jordansrowles
jordansrowles merged commit f6c4194 into main Jul 14, 2026
7 of 8 checks passed
@jordansrowles
jordansrowles deleted the 2.0.0 branch July 18, 2026 13:10
@jordansrowles
jordansrowles restored the 2.0.0 branch July 18, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant