You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by jd...@apache.org on 2024/10/31 14:48:59 UTC
(solr) 01/10: Add additional unit tests
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 c7e306c33e1abac62589d2a1d1859585e18806f3
Author: jdyer1 <jd...@apache.org>
AuthorDate: Mon Oct 28 17:57:40 2024 -0500
Add additional unit tests
---
.../client/solrj/impl/LBHttp2SolrClientTest.java | 448 +++++++++++++--------
1 file changed, 272 insertions(+), 176 deletions(-)
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 5f55b353c0d..63ccd04b814 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
@@ -16,6 +16,7 @@
*/
package org.apache.solr.client.solrj.impl;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -28,6 +29,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.solr.SolrTestCase;
import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.MapSolrParams;
@@ -37,200 +39,294 @@ 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 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 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 (MockHttp2SolrClient client = new MockHttp2SolrClient("http://base.url", b);
- LBHttp2SolrClient 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 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 testClient =
+ new LBHttp2SolrClient.Builder(http2SolrClient, new LBSolrClient.Endpoint(url))
+ .build()) {
- 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);
+ 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 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 (MockHttp2SolrClient client = new MockHttp2SolrClient("http://base.url", b);
- LBHttp2SolrClient 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);
+ @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 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);
+ }
}
- NamedList<Object> lastResponse = lastRsp.getResponse();
+ }
+
+ @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 testClient = new LBHttp2SolrClient.Builder(client, ep1, ep2).build()) {
- // The Mock will return {"response": index}.
- assertEquals("" + index, lastResponse.get("response"));
- }
+ client.basePathToFail = ep1.getBaseUrl();
+ String basePathToSucceed = ep2.getBaseUrl();
+ String qValue = "First time";
- // 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);
+ 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);
+ }
- assertEquals(limit, client.lastSolrRequests.size());
- assertEquals(limit, client.lastCollections.size());
+ 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);
+ }
+ }
}
- }
- public static class MockHttp2SolrClient extends Http2SolrClient {
+ 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.
- public List<SolrRequest<?>> lastSolrRequests = new ArrayList<>();
+ 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);
- public List<String> lastBasePaths = new ArrayList<>();
+ Http2SolrClient.Builder b =
+ new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
+ ;
+ try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
+ LBHttp2SolrClient testClient = new LBHttp2SolrClient.Builder(client, ep1, ep2).build()) {
- public List<String> lastCollections = new ArrayList<>();
+ for (int j = 0; j < 2; j++) {
+ // first time Endpoint One will return error code 500.
+ // second time Endpoint One will be healthy
- public String basePathToFail = null;
+ String basePathToSucceed;
+ if (j == 0) {
+ client.basePathToFail = ep1.getBaseUrl();
+ basePathToSucceed = ep2.getBaseUrl();
+ } else {
+ client.basePathToFail = ep2.getBaseUrl();
+ basePathToSucceed = ep1.getBaseUrl();
+ }
- protected MockHttp2SolrClient(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);
+ 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);
+ }
+ }
+ }
}
- @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;
+ @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 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());
+ }
}
- private NamedList<Object> generateResponse(SolrRequest<?> solrRequest) {
- String id = solrRequest.getParams().get("q");
- return new NamedList<>(Collections.singletonMap("response", id));
+ public static class MockHttpSolrClient extends Http2SolrClient {
+
+ public List<SolrRequest<?>> lastSolrRequests = new ArrayList<>();
+
+ public List<String> lastBasePaths = new ArrayList<>();
+
+ public List<String> lastCollections = new ArrayList<>();
+
+ 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);
+ }
+
+ @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;
+ }
+
+ private NamedList<Object> generateResponse(SolrRequest<?> solrRequest) {
+ String id = solrRequest.getParams().get("q");
+ return new NamedList<>(Collections.singletonMap("response", id));
+ }
}
- }
}