This is an automated email from the ASF dual-hosted git repository. jdyer pushed a commit to branch feature/SOLR-17516-c in repository https://gitbox.apache.org/repos/asf/solr.git
commit 025a6c01814054c9b18471e66fee214f2ba3782b Author: jdyer1 <[email protected]> AuthorDate: Thu Oct 31 09:29:13 2024 -0500 tidy --- .../solr/client/solrj/impl/LBHttp2SolrClient.java | 135 +++-- .../impl/LBHttp2SolrClientIntegrationTest.java | 38 +- .../client/solrj/impl/LBHttp2SolrClientTest.java | 544 +++++++++++---------- 3 files changed, 361 insertions(+), 356 deletions(-) diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java index 12db741d231..3f39e5aaf55 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java @@ -140,59 +140,59 @@ public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClien CompletableFuture<Rsp> apiFuture = new CompletableFuture<>(); Rsp rsp = new Rsp(); boolean isNonRetryable = - req.request instanceof IsUpdateRequest || ADMIN_PATHS.contains(req.request.getPath()); + req.request instanceof IsUpdateRequest || ADMIN_PATHS.contains(req.request.getPath()); EndpointIterator it = new EndpointIterator(req, zombieServers); AtomicReference<CompletableFuture<NamedList<Object>>> currentFuture = new AtomicReference<>(); RetryListener retryListener = - new RetryListener() { + new RetryListener() { - @Override - public void onSuccess(Rsp rsp) { - apiFuture.complete(rsp); - } + @Override + public void onSuccess(Rsp rsp) { + apiFuture.complete(rsp); + } - @Override - public void onFailure(Exception e, boolean retryReq) { - if (retryReq) { - Endpoint url; - try { - url = it.nextOrError(e); - } catch (SolrServerException ex) { - apiFuture.completeExceptionally(e); - return; - } - MDC.put("LBSolrClient.url", url.toString()); - if (!apiFuture.isCancelled()) { - CompletableFuture<NamedList<Object>> future = - doAsyncRequest(url, req, rsp, isNonRetryable, it.isServingZombieServer(), this); - currentFuture.set(future); - } - } else { - apiFuture.completeExceptionally(e); - } + @Override + public void onFailure(Exception e, boolean retryReq) { + if (retryReq) { + Endpoint url; + try { + url = it.nextOrError(e); + } catch (SolrServerException ex) { + apiFuture.completeExceptionally(e); + return; + } + MDC.put("LBSolrClient.url", url.toString()); + if (!apiFuture.isCancelled()) { + CompletableFuture<NamedList<Object>> future = + doAsyncRequest(url, req, rsp, isNonRetryable, it.isServingZombieServer(), this); + currentFuture.set(future); } - }; + } else { + apiFuture.completeExceptionally(e); + } + } + }; try { CompletableFuture<NamedList<Object>> future = - doAsyncRequest( - it.nextOrError(), - req, - rsp, - isNonRetryable, - it.isServingZombieServer(), - retryListener); + doAsyncRequest( + it.nextOrError(), + req, + rsp, + isNonRetryable, + it.isServingZombieServer(), + retryListener); currentFuture.set(future); } catch (SolrServerException e) { apiFuture.completeExceptionally(e); return apiFuture; } apiFuture.exceptionally( - (error) -> { - if (apiFuture.isCancelled()) { - currentFuture.get().cancel(true); - } - return null; - }); + (error) -> { + if (apiFuture.isCancelled()) { + currentFuture.get().cancel(true); + } + return null; + }); return apiFuture; } @@ -203,34 +203,34 @@ public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClien } private CompletableFuture<NamedList<Object>> doAsyncRequest( - Endpoint endpoint, - Req req, - Rsp rsp, - boolean isNonRetryable, - boolean isZombie, - RetryListener listener) { + Endpoint endpoint, + Req req, + Rsp rsp, + boolean isNonRetryable, + boolean isZombie, + RetryListener listener) { String baseUrl = endpoint.toString(); rsp.server = baseUrl; req.getRequest().setBasePath(baseUrl); CompletableFuture<NamedList<Object>> future = - ((HttpSolrClientBase) getClient(endpoint)).requestAsync(req.getRequest()); + ((HttpSolrClientBase) getClient(endpoint)).requestAsync(req.getRequest()); future.whenComplete( - (result, throwable) -> { - if (!future.isCompletedExceptionally()) { - onSuccessfulRequest(result, endpoint, rsp, isZombie, listener); - } else if (!future.isCancelled()) { - onFailedRequest(throwable, endpoint, isNonRetryable, isZombie, listener); - } - }); + (result, throwable) -> { + if (!future.isCompletedExceptionally()) { + onSuccessfulRequest(result, endpoint, rsp, isZombie, listener); + } else if (!future.isCancelled()) { + onFailedRequest(throwable, endpoint, isNonRetryable, isZombie, listener); + } + }); return future; } private void onSuccessfulRequest( - NamedList<Object> result, - Endpoint endpoint, - Rsp rsp, - boolean isZombie, - RetryListener listener) { + NamedList<Object> result, + Endpoint endpoint, + Rsp rsp, + boolean isZombie, + RetryListener listener) { rsp.rsp = result; if (isZombie) { zombieServers.remove(endpoint.toString()); @@ -239,11 +239,11 @@ public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClien } private void onFailedRequest( - Throwable oe, - Endpoint endpoint, - boolean isNonRetryable, - boolean isZombie, - RetryListener listener) { + Throwable oe, + Endpoint endpoint, + boolean isNonRetryable, + boolean isZombie, + RetryListener listener) { try { throw (Exception) oe; } catch (BaseHttpSolrClient.RemoteExecutionException e) { @@ -285,11 +285,12 @@ public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClien listener.onFailure(new SolrServerException(e), false); } } + public static class Builder<C extends HttpSolrClientBase> { final C solrClient; protected final LBSolrClient.Endpoint[] solrEndpoints; long aliveCheckIntervalMillis = - TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS); // 1 minute between checks + TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS); // 1 minute between checks protected String defaultCollection; public LBHttp2SolrClient<C> build() { @@ -311,15 +312,13 @@ public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClien public Builder<C> setAliveCheckInterval(int aliveCheckInterval, TimeUnit unit) { if (aliveCheckInterval <= 0) { throw new IllegalArgumentException( - "Alive check interval must be " + "positive, specified value = " + aliveCheckInterval); + "Alive check interval must be " + "positive, specified value = " + aliveCheckInterval); } this.aliveCheckIntervalMillis = TimeUnit.MILLISECONDS.convert(aliveCheckInterval, unit); return this; } - /** - * Sets a default for core or collection based requests. - */ + /** Sets a default for core or collection based requests. */ @SuppressWarnings("unchecked") public Builder<C> withDefaultCollection(String defaultCoreOrCollection) { this.defaultCollection = defaultCoreOrCollection; diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java index 6500aa631e1..a4cf3292e81 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java @@ -16,6 +16,18 @@ */ package org.apache.solr.client.solrj.impl; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.invoke.MethodHandles; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.TimeUnit; import org.apache.lucene.util.IOUtils; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.client.solrj.SolrClient; @@ -33,19 +45,6 @@ import org.junit.BeforeClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.lang.invoke.MethodHandles; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.TimeUnit; - /** * Test for LBHttp2SolrClient * @@ -89,6 +88,7 @@ public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 { addDocs(solr[i]); } } + private void addDocs(SolrInstance solrInstance) throws IOException, SolrServerException { List<SolrInputDocument> docs = new ArrayList<>(); for (int i = 0; i < 10; i++) { @@ -118,8 +118,9 @@ public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 { } private LBClientHolder client(LBSolrClient.Endpoint... baseSolrEndpoints) { - if(random().nextBoolean()) { - var delegateClient = new Http2SolrClient.Builder() + if (random().nextBoolean()) { + var delegateClient = + new Http2SolrClient.Builder() .withConnectionTimeout(1000, TimeUnit.MILLISECONDS) .withIdleTimeout(2000, TimeUnit.MILLISECONDS) .build(); @@ -130,7 +131,8 @@ public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 { .build(); return new LBClientHolder(lbClient, delegateClient); } else { - var delegateClient = new HttpJdkSolrClient.Builder() + var delegateClient = + new HttpJdkSolrClient.Builder() .withConnectionTimeout(1000, TimeUnit.MILLISECONDS) .withIdleTimeout(2000, TimeUnit.MILLISECONDS) .withSSLContext(MockTrustManager.ALL_TRUSTING_SSL_CONTEXT) @@ -234,7 +236,7 @@ public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 { // wait maximum ms for serverName to come back up private void waitForServer( - int maxSeconds, LBHttp2SolrClient<?> client, int nServers, String serverName) + int maxSeconds, LBHttp2SolrClient<?> client, int nServers, String serverName) throws Exception { final TimeOut timeout = new TimeOut(maxSeconds, TimeUnit.SECONDS, TimeSource.NANO_TIME); while (!timeout.hasTimedOut()) { @@ -366,7 +368,7 @@ public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 { lbClient.close(); try { delegate.close(); - } catch(IOException ioe) { + } catch (IOException ioe) { throw new UncheckedIOException(ioe); } } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientTest.java index 790c17af8bf..09528afc427 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientTest.java @@ -39,294 +39,298 @@ import org.junit.Test; /** Test the LBHttp2SolrClient. */ public class LBHttp2SolrClientTest extends SolrTestCase { - /** - * Test method for {@link LBHttp2SolrClient.Builder} that validates that the query param keys - * passed in by the base <code>Http2SolrClient - * </code> instance are used by the LBHttp2SolrClient. - */ - @Test - public void testLBHttp2SolrClientWithTheseParamNamesInTheUrl() { - String url = "http://127.0.0.1:8080"; - Set<String> urlParamNames = new HashSet<>(2); - urlParamNames.add("param1"); - - try (Http2SolrClient http2SolrClient = - new Http2SolrClient.Builder(url).withTheseParamNamesInTheUrl(urlParamNames).build(); - LBHttp2SolrClient<Http2SolrClient> testClient = - new LBHttp2SolrClient.Builder<>(http2SolrClient, new LBSolrClient.Endpoint(url)) - .build()) { - - assertArrayEquals( - "Wrong urlParamNames found in lb client.", - urlParamNames.toArray(), - testClient.getUrlParamNames().toArray()); - assertArrayEquals( - "Wrong urlParamNames found in base client.", - urlParamNames.toArray(), - http2SolrClient.getUrlParamNames().toArray()); - } - } - - @Test - public void testSynchronous() throws Exception { - LBSolrClient.Endpoint ep1 = new LBSolrClient.Endpoint("http://endpoint.one"); - LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two"); - List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2); - - Http2SolrClient.Builder b = - new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS); - ; - try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b); - LBHttp2SolrClient<MockHttpSolrClient> testClient = new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) { - - String lastEndpoint = null; - for (int i = 0; i < 10; i++) { - String qValue = "Query Number: " + i; - QueryRequest queryRequest = new QueryRequest(new MapSolrParams(Map.of("q", qValue))); - LBSolrClient.Req req = new LBSolrClient.Req(queryRequest, endpointList); - LBSolrClient.Rsp response = testClient.request(req); - - String expectedEndpoint = - ep1.toString().equals(lastEndpoint) ? ep2.toString() : ep1.toString(); - assertEquals( - "There should be round-robin load balancing.", expectedEndpoint, response.server); - checkSynchonousResponseContent(response, qValue); - } - } + /** + * Test method for {@link LBHttp2SolrClient.Builder} that validates that the query param keys + * passed in by the base <code>Http2SolrClient + * </code> instance are used by the LBHttp2SolrClient. + */ + @Test + public void testLBHttp2SolrClientWithTheseParamNamesInTheUrl() { + String url = "http://127.0.0.1:8080"; + Set<String> urlParamNames = new HashSet<>(2); + urlParamNames.add("param1"); + + try (Http2SolrClient http2SolrClient = + new Http2SolrClient.Builder(url).withTheseParamNamesInTheUrl(urlParamNames).build(); + LBHttp2SolrClient<Http2SolrClient> testClient = + new LBHttp2SolrClient.Builder<>(http2SolrClient, new LBSolrClient.Endpoint(url)) + .build()) { + + assertArrayEquals( + "Wrong urlParamNames found in lb client.", + urlParamNames.toArray(), + testClient.getUrlParamNames().toArray()); + assertArrayEquals( + "Wrong urlParamNames found in base client.", + urlParamNames.toArray(), + http2SolrClient.getUrlParamNames().toArray()); } - - @Test - public void testSynchronousWihFalures() throws Exception { - LBSolrClient.Endpoint ep1 = new LBSolrClient.Endpoint("http://endpoint.one"); - LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two"); - List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2); - - Http2SolrClient.Builder b = - new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS); - ; - try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b); - LBHttp2SolrClient<MockHttpSolrClient> testClient = new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) { - - client.basePathToFail = ep1.getBaseUrl(); - String basePathToSucceed = ep2.getBaseUrl(); - String qValue = "First time"; - - for (int i = 0; i < 5; i++) { - LBSolrClient.Req req = - new LBSolrClient.Req( - new QueryRequest(new MapSolrParams(Map.of("q", qValue))), endpointList); - LBSolrClient.Rsp response = testClient.request(req); - assertEquals( - "The healthy node 'endpoint two' should have served the request: " + i, - basePathToSucceed, - response.server); - checkSynchonousResponseContent(response, qValue); - } - - client.basePathToFail = ep2.getBaseUrl(); - basePathToSucceed = ep1.getBaseUrl(); - qValue = "Second time"; - - for (int i = 0; i < 5; i++) { - LBSolrClient.Req req = - new LBSolrClient.Req( - new QueryRequest(new MapSolrParams(Map.of("q", qValue))), endpointList); - LBSolrClient.Rsp response = testClient.request(req); - assertEquals( - "The healthy node 'endpoint one' should have served the request: " + i, - basePathToSucceed, - response.server); - checkSynchonousResponseContent(response, qValue); - } - } + } + + @Test + public void testSynchronous() throws Exception { + LBSolrClient.Endpoint ep1 = new LBSolrClient.Endpoint("http://endpoint.one"); + LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two"); + List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2); + + Http2SolrClient.Builder b = + new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS); + ; + try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b); + LBHttp2SolrClient<MockHttpSolrClient> testClient = + new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) { + + String lastEndpoint = null; + for (int i = 0; i < 10; i++) { + String qValue = "Query Number: " + i; + QueryRequest queryRequest = new QueryRequest(new MapSolrParams(Map.of("q", qValue))); + LBSolrClient.Req req = new LBSolrClient.Req(queryRequest, endpointList); + LBSolrClient.Rsp response = testClient.request(req); + + String expectedEndpoint = + ep1.toString().equals(lastEndpoint) ? ep2.toString() : ep1.toString(); + assertEquals( + "There should be round-robin load balancing.", expectedEndpoint, response.server); + checkSynchonousResponseContent(response, qValue); + } } - - private void checkSynchonousResponseContent(LBSolrClient.Rsp response, String qValue) { - assertEquals("There should be one element in the respnse.", 1, response.getResponse().size()); + } + + @Test + public void testSynchronousWihFalures() throws Exception { + LBSolrClient.Endpoint ep1 = new LBSolrClient.Endpoint("http://endpoint.one"); + LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two"); + List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2); + + Http2SolrClient.Builder b = + new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS); + ; + try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b); + LBHttp2SolrClient<MockHttpSolrClient> testClient = + new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) { + + client.basePathToFail = ep1.getBaseUrl(); + String basePathToSucceed = ep2.getBaseUrl(); + String qValue = "First time"; + + for (int i = 0; i < 5; i++) { + LBSolrClient.Req req = + new LBSolrClient.Req( + new QueryRequest(new MapSolrParams(Map.of("q", qValue))), endpointList); + LBSolrClient.Rsp response = testClient.request(req); + assertEquals( + "The healthy node 'endpoint two' should have served the request: " + i, + basePathToSucceed, + response.server); + checkSynchonousResponseContent(response, qValue); + } + + client.basePathToFail = ep2.getBaseUrl(); + basePathToSucceed = ep1.getBaseUrl(); + qValue = "Second time"; + + for (int i = 0; i < 5; i++) { + LBSolrClient.Req req = + new LBSolrClient.Req( + new QueryRequest(new MapSolrParams(Map.of("q", qValue))), endpointList); + LBSolrClient.Rsp response = testClient.request(req); assertEquals( - "The response key 'response' should echo the query.", - qValue, - response.getResponse().get("response")); + "The healthy node 'endpoint one' should have served the request: " + i, + basePathToSucceed, + response.server); + checkSynchonousResponseContent(response, qValue); + } } + } + + private void checkSynchonousResponseContent(LBSolrClient.Rsp response, String qValue) { + assertEquals("There should be one element in the respnse.", 1, response.getResponse().size()); + assertEquals( + "The response key 'response' should echo the query.", + qValue, + response.getResponse().get("response")); + } + + @Test + public void testAsyncWithFailures() { + + // This demonstrates that the failing endpoint always gets retried, and it is up to the user + // to remove any failing nodes if desired. + + LBSolrClient.Endpoint ep1 = new LBSolrClient.Endpoint("http://endpoint.one"); + LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two"); + List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2); + + Http2SolrClient.Builder b = + new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS); + ; + try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b); + LBHttp2SolrClient<MockHttpSolrClient> testClient = + new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) { + + for (int j = 0; j < 2; j++) { + // first time Endpoint One will return error code 500. + // second time Endpoint One will be healthy + + String basePathToSucceed; + if (j == 0) { + client.basePathToFail = ep1.getBaseUrl(); + basePathToSucceed = ep2.getBaseUrl(); + } else { + client.basePathToFail = ep2.getBaseUrl(); + basePathToSucceed = ep1.getBaseUrl(); + } - @Test - public void testAsyncWithFailures() { - - // This demonstrates that the failing endpoint always gets retried, and it is up to the user - // to remove any failing nodes if desired. - - LBSolrClient.Endpoint ep1 = new LBSolrClient.Endpoint("http://endpoint.one"); - LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two"); - List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2); - - Http2SolrClient.Builder b = - new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS); - ; - try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b); - LBHttp2SolrClient<MockHttpSolrClient> testClient = new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) { - - for (int j = 0; j < 2; j++) { - // first time Endpoint One will return error code 500. - // second time Endpoint One will be healthy - - String basePathToSucceed; - if (j == 0) { - client.basePathToFail = ep1.getBaseUrl(); - basePathToSucceed = ep2.getBaseUrl(); - } else { - client.basePathToFail = ep2.getBaseUrl(); - basePathToSucceed = ep1.getBaseUrl(); - } - - for (int i = 0; i < 10; i++) { - // i: we'll try 10 times to see if it behaves the same every time. - - QueryRequest queryRequest = new QueryRequest(new MapSolrParams(Map.of("q", "" + i))); - LBSolrClient.Req req = new LBSolrClient.Req(queryRequest, endpointList); - String iterMessage = "iter j/i " + j + "/" + i; - try { - testClient.requestAsync(req).get(1, TimeUnit.MINUTES); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - fail("interrupted"); - } catch (TimeoutException | ExecutionException e) { - fail(iterMessage + " Response ended in failure: " + e); - } - if (i == 0) { - // When j=0, "endpoint one" fails. - // The first time around (i) it tries the first, then the second. - // - // With j=0 and i>0, it only tries "endpoint two". - // - // When j=1 and i=0, "endpoint two" starts failing. - // So it tries both it and "endpoint one" - // - // With j=1 and i>0, it only tries "endpoint one". - assertEquals(iterMessage, 2, client.lastBasePaths.size()); - - String failedBasePath = client.lastBasePaths.remove(0); - assertEquals(iterMessage, client.basePathToFail, failedBasePath); - } else { - // The first endpoint does not give the exception, it doesn't retry. - assertEquals(iterMessage, 1, client.lastBasePaths.size()); - } - String successBasePath = client.lastBasePaths.remove(0); - assertEquals(iterMessage, basePathToSucceed, successBasePath); - } - } + for (int i = 0; i < 10; i++) { + // i: we'll try 10 times to see if it behaves the same every time. + + QueryRequest queryRequest = new QueryRequest(new MapSolrParams(Map.of("q", "" + i))); + LBSolrClient.Req req = new LBSolrClient.Req(queryRequest, endpointList); + String iterMessage = "iter j/i " + j + "/" + i; + try { + testClient.requestAsync(req).get(1, TimeUnit.MINUTES); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + fail("interrupted"); + } catch (TimeoutException | ExecutionException e) { + fail(iterMessage + " Response ended in failure: " + e); + } + if (i == 0) { + // When j=0, "endpoint one" fails. + // The first time around (i) it tries the first, then the second. + // + // With j=0 and i>0, it only tries "endpoint two". + // + // When j=1 and i=0, "endpoint two" starts failing. + // So it tries both it and "endpoint one" + // + // With j=1 and i>0, it only tries "endpoint one". + assertEquals(iterMessage, 2, client.lastBasePaths.size()); + + String failedBasePath = client.lastBasePaths.remove(0); + assertEquals(iterMessage, client.basePathToFail, failedBasePath); + } else { + // The first endpoint does not give the exception, it doesn't retry. + assertEquals(iterMessage, 1, client.lastBasePaths.size()); + } + String successBasePath = client.lastBasePaths.remove(0); + assertEquals(iterMessage, basePathToSucceed, successBasePath); } + } } + } + + @Test + public void testAsync() { + LBSolrClient.Endpoint ep1 = new LBSolrClient.Endpoint("http://endpoint.one"); + LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two"); + List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2); + + Http2SolrClient.Builder b = + new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS); + try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b); + LBHttp2SolrClient<MockHttpSolrClient> testClient = + new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) { + + int limit = 10; // For simplicity use an even limit + List<CompletableFuture<LBSolrClient.Rsp>> responses = new ArrayList<>(); + + for (int i = 0; i < limit; i++) { + QueryRequest queryRequest = new QueryRequest(new MapSolrParams(Map.of("q", "" + i))); + LBSolrClient.Req req = new LBSolrClient.Req(queryRequest, endpointList); + responses.add(testClient.requestAsync(req)); + } + + QueryRequest[] queryRequests = new QueryRequest[limit]; + int numEndpointOne = 0; + int numEndpointTwo = 0; + for (int i = 0; i < limit; i++) { + SolrRequest<?> lastSolrReq = client.lastSolrRequests.get(i); + assertTrue(lastSolrReq instanceof QueryRequest); + QueryRequest lastQueryReq = (QueryRequest) lastSolrReq; + int index = Integer.parseInt(lastQueryReq.getParams().get("q")); + assertNull("Found same request twice: " + index, queryRequests[index]); + queryRequests[index] = lastQueryReq; + if (lastQueryReq.getBasePath().equals(ep1.toString())) { + numEndpointOne++; + } else if (lastQueryReq.getBasePath().equals(ep2.toString())) { + numEndpointTwo++; + } - @Test - public void testAsync() { - LBSolrClient.Endpoint ep1 = new LBSolrClient.Endpoint("http://endpoint.one"); - LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two"); - List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2); - - Http2SolrClient.Builder b = - new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS); - try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b); - LBHttp2SolrClient<MockHttpSolrClient> testClient = new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) { - - int limit = 10; // For simplicity use an even limit - List<CompletableFuture<LBSolrClient.Rsp>> responses = new ArrayList<>(); - - for (int i = 0; i < limit; i++) { - QueryRequest queryRequest = new QueryRequest(new MapSolrParams(Map.of("q", "" + i))); - LBSolrClient.Req req = new LBSolrClient.Req(queryRequest, endpointList); - responses.add(testClient.requestAsync(req)); - } - - QueryRequest[] queryRequests = new QueryRequest[limit]; - int numEndpointOne = 0; - int numEndpointTwo = 0; - for (int i = 0; i < limit; i++) { - SolrRequest<?> lastSolrReq = client.lastSolrRequests.get(i); - assertTrue(lastSolrReq instanceof QueryRequest); - QueryRequest lastQueryReq = (QueryRequest) lastSolrReq; - int index = Integer.parseInt(lastQueryReq.getParams().get("q")); - assertNull("Found same request twice: " + index, queryRequests[index]); - queryRequests[index] = lastQueryReq; - if (lastQueryReq.getBasePath().equals(ep1.toString())) { - numEndpointOne++; - } else if (lastQueryReq.getBasePath().equals(ep2.toString())) { - numEndpointTwo++; - } - - LBSolrClient.Rsp lastRsp = null; - try { - lastRsp = responses.get(index).get(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - fail("interrupted"); - } catch (ExecutionException ee) { - fail("Response " + index + " ended in failure: " + ee); - } - NamedList<Object> lastResponse = lastRsp.getResponse(); - - // The Mock will return {"response": index}. - assertEquals("" + index, lastResponse.get("response")); - } - - // It is the user's responsibility to shuffle the endpoints when using - // async. LB Http Solr Client will always try the passed-in endpoints - // in order. In this case, endpoint 1 gets all the requests! - assertEquals(limit, numEndpointOne); - assertEquals(0, numEndpointTwo); - - assertEquals(limit, client.lastSolrRequests.size()); - assertEquals(limit, client.lastCollections.size()); + LBSolrClient.Rsp lastRsp = null; + try { + lastRsp = responses.get(index).get(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + fail("interrupted"); + } catch (ExecutionException ee) { + fail("Response " + index + " ended in failure: " + ee); } + NamedList<Object> lastResponse = lastRsp.getResponse(); + + // The Mock will return {"response": index}. + assertEquals("" + index, lastResponse.get("response")); + } + + // It is the user's responsibility to shuffle the endpoints when using + // async. LB Http Solr Client will always try the passed-in endpoints + // in order. In this case, endpoint 1 gets all the requests! + assertEquals(limit, numEndpointOne); + assertEquals(0, numEndpointTwo); + + assertEquals(limit, client.lastSolrRequests.size()); + assertEquals(limit, client.lastCollections.size()); } + } - public static class MockHttpSolrClient extends Http2SolrClient { + public static class MockHttpSolrClient extends Http2SolrClient { - public List<SolrRequest<?>> lastSolrRequests = new ArrayList<>(); + public List<SolrRequest<?>> lastSolrRequests = new ArrayList<>(); - public List<String> lastBasePaths = new ArrayList<>(); + public List<String> lastBasePaths = new ArrayList<>(); - public List<String> lastCollections = new ArrayList<>(); + public List<String> lastCollections = new ArrayList<>(); - public String basePathToFail = null; + public String basePathToFail = null; - protected MockHttpSolrClient(String serverBaseUrl, Builder builder) { - // TODO: Consider creating an interface for Http*SolrClient - // so mocks can Implement, not Extend, and not actually need to - // build an (unused) client - super(serverBaseUrl, builder); - } + protected MockHttpSolrClient(String serverBaseUrl, Builder builder) { + // TODO: Consider creating an interface for Http*SolrClient + // so mocks can Implement, not Extend, and not actually need to + // build an (unused) client + super(serverBaseUrl, builder); + } - @Override - public NamedList<Object> request(final SolrRequest<?> request, String collection) - throws SolrServerException, IOException { - lastSolrRequests.add(request); - lastBasePaths.add(request.getBasePath()); - lastCollections.add(collection); - if (request.getBasePath().equals(basePathToFail)) { - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "We should retry this."); - } - return generateResponse(request); - } + @Override + public NamedList<Object> request(final SolrRequest<?> request, String collection) + throws SolrServerException, IOException { + lastSolrRequests.add(request); + lastBasePaths.add(request.getBasePath()); + lastCollections.add(collection); + if (request.getBasePath().equals(basePathToFail)) { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "We should retry this."); + } + return generateResponse(request); + } - @Override - public CompletableFuture<NamedList<Object>> requestAsync( - final SolrRequest<?> solrRequest, String collection) { - CompletableFuture<NamedList<Object>> cf = new CompletableFuture<>(); - lastSolrRequests.add(solrRequest); - lastBasePaths.add(solrRequest.getBasePath()); - lastCollections.add(collection); - if (solrRequest.getBasePath().equals(basePathToFail)) { - cf.completeExceptionally( - new SolrException(SolrException.ErrorCode.SERVER_ERROR, "We should retry this.")); - } else { - cf.complete(generateResponse(solrRequest)); - } - return cf; - } + @Override + public CompletableFuture<NamedList<Object>> requestAsync( + final SolrRequest<?> solrRequest, String collection) { + CompletableFuture<NamedList<Object>> cf = new CompletableFuture<>(); + lastSolrRequests.add(solrRequest); + lastBasePaths.add(solrRequest.getBasePath()); + lastCollections.add(collection); + if (solrRequest.getBasePath().equals(basePathToFail)) { + cf.completeExceptionally( + new SolrException(SolrException.ErrorCode.SERVER_ERROR, "We should retry this.")); + } else { + cf.complete(generateResponse(solrRequest)); + } + return cf; + } - private NamedList<Object> generateResponse(SolrRequest<?> solrRequest) { - String id = solrRequest.getParams().get("q"); - return new NamedList<>(Collections.singletonMap("response", id)); - } + private NamedList<Object> generateResponse(SolrRequest<?> solrRequest) { + String id = solrRequest.getParams().get("q"); + return new NamedList<>(Collections.singletonMap("response", id)); } + } }
