CH-Abhinav opened a new pull request, #16765:
URL: https://github.com/apache/lucene/pull/16765

   ### Description
   Fixes #16729.
   
   #### Problem
   In `HnswConcurrentMergeBuilder`, $N$ workers were previously constructed as 
greedy tasks where each worker executed an internal loop draining batches from 
a shared atomic counter (`workProgress`) until all vectors were added. These 
$N$ tasks were submitted in a single call to `TaskExecutor#invokeAll`.
   
   When `ConcurrentMergeScheduler.CachedExecutor` is briefly saturated at the 
time of submission (i.e. `maxThreadCount - mergeThreads.size() - 1 <= 0` due to 
concurrent merges), it falls back to executing the task inline on the calling 
thread. Because worker 0 was an unbounded loop draining all available batches, 
it consumed 100% of the merge's work inline before returning control to the 
submit loop. Even if sibling merges completed milliseconds later and freed up 
intra-merge thread capacity, helper threads were never enlisted, locking the 
entire merge to 1.00x concurrency (running single-threaded for its entire 
duration).
   
   #### Solution
   1. **Chunked Task Submission**: Instead of submitting $N$ long-lived greedy 
tasks, the ordinal space `[0, maxOrd)` is partitioned into discrete batch tasks 
of `batchSize` (default 2048) and submitted to `TaskExecutor#invokeAll`.
   2. **Worker Pool**: A `BlockingQueue<ConcurrentMergeWorker> workerPool = new 
ArrayBlockingQueue<>(workers.length)` holds the pre-initialized workers (each 
with its own `RandomVectorScorerSupplier` and scratch buffers).
   3. **Mid-Merge Concurrency Recovery**: When a task runs (either on an 
intra-merge pool thread or inline on the calling thread), it leases a worker 
from `workerPool`, executes a single slice `[start, end)`, and releases the 
worker back in a `finally` block. If the calling thread must run a task inline, 
it only processes one batch (~50ms) rather than draining the whole merge, 
allowing subsequent batches to be scheduled onto helper threads as soon as 
sibling merges finish.
   4. **Cleanup**: Removed unused `workProgress`, `run(int maxOrd)`, and 
`getStartPos(int maxOrd)` from `ConcurrentMergeWorker`.
   
   #### Tests
   - Added `TestHnswConcurrentMergeSubmission`:
     - Reproduces organic thread contention using stock 
`ConcurrentMergeScheduler`, `TieredMergePolicy`, and vector merges $> 
50\text{MB}$ (clearing `MIN_BIG_MERGE_MB`).
     - Verifies that a merge outliving sibling merges recovers intra-merge 
threads mid-merge (`batchesByThread.size() > 1`).
     - Annotated with `@LuceneTestCase.Monster` and `@Repeat(iterations = 3)` 
given segment size and indexing requirements.
   - Verified `./gradlew check -x test` passes all precommit checks (licenses, 
ecj linter for main and test, forbidden APIs, and tidy formatting).
   - Verified existing tests (`TestConcurrentMergeScheduler`, 
`TestHnswFloatVectorGraph`) pass without regressions.


-- 
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