Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions changelog/unreleased/SOLR-7177-cusc-connection-error-url.yml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,14 @@ void sendUpdateStream() throws Exception {

} 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,90 @@ public void onError(Throwable ex) {
"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.
Expand Down
Loading