Added
- LuceneNet TF-IDF similarity benchmark for comparing classic vector-space scoring against LeanCorpus (bfe0461dc)
- Similarity benchmarks now compare LeanCorpus BM25 term and Boolean queries with Lucene.NET (c42d2cf40)
- Linux kernel e2e cache telemetry, before-and-after measurements, ADR011, and concurrency coverage (14c971ccf)
- Lazy segment readers with a bounded 256-entry cache for lower open-time memory (14c971ccf)
- A Linux kernel code-search example now reproduces and measures highly segmented index startup costs (295194537)
DwptFlushSnapshotcaptures immutable DWPT state via collection swap so the DWPT can index new documents while the snapshot flushes independently (a14c71a80)SnapshotFlushSourceimplementsIFlushSourceover a snapshot soFlushCoreand the postings write path read from owned copies (a14c71a80)FlushPendingStatetracks in-flight detached flushes forMaxConcurrentFlushes > 1(a14c71a80)SegmentFlusher.FlushFromSnapshotnew entry point for snapshot-based flush I/O (a14c71a80)IFlushSource.CopySortedPostingsUtf8returns(byte[] TermUtf8, PostingAccumulator Acc)[]so flush sources produce term bytes directly fromBytesRefHash(a14c71a80)TermDictionaryWriter.BuildFstUtf8accepts pre-sorted(byte[], long)[]to eliminate the decode-to-string-then-re-encode round-trip in dictionary construction (a14c71a80)TermDictionaryReader.TryGetPostingsOffset(ReadOnlySpan\byte>)byte-native FST lookup for the delete hot path (a14c71a80)DeleteTermrecord struct with field ordinal, UTF-8 term bytes, and pre-encoded qualified-term prefix for compact delete storage (a14c71a80)QueueDeletewith deduplication onIndexWriterso hard deletes always win over soft deletes for the same field and term (a14c71a80)ByteBlockPoolandIntBlockPoolsegment-lifetime allocators in Codecs/Postings/ with bulk Reset (a14c71a80)- 6 integration tests for detached flush correctness covering concurrent flush and commit, snapshot consistency,
UpdateDocumentduringFlush,Rollback, andDispose(a14c71a80) - Architecture tests now guard namespace, dependency, storage and structural boundaries (49916e9b1)
HasBlockMetadatatoPostingsEnumfor exception-free WAND capability checks (f3305a51a)- Five integration tests to reproduce WAND crashes caused by absent terms in specific segments (f3305a51a)
- Add WAND searcher to
MoreLikeThisBenchmarksto directly compare WAND vs. scalar performance (f3305a51a) - Add
MoreLikeThisSingleSegmentBenchmarksto safely isolate WAND segment-topology effects (f3305a51a)
Changed
- Hot scoring loops now get document ID and term frequency in a single
PostingsEnumcall instead of two separate property accesses, cutting per-hit overhead in half (bfe0461dc) - Collection frequency lookups are now cached per
IndexSearcherinstance, avoiding repeated cross-segment computation for the same term (bfe0461dc) - Search benchmarks now measure cache misses, fair sorting, explicit parallel opt-in, and scalar single-segment MLT (e3f1d25a7)
- Benchmark runs now reuse standard search indexes and avoid duplicate jobs, cutting repeated setup and build time (c42d2cf40)
- Parallel,
SearcherManager, query-cache and segment-reader cache results are now easier to compare (c42d2cf40) - Heavy index data now loads on demand, with safe shared file lifetimes across directory instances (14c971ccf)
DwptManager.AddDocumentthreshold flush path captures snapshot underlock(dwpt), does I/O outside_writeLock, then briefly locks to publish (a14c71a80)DwptManager.FlushDwptPooldrains pending flushes first then flushes remaining DWPTs viaFlushFromSnapshot(a14c71a80)CommitCoreandPrepareCommitcallWaitForPendingFlushesbeforeFlushDwptPool(a14c71a80)UpdateDocumentandUpdateDocumentscallWaitForPendingFlushesbefore applying deletes so pending threshold-flush segments are published first (a14c71a80)SegmentFlusher.FlushCoresorts terms by UTF-8 byte order viaSequenceCompareToinstead ofstring.CompareOrdinal(a14c71a80)DeletionApplier.ApplyPendingDeletionsnow usesApplyDeletesByOrdinalwith byte-native FST lookup instead of string-qualified-term construction (a14c71a80)_pendingDeleteschanged fromList<(string,string,bool)>toList<DeleteTerm>with field ordinal and UTF-8 term storage (a14c71a80)DocumentsWriterPerThreadcollection fields changed fromreadonlyto mutable soResetAfterSnapshotcan swap them with fresh instances (a14c71a80)- NextSegmentOrdinal assignment in threshold flush uses Interlocked.Increment since the path no longer holds
_writeLock(a14c71a80) - Write to
SlowQueryLogasynchronously via a bounded channel to prevent slow I/O blocking search threads (3632683e4) - Codecs no longer depend on Search or Index internals (49916e9b1)
- File-system access is now encapsulated by the Store layer (49916e9b1)
PorterStemmerhas moved toRowles.LeanCorpus.Analysis.Stemmers(49916e9b1)- Optimise
CodecFileHeaderreads using bulkIndexInput.ReadBytesto reduce decoding overhead (9c77ff7a3)
Fixed
BlockJoinbenchmark LuceneNet baseline now usesScoreMode.Noneinstead ofMax, so both engines measure parent-document matching without child scoring (bfe0461dc)- Collapse and facet benchmarks now use proper Lucene.Net
SortedDocValuesand facet infrastructure instead of naive string deduplication, giving realistic comparison numbers (bfe0461dc) - Highlighter benchmarks now highlight a consistent 100 documents per operation instead of the entire corpus, removing a hidden scale factor from measurements (bfe0461dc)
- Parallel search benchmarks now disable merging in both engines, preserving the intended segment layout rather than letting it drift during the benchmark run (bfe0461dc)
- The Porter benchmark no longer includes temporary token-list and sink allocations (c42d2cf40)
- The single-segment MLT benchmark now builds and verifies an actual single-segment index (c42d2cf40)
- Warm searches now pin segment state once and reuse field data across scoring hot paths (c42d2cf40)
- Active postings and vector queries now keep their reader state alive until disposal (14c971ccf)
- Swallowed exceptions in deletion policies now emit OpenTelemetry
ActivityEventsso cleanup failures are observable (72b59fe49) PostingsEnumnow prevents copied instances from returning pooled buffers twice and waits for active enum operations before returning buffers to their pools (8cef21a86)- Dispose drains and publishes pending detached flushes before releasing the write lock so no flush work is lost on shutdown (a14c71a80)
StoredFieldsReadernow serialises concurrent document reads to prevent block cache corruption from multi-threaded stored-field access (ad7c40a35)- Replace
ConditionalWeakTablewithConcurrentDictionaryin SearcherManager to preventSearcherRefandIndexSearcherleaks on refresh (96c550269) - Vector readers now preserve mapped-file lifetime and deferred deletion behaviour (49916e9b1)
- Dispose
QuantisedVectorReaderinSegmentReader.Disposeto prevent memory-mapped file leaks (d6e96315b) - Wait for background refresh to exit in
SearcherManager.Disposeto prevent shutdownObjectDisposedExceptions(da60cdb49) - Cap
NumericAggregatorhistogram buckets at 100k to prevent OOM errors on extreme ratios (f5db3e27d) - Fix
ExecuteBooleanStreamingcrash on absent MLT terms by correctly handling empty postings (f3305a51a) - Pass segment-local
shouldCountexplicitly inExecuteShouldOnlyWandinstead of relying on array capacity (f3305a51a) - Clamp
estimatedGlobalto_totalDocCountin MLT extraction to prevent negative IDF (f3305a51a) - Fix empty queries in MLT benchmarks by explicitly setting
FieldNamesand adding aDebug.Assert(f3305a51a)