Skip to content

Fix permanent Lucene index corruption under concurrent indexing requests#35

Open
vany0114 wants to merge 1 commit into
feature23:mainfrom
DrivingThru:main
Open

Fix permanent Lucene index corruption under concurrent indexing requests#35
vany0114 wants to merge 1 commit into
feature23:mainfrom
DrivingThru:main

Conversation

@vany0114

@vany0114 vany0114 commented Jul 8, 2026

Copy link
Copy Markdown

Context

LuceneNetSearchIndexer.IndexDocuments opens a fresh IndexWriter per request. Lucene allows only a single IndexWriter per directory, so two concurrent indexing requests against the same index race:

  • the loser typically fails with LockObtainFailedException (HTTP 500), and
  • under sustained concurrency a torn commit permanently corrupts the segment files — every subsequent write to that index throws System.IO.FileNotFoundException: Could not find file '.../_N.si' until the index directory is deleted from disk.

We hit this in a CI environment where several services index into one shared index: 500 consecutive indexing requests failed after the corruption event, and the index never recovered on its own.

Repro

30 concurrent workers × 30 sequential mergeOrUpload requests against a freshly created index:

  • before: 11/900 requests succeed, 889 return 500; after the load stops, every sequential write still returns 500 (FileNotFoundException on segment files) — the index is permanently broken.
  • after: 900/900 succeed; the index stays healthy.

Change

A static ConcurrentDictionary<string, SemaphoreSlim> gates IndexDocuments per index name. Writes to the same index are serialized; requests against different indexes still run in parallel. Read paths are untouched.

Test plan

  • dotnet test — unit tests 134/134 pass; integration tests 24/25 (the single failure pre-exists on main and is unrelated).
  • Concurrency hammer described above against the docker image built from this branch.

🤖 Generated with Claude Code

IndexDocuments creates a fresh IndexWriter per request. Lucene allows only
one IndexWriter per directory, so two concurrent indexing requests against
the same index race: the loser fails with LockObtainFailedException, and
under sustained concurrency a torn commit corrupts the segment files
permanently (FileNotFoundException: .../_N.si on every subsequent write
until the index directory is deleted from disk).

Reproduced with 30 concurrent writers x 30 sequential mergeOrUpload
requests against a fresh index: 889/900 requests returned 500 and the
index remained broken after concurrency stopped. With this change the
same load succeeds 900/900 and the index stays healthy.

A per-index SemaphoreSlim serializes IndexDocuments per index name;
requests against different indexes still run in parallel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant