This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit fbf2015d9a5ed6872c1813c5e2077275ca16a9f2
Author: Serhiy Bzhezytskyy <[email protected]>
AuthorDate: Wed Sep 2 06:26:45 2026 +0300

    SOLR-7177: include the target URL when a ConcurrentUpdateSolrClient fails 
to connect (#4638)
    
    ConcurrentUpdateSolrClient now identifies the target server when an update 
fails to connect, instead of reporting a bare connection error with no URL.
    
    (cherry picked from commit 428b6250e5977b3643e3e2796417d17eabf9d3e4)
---
 .../SOLR-7177-cusc-connection-error-url.yml        | 11 +++
 .../solrj/impl/ConcurrentUpdateBaseSolrClient.java |  8 ++-
 .../impl/ConcurrentUpdateSolrClientTestBase.java   | 84 ++++++++++++++++++++++
 3 files changed, 102 insertions(+), 1 deletion(-)

diff --git a/changelog/unreleased/SOLR-7177-cusc-connection-error-url.yml 
b/changelog/unreleased/SOLR-7177-cusc-connection-error-url.yml
new file mode 100644
index 00000000000..f9743b7fcf5
--- /dev/null
+++ b/changelog/unreleased/SOLR-7177-cusc-connection-error-url.yml
@@ -0,0 +1,11 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+
+title: >
+  ConcurrentUpdateSolrClient now identifies the target server when an update 
fails to connect,
+  instead of reporting a bare connection error with no URL.
+type: fixed
+authors:
+  - name: Serhiy Bzhezytskyy
+links:
+  - name: SOLR-7177
+    url: https://issues.apache.org/jira/browse/SOLR-7177
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
index ce4c8673bb4..5baeaec60a9 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
@@ -340,8 +340,14 @@ public abstract class ConcurrentUpdateBaseSolrClient 
extends SolrClient {
 
           } catch (OutOfMemoryError | InterruptedException e) {
             throw e;
-          } catch (Throwable e) {
+          } catch (RemoteSolrException e) {
             handleError(e, docIds, collection);
+          } catch (Throwable e) {
+            handleError(
+                new SolrServerException(
+                    "Error from server at " + basePath + ": " + 
e.getMessage(), e),
+                docIds,
+                collection);
           } finally {
             try {
               consumeFully(rspBody);
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientTestBase.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientTestBase.java
index 7a4c387e408..c3e9184cec5 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientTestBase.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientTestBase.java
@@ -399,6 +399,90 @@ public abstract class ConcurrentUpdateSolrClientTestBase 
extends SolrTestCaseJ4
         "the per-id callback should not fire for a non-document error", 0, 
richCalls.get());
   }
 
+  /**
+   * A connection failure surfaces the target URL so the failing server is 
identifiable, rather than
+   * a bare socket error with no context.
+   */
+  @Test
+  public void testConnectionErrorIncludesUrl() throws Exception {
+    String unreachable = "http://127.0.0.1:1/solr";; // nothing listening -> 
connect failure
+    List<Throwable> errors = new CopyOnWriteArrayList<>();
+    ConcurrentUpdateBaseSolrClient.UpdateErrorHandler handler =
+        new ConcurrentUpdateBaseSolrClient.UpdateErrorHandler() {
+          @Override
+          public void onError(Throwable ex, List<String> ids, String 
collection) {
+            errors.add(ex);
+          }
+
+          @Override
+          public void onError(Throwable ex) {
+            errors.add(ex);
+          }
+        };
+
+    try (var httpClient = solrClient(null);
+        var concurrentClient =
+            errorHandlerConcurrentClient(unreachable, 10, 2, httpClient, 
handler)) {
+      SolrInputDocument doc = new SolrInputDocument();
+      doc.addField("id", "doc-1");
+      concurrentClient.add("collection1", doc);
+      concurrentClient.blockUntilFinished();
+    }
+
+    assertFalse("expected a connection error to be reported", 
errors.isEmpty());
+    boolean anyMentionsUrl =
+        errors.stream()
+            .anyMatch(
+                e -> {
+                  for (Throwable t = e; t != null; t = t.getCause()) {
+                    if (t.getMessage() != null && 
t.getMessage().contains("127.0.0.1:1")) {
+                      return true;
+                    }
+                  }
+                  return false;
+                });
+    assertTrue(
+        "connection error should identify the target server; got: " + errors, 
anyMentionsUrl);
+  }
+
+  /**
+   * A connection error is reported as a SolrServerException that preserves 
the original cause, so
+   * SolrCmdDistributor's checkRetry can unwrap it via getRootCause to decide 
whether to retry.
+   */
+  @Test
+  public void testConnectionErrorIsSolrServerExceptionPreservingCause() throws 
Exception {
+    String unreachable = "http://127.0.0.1:1/solr";;
+    List<Throwable> errors = new CopyOnWriteArrayList<>();
+    ConcurrentUpdateBaseSolrClient.UpdateErrorHandler handler =
+        new ConcurrentUpdateBaseSolrClient.UpdateErrorHandler() {
+          @Override
+          public void onError(Throwable ex, List<String> ids, String 
collection) {
+            errors.add(ex);
+          }
+
+          @Override
+          public void onError(Throwable ex) {
+            errors.add(ex);
+          }
+        };
+
+    try (var httpClient = solrClient(null);
+        var concurrentClient =
+            errorHandlerConcurrentClient(unreachable, 10, 2, httpClient, 
handler)) {
+      SolrInputDocument doc = new SolrInputDocument();
+      doc.addField("id", "doc-1");
+      concurrentClient.add("collection1", doc);
+      concurrentClient.blockUntilFinished();
+    }
+
+    assertFalse("expected a connection error to be reported", 
errors.isEmpty());
+    Throwable reported = errors.get(0);
+    assertTrue(
+        "connection error should be wrapped as SolrServerException; got: " + 
reported,
+        reported instanceof SolrServerException);
+    assertNotNull("the original cause must be preserved", reported.getCause());
+  }
+
   /**
    * A naive lambda handler only registers for per-id failures; an error not 
tied to identifiable
    * documents is logged by the default general callback and does not stop the 
client.

Reply via email to