From af6b33f2331c8e6e658fe42c6c9cc83e7d81151e Mon Sep 17 00:00:00 2001 From: James Vanneman Date: Tue, 30 Dec 2025 11:03:50 -0500 Subject: [PATCH] PR 2402 added support for CompletableFuture which accidentally introduced a double registration bug in HttpJettySolrClient by calling onRequestQueued/onComplete twice with asyncListener.queuedListener/completeListener This introduces three undesirable scenarios: Potential deadlock due to a surge in requests that all acquire 1 permit and cannot acquire a second Unnecessary system throttling when a request acquires a single permit but can't acquire a second due to in flight requests and new requests that arrived after the first token acquisition but before the second. Half the number of permits available --- ...SOLR-18051-fix-double-registration-bug.yml | 8 ++ .../solrj/jetty/HttpJettySolrClient.java | 86 +++++++++---------- 2 files changed, 49 insertions(+), 45 deletions(-) create mode 100644 changelog/unreleased/SOLR-18051-fix-double-registration-bug.yml diff --git a/changelog/unreleased/SOLR-18051-fix-double-registration-bug.yml b/changelog/unreleased/SOLR-18051-fix-double-registration-bug.yml new file mode 100644 index 00000000000..0181f211698 --- /dev/null +++ b/changelog/unreleased/SOLR-18051-fix-double-registration-bug.yml @@ -0,0 +1,8 @@ +# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc +title: Fix phaser/semaphore double registration bug in HttpJettySolrClient +type: fixed # added, changed, fixed, deprecated, removed, dependency_update, security, other +authors: + - name: James Vanneman +links: + - name: SOLR-18051 + url: https://issues.apache.org/jira/browse/SOLR-18051 diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java index 072a2add953..cac90ba4670 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java @@ -401,51 +401,47 @@ public CompletableFuture> requestAsync( future.completeExceptionally(e); return future; } - mrrv.request - .onRequestQueued(asyncTracker.queuedListener) - .onComplete(asyncTracker.completeListener) - .send( - new InputStreamResponseListener() { - // MDC snapshot from requestAsync's thread - MDCCopyHelper mdcCopyHelper = new MDCCopyHelper(); - - @Override - public void onHeaders(Response response) { - super.onHeaders(response); - InputStreamResponseListener listener = this; - executor.execute( - () -> { - InputStream is = listener.getInputStream(); - try { - NamedList body = - processErrorsAndResponse(solrRequest, response, is, url); - mdcCopyHelper.onBegin(null); - log.debug("response processing success"); - future.complete(body); - } catch (CancellationException e) { - mdcCopyHelper.onBegin(null); - log.debug("response processing cancelled", e); - if (!future.isDone()) { - future.cancel(true); - } - } catch (Throwable e) { - mdcCopyHelper.onBegin(null); - log.debug("response processing failed", e); - future.completeExceptionally(e); - } finally { - log.debug("response processing completed"); - mdcCopyHelper.onComplete(null); - } - }); - } - - @Override - public void onFailure(Response response, Throwable failure) { - super.onFailure(response, failure); - future.completeExceptionally( - new SolrServerException(failure.getMessage(), failure)); - } - }); + mrrv.request.send( + new InputStreamResponseListener() { + // MDC snapshot from requestAsync's thread + MDCCopyHelper mdcCopyHelper = new MDCCopyHelper(); + + @Override + public void onHeaders(Response response) { + super.onHeaders(response); + InputStreamResponseListener listener = this; + executor.execute( + () -> { + InputStream is = listener.getInputStream(); + try { + NamedList body = + processErrorsAndResponse(solrRequest, response, is, url); + mdcCopyHelper.onBegin(null); + log.debug("response processing success"); + future.complete(body); + } catch (CancellationException e) { + mdcCopyHelper.onBegin(null); + log.debug("response processing cancelled", e); + if (!future.isDone()) { + future.cancel(true); + } + } catch (Throwable e) { + mdcCopyHelper.onBegin(null); + log.debug("response processing failed", e); + future.completeExceptionally(e); + } finally { + log.debug("response processing completed"); + mdcCopyHelper.onComplete(null); + } + }); + } + + @Override + public void onFailure(Response response, Throwable failure) { + super.onFailure(response, failure); + future.completeExceptionally(new SolrServerException(failure.getMessage(), failure)); + } + }); // SOLR-17916: Disable request aborting // future.exceptionally(