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

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


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 34780b2d42a SOLR-17538: CloudHttp2SolrClient needs a custom 
ClusterStateProvider option (#2832)
34780b2d42a is described below

commit 34780b2d42aa870a620a544c632a2dd241f81411
Author: Sanjay Dutt <[email protected]>
AuthorDate: Tue Nov 5 20:14:24 2024 +0530

    SOLR-17538: CloudHttp2SolrClient needs a custom ClusterStateProvider option 
(#2832)
    
    * SOLR-17538: CloudHttp2SolrClient needs a custom ClusterStateProvider 
option
    
    ---------
    
    Co-authored-by: David Smiley <[email protected]>
    (cherry picked from commit 783d7805ea7ef6defcb87cdec55a662935a938fb)
---
 .../java/org/apache/solr/cloud/ZkController.java   |  3 +-
 .../test/org/apache/solr/cloud/OverseerTest.java   |  8 ++---
 .../client/solrj/impl/CloudHttp2SolrClient.java    | 35 +++++++++++++++++-----
 .../solr/client/solrj/impl/CloudSolrClient.java    |  5 ++++
 .../impl/CloudHttp2SolrClientBuilderTest.java      | 24 ++++++++++-----
 5 files changed, 54 insertions(+), 21 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java 
b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index da54f8d170e..f7b61765fcd 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -63,6 +63,7 @@ import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.HttpSolrClient.Builder;
 import org.apache.solr.client.solrj.impl.SolrClientCloudManager;
 import org.apache.solr.client.solrj.impl.SolrZkClientTimeout;
+import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.WaitForState;
 import org.apache.solr.cloud.overseer.ClusterStateMutator;
 import org.apache.solr.cloud.overseer.OverseerAction;
@@ -881,7 +882,7 @@ public class ZkController implements Closeable {
               .withConnectionTimeout(15000, TimeUnit.MILLISECONDS)
               .build();
       cloudSolrClient =
-          new CloudHttp2SolrClient.Builder(List.of(getZkServerAddress()), 
Optional.empty())
+          new CloudHttp2SolrClient.Builder(new 
ZkClientClusterStateProvider(zkStateReader))
               .withHttpClient(http2SolrClient)
               .build();
       cloudManager = new SolrClientCloudManager(cloudSolrClient, 
cc.getObjectCache());
diff --git a/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java 
b/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
index b11801ed3ad..3e67ed8f14f 100644
--- a/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
@@ -41,7 +41,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -55,6 +54,7 @@ import org.apache.solr.client.solrj.cloud.SolrCloudManager;
 import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.SolrClientCloudManager;
+import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
 import org.apache.solr.cloud.overseer.NodeMutator;
 import org.apache.solr.cloud.overseer.OverseerAction;
 import org.apache.solr.cloud.overseer.ZkWriteCommand;
@@ -1944,18 +1944,18 @@ public class OverseerTest extends SolrTestCaseJ4 {
     when(zkController.getLeaderProps(anyString(), anyString(), 
anyInt())).thenCallRealMethod();
     when(zkController.getLeaderProps(anyString(), anyString(), anyInt(), 
anyBoolean()))
         .thenCallRealMethod();
-    
doReturn(getCloudDataProvider(zkAddress)).when(zkController).getSolrCloudManager();
+    
doReturn(getCloudDataProvider(reader)).when(zkController).getSolrCloudManager();
     return zkController;
   }
 
-  private SolrCloudManager getCloudDataProvider(String zkAddress) {
+  private SolrCloudManager getCloudDataProvider(ZkStateReader zkStateReader) {
     var httpSolrClient =
         new Http2SolrClient.Builder()
             .withIdleTimeout(30000, TimeUnit.MILLISECONDS)
             .withConnectionTimeout(15000, TimeUnit.MILLISECONDS)
             .build();
     var cloudSolrClient =
-        new CloudHttp2SolrClient.Builder(Collections.singletonList(zkAddress), 
Optional.empty())
+        new CloudHttp2SolrClient.Builder(new 
ZkClientClusterStateProvider(zkStateReader))
             .withHttpClient(httpSolrClient)
             .build();
     solrClients.add(cloudSolrClient);
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
index 266b90c223c..670bd2963ff 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
@@ -56,10 +56,7 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
     super(builder.shardLeadersOnly, builder.parallelUpdates, 
builder.directUpdatesToLeadersOnly);
     this.clientIsInternal = builder.httpClient == null;
     this.myClient = createOrGetHttpClientFromBuilder(builder);
-    this.stateProvider =
-        builder.zkHosts.isEmpty()
-            ? createHttp2ClusterStateProvider(builder.solrUrls, myClient)
-            : createZkClusterStateProvider(builder);
+    this.stateProvider = createClusterStateProvider(builder);
     this.retryExpiryTimeNano = builder.retryExpiryTimeNano;
     this.defaultCollection = builder.defaultCollection;
     if (builder.requestWriter != null) {
@@ -89,6 +86,16 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
     }
   }
 
+  private ClusterStateProvider createClusterStateProvider(Builder builder) {
+    if (builder.stateProvider != null) {
+      return builder.stateProvider;
+    } else if (builder.zkHosts.isEmpty()) {
+      return createHttp2ClusterStateProvider(builder.solrUrls, this.myClient);
+    } else {
+      return createZkClusterStateProvider(builder);
+    }
+  }
+
   private ClusterStateProvider createZkClusterStateProvider(Builder builder) {
     try {
       ClusterStateProvider stateProvider =
@@ -233,6 +240,11 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
       if (zkChroot.isPresent()) this.zkChroot = zkChroot.get();
     }
 
+    /** for an expert use-case */
+    public Builder(ClusterStateProvider stateProvider) {
+      this.stateProvider = stateProvider;
+    }
+
     /** Whether to use the default ZK ACLs when building a ZK Client. */
     public Builder canUseZkACLs(boolean canUseZkACLs) {
       this.canUseZkACLs = canUseZkACLs;
@@ -451,12 +463,19 @@ public class CloudHttp2SolrClient extends CloudSolrClient 
{
 
     /** Create a {@link CloudHttp2SolrClient} based on the provided 
configuration. */
     public CloudHttp2SolrClient build() {
-      if (!zkHosts.isEmpty() && !solrUrls.isEmpty()) {
+      int providedOptions = 0;
+      if (!zkHosts.isEmpty()) providedOptions++;
+      if (!solrUrls.isEmpty()) providedOptions++;
+      if (stateProvider != null) providedOptions++;
+
+      if (providedOptions > 1) {
         throw new IllegalArgumentException(
-            "Both zkHost(s) & solrUrl(s) have been specified. Only specify 
one.");
-      } else if (zkHosts.isEmpty() && solrUrls.isEmpty()) {
-        throw new IllegalArgumentException("Both zkHosts and solrUrl cannot be 
null.");
+            "Only one of zkHost(s), solrUrl(s), or stateProvider should be 
specified.");
+      } else if (providedOptions == 0) {
+        throw new IllegalArgumentException(
+            "One of zkHosts, solrUrls, or stateProvider must be specified.");
       }
+
       return new CloudHttp2SolrClient(this);
     }
   }
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 e6602f47545..3ab0d5e844a 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
@@ -178,6 +178,11 @@ public abstract class CloudSolrClient extends SolrClient {
     public Builder(List<String> zkHosts, Optional<String> zkChroot) {
       super(zkHosts, zkChroot);
     }
+
+    /** for an expert use-case */
+    public Builder(ClusterStateProvider stateProvider) {
+      super(stateProvider);
+    }
   }
 
   static class StateCache extends ConcurrentHashMap<String, 
ExpiringCachedDocCollection> {
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
index 19c32d9c138..01f57970e99 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.solr.client.solrj.impl;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -30,7 +31,6 @@ import java.util.Optional;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.junit.BeforeClass;
 import org.junit.Test;
-import org.mockito.Mockito;
 
 public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
   private static final String ANY_CHROOT = "/ANY_CHROOT";
@@ -128,23 +128,23 @@ public class CloudHttp2SolrClientBuilderTest extends 
SolrCloudTestCase {
         () ->
             new CloudHttp2SolrClient.Builder(
                     Collections.singletonList(ANY_ZK_HOST), 
Optional.of(ANY_CHROOT))
-                .withHttpClient(Mockito.mock(Http2SolrClient.class))
-                
.withInternalClientBuilder(Mockito.mock(Http2SolrClient.Builder.class))
+                .withHttpClient(mock(Http2SolrClient.class))
+                .withInternalClientBuilder(mock(Http2SolrClient.Builder.class))
                 .build());
     expectThrows(
         IllegalStateException.class,
         () ->
             new CloudHttp2SolrClient.Builder(
                     Collections.singletonList(ANY_ZK_HOST), 
Optional.of(ANY_CHROOT))
-                
.withInternalClientBuilder(Mockito.mock(Http2SolrClient.Builder.class))
-                .withHttpClient(Mockito.mock(Http2SolrClient.class))
+                .withInternalClientBuilder(mock(Http2SolrClient.Builder.class))
+                .withHttpClient(mock(Http2SolrClient.class))
                 .build());
   }
 
   @Test
   public void testProvideInternalBuilder() throws IOException {
-    Http2SolrClient http2Client = Mockito.mock(Http2SolrClient.class);
-    Http2SolrClient.Builder http2ClientBuilder = 
Mockito.mock(Http2SolrClient.Builder.class);
+    Http2SolrClient http2Client = mock(Http2SolrClient.class);
+    Http2SolrClient.Builder http2ClientBuilder = 
mock(Http2SolrClient.Builder.class);
     when(http2ClientBuilder.build()).thenReturn(http2Client);
     CloudHttp2SolrClient.Builder clientBuilder =
         new CloudHttp2SolrClient.Builder(
@@ -162,7 +162,7 @@ public class CloudHttp2SolrClientBuilderTest extends 
SolrCloudTestCase {
 
   @Test
   public void testProvideExternalClient() throws IOException {
-    Http2SolrClient http2Client = Mockito.mock(Http2SolrClient.class);
+    Http2SolrClient http2Client = mock(Http2SolrClient.class);
     CloudHttp2SolrClient.Builder clientBuilder =
         new CloudHttp2SolrClient.Builder(
                 Collections.singletonList(ANY_ZK_HOST), 
Optional.of(ANY_CHROOT))
@@ -228,4 +228,12 @@ public class CloudHttp2SolrClientBuilderTest extends 
SolrCloudTestCase {
           ((Http2ClusterStateProvider) 
client.getClusterStateProvider()).getHttpClient());
     }
   }
+
+  public void testCustomStateProvider() throws IOException {
+    ZkClientClusterStateProvider stateProvider = 
mock(ZkClientClusterStateProvider.class);
+    try (CloudSolrClient cloudSolrClient = new 
CloudSolrClient.Builder(stateProvider).build()) {
+      assertEquals(stateProvider, cloudSolrClient.getClusterStateProvider());
+    }
+    verify(stateProvider, times(1)).close();
+  }
 }

Reply via email to