fabriziofortino opened a new pull request, #2828:
URL: https://github.com/apache/jackrabbit-oak/pull/2828

   - Offload Elasticsearch async response processing from the HTTP I/O thread 
to `ForkJoinPool.commonPool()` by replacing `whenComplete` with 
`whenCompleteAsync`
     - Capture `responseTimeMs` at callback entry and pass it through to 
`onSuccess/onFailure` so that `ELASTIC_QUERY_TOTAL_TIME` reflects the actual 
query round-trip time
   
   ### Problem
   
    The `CompletableFuture` returned by the Elasticsearch async client 
completes on the `elasticsearch-rest-client` I/O thread (Apache HttpAsyncClient 
I/O dispatcher). With `whenComplete`, the `handleResponse` callback — including 
`onSuccess` and its hit processing loop — runs inline on that same thread.
   
   During hit processing, `onSuccess` iterates over search hits and calls 
`SearchHitListener.on(hit)` for each result. In 
`ElasticResultRowAsyncIterator.on()`, two operations can block the I/O thread:
   
   1. `rowInclusionPredicate.test(path)` — evaluates ACLs, which may require 
remote calls to the underlying persistence layer (e.g. MongoDB, segment store)
   2. `queue.offer(resultRow, enqueueTimeoutMs, TimeUnit.MILLISECONDS)` — 
blocks when the bounded result queue is full and the consumer is slow to drain 
it
   
   Since the I/O dispatcher threads are shared across all HTTP connections, 
blocking one thread delays response reading and `CompletableFuture` completion 
for other concurrent Elasticsearch queries.
   This causes `ELASTIC_QUERY_TOTAL_TIME` to include not just the actual query 
round-trip (ES server processing + network), but also the I/O thread scheduling 
delay caused by other queries' blocked callbacks.
   
   Evidence: Adding `LOG.info("handleResponse running on thread: {}", 
Thread.currentThread().getName())` to `handleResponse` confirmed the callback 
runs on `elasticsearch-rest-client-1-thread-N`, the I/O dispatcher threads.
   
   ### Relevant documentation:
   - 
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CompletableFuture.html:
 "Actions supplied for dependent completions of non-async methods may be 
performed by the thread that completes the current CompletableFuture"
   - https://issues.apache.org/jira/browse/HTTPCLIENT-1805: Apache HttpClient 
lead warns to "keep code executed in callbacks as light as possible given the 
same I/O dispatch thread has to handle I/O events of multiple connections"
   
   ### Fix
   
   `whenComplete` → `whenCompleteAsync`: Response processing now runs on 
`ForkJoinPool.commonPool()` instead of the I/O dispatcher thread. This frees 
the I/O thread immediately after the future completes, preventing cross-query 
interference.
   
   `responseTimeMs` parameter: `System.currentTimeMillis()` is captured at the 
entry of the `whenCompleteAsync` lambda and passed through `handleResponse` to 
`onSuccess/onFailure`. This ensures `ELASTIC_QUERY_TOTAL_TIME` measures the 
actual query round-trip (ES server time + network + response deserialization + 
minimal ForkJoinPool scheduling overhead) rather than including arbitrary I/O 
thread contention delays.
   
   ### Risk assessment
   
   - Thread safety: The semaphore already ensures at most one in-flight request 
per scanner. `searchStartTime` is written before the async call and read in the 
callback; the `CompletableFuture` chain guarantees happens-before. No new 
thread-safety concerns.
   - `ForkJoinPool` blocking: `queue.offer` and `rowInclusionPredicate.test` 
can block, but `queue.offer` is bounded by `enqueueTimeoutMs` and the common 
pool can compensate with additional threads. This is far better than blocking 
the I/O dispatcher threads which also prevents socket reads for other 
connections.
   - Cancellation: `ongoingRequest.cancel(true)` behaves the same — it cancels 
the `whenCompleteAsync` stage. The `isClosed` check in `handleResponse` guards 
against processing after close, same as before.


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

Reply via email to