diff --git a/changelog/unreleased/SOLR-17764-retry-closedchannel.yml b/changelog/unreleased/SOLR-17764-retry-closedchannel.yml new file mode 100644 index 00000000000..d7655a26243 --- /dev/null +++ b/changelog/unreleased/SOLR-17764-retry-closedchannel.yml @@ -0,0 +1,9 @@ +title: > + CloudSolrClient now retries requests that fail with ClosedChannelException, treating a connection + dropped mid-request the same as other communication errors. +type: fixed +authors: + - name: Serhiy Bzhezytskyy +links: + - name: SOLR-17764 + url: https://issues.apache.org/jira/browse/SOLR-17764 diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java index 220f1a72f64..16274e79551 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java @@ -23,6 +23,7 @@ import java.lang.invoke.MethodHandles; import java.net.SocketException; import java.net.UnknownHostException; +import java.nio.channels.ClosedChannelException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -208,7 +209,9 @@ public ClusterState getClusterState() { /** Is this a communication error? We will retry if so. */ protected boolean wasCommError(Throwable t) { - return t instanceof SocketException || t instanceof UnknownHostException; + return t instanceof SocketException + || t instanceof UnknownHostException + || t instanceof ClosedChannelException; } @Override diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientCommErrorTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientCommErrorTest.java new file mode 100644 index 00000000000..09757195548 --- /dev/null +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientCommErrorTest.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.client.solrj.impl; + +import java.io.IOException; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.channels.ClosedChannelException; +import org.apache.solr.SolrTestCaseJ4; + +/** Unit tests for {@link CloudSolrClient#wasCommError(Throwable)}, which drives request retries. */ +public class CloudSolrClientCommErrorTest extends SolrTestCaseJ4 { + + public void testCommErrorClassification() throws Exception { + try (ProbeClient client = new ProbeClient()) { + // connection-level failures: the target node is gone, so retrying on a fresh node makes sense + assertTrue(client.wasCommError(new SocketException("connection reset"))); + assertTrue(client.wasCommError(new UnknownHostException("no such host"))); + // a connection dropped mid-request (e.g. an HTTP/2 GOAWAY on server shutdown) surfaces as a + // ClosedChannelException; it is the same "node went away" case and should be retried too + assertTrue(client.wasCommError(new ClosedChannelException())); + + // not comm errors: these should not trigger a comm-error retry + assertFalse(client.wasCommError(new IOException("some other io problem"))); + assertFalse(client.wasCommError(new RuntimeException("boom"))); + } + } + + /** Minimal concrete subclass so the protected wasCommError can be exercised in isolation. */ + private static final class ProbeClient extends CloudSolrClient implements AutoCloseable { + ProbeClient() { + super(true, true, false); + } + + @Override + protected LBSolrClient getLbClient() { + return null; + } + + @Override + public ClusterStateProvider getClusterStateProvider() { + return null; + } + + @Override + public HttpSolrClient getHttpClient() { + return null; + } + } +}