Backport: Fix SearchableFields caching and CommitNow race condition (#426, #427)#428
Conversation
…426) When a SearchContext is created during application startup before any documents have been indexed, SearchableFields reads from the empty index reader and caches an empty array. After documents are indexed and the NRT reader is refreshed, IsSearcherCurrent() returns true (the refreshed reader IS current), so the SearchContext is reused with its stale empty _searchableFields cache. This causes ManagedQuery/Search(string) to generate queries with no fields, returning zero results even though documents exist in the index. Fix: only cache SearchableFields when the result is non-empty. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move WaitForChanges() into CommitNow() before the IndexCommitted event fires, and remove the redundant call from TimerRelease(). Previously, in the async commit path (timer-based), IndexCommitted fired before WaitForChanges() completed, creating a race condition where consumers reacting to the event could search with a stale NRT reader. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@greptile review |
Greptile SummaryThis PR backports two targeted bug fixes from the v4 branch to
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant C as Caller
participant CI as IndexCommiter
participant IW as IndexWriter
participant NRT as NrtReopenThread
participant H as IndexCommitted Handler
Note over C,H: BEFORE fix (async / TimerRelease path)
C->>CI: ScheduleCommit()
CI->>CI: TimerRelease()
CI->>IW: Commit()
CI->>H: IndexCommitted (⚠️ stale NRT reader!)
CI->>NRT: WaitForChanges() (too late)
Note over C,H: AFTER fix (all paths)
C->>CI: CommitNow()
CI->>IW: Commit()
CI->>NRT: WaitForChanges() (reader refreshed ✅)
CI->>H: IndexCommitted (fresh NRT reader ✅)
Last reviewed commit: 25c2036 |
Additional Comments (1)
Now that The same redundancy also appears in And likewise for the delete path (around line 519): |
CommitNow() now calls WaitForChanges() internally, so the explicit calls after CommitNow() in the !RunAsync paths of PerformIndexItemsInternal and PerformDeleteFromIndexInternal are redundant no-ops. Remove them and update comments for clarity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@greptile review |
Summary
Backports two bug fixes from the v4 branch to support/3.x. Both bugs were discovered during Umbraco.Cms.Search compatibility testing.
Fixes
1. SearchableFields caches empty result from initially empty index (#426)
File:
src/Examine.Lucene/Search/SearchContext.csSearchContext.SearchableFieldscaches its result after the first read. If accessed before any documents are indexed (e.g., during startup whenExamineMultiFieldQueryParseris constructed), an empty array is cached permanently. After documents are indexed,IsSearcherCurrent()returnstrueso theSearchContextis never recreated, leavingSearch(string)/ManagedQuery()with zero fields to search against.Fix: Only cache when the result is non-empty.
2. Race condition: IndexCommitted fires before NRT reader refresh (#427)
File:
src/Examine.Lucene/Providers/LuceneIndex.cs(nestedIndexCommiterclass)In the async commit path (
TimerRelease),CommitNow()firesIndexCommittedbeforeWaitForChanges()refreshes the NRT reader. Consumers reacting to the event may search with a stale reader.Fix: Move
WaitForChanges()intoCommitNow()before firing the event.Testing
ContentDeliveryApiTests.csbuild error is unrelated — it uses v4-only APIs)