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

dsmiley 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 ff0f9050075 SOLR-17607: Http ClusterStateProvider, lazy connect (#3249)
ff0f9050075 is described below

commit ff0f9050075543d64d6ccc760466a233c00863e8
Author: David Smiley <[email protected]>
AuthorDate: Tue Mar 11 19:38:17 2025 -0400

    SOLR-17607: Http ClusterStateProvider, lazy connect (#3249)
    
    i.e. stop eagerly connecting when CloudSolrClient is created.
    Update urlScheme on successful getLiveNodes
    
    (cherry picked from commit 9918ee640dce7123ff51eb38d9eb1c6f4e06b487)
---
 solr/CHANGES.txt                                   |   3 +
 .../solrj/impl/BaseHttpClusterStateProvider.java   | 114 ++++++++-------------
 .../solrj/impl/Http2ClusterStateProvider.java      |   2 +-
 .../solrj/impl/HttpClusterStateProvider.java       |   2 +-
 .../solrj/impl/CloudHttp2SolrClientTest.java       |   1 +
 5 files changed, 49 insertions(+), 73 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index b14aff72a80..7301c7ffd86 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -33,6 +33,9 @@ Improvements
   `org.apache.solr.client.solrj.request.FileStoreApi`. The older form of this 
endpoint (`GET /api/node/files`) is deprecated and is slated to
   be removed. (Jason Gerlowski)
 
+* SOLR-17607: SolrJ CloudSolrClient configured with HTTP URLs will no longer 
eagerly connect to
+  anything. (David Smiley)
+
 Optimizations
 ---------------------
 * SOLR-17578: Remove ZkController internal core supplier, for slightly faster 
reconnection after Zookeeper session loss. (Pierre Salagnac)
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java
index 8d956ca7a94..b1477a0f45a 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java
@@ -56,7 +56,7 @@ public abstract class BaseHttpClusterStateProvider implements 
ClusterStateProvid
 
   private String urlScheme;
   private List<URL> configuredNodes;
-  volatile Set<String> liveNodes;
+  volatile Set<String> liveNodes; // initially null then never null
   long liveNodesTimestamp = 0;
   volatile Map<String, List<String>> aliases;
   volatile Map<String, Map<String, String>> aliasProperties;
@@ -65,41 +65,24 @@ public abstract class BaseHttpClusterStateProvider 
implements ClusterStateProvid
   // the liveNodes and aliases cache will be invalidated after 5 secs
   private int cacheTimeout = 
EnvUtils.getPropertyAsInteger("solr.solrj.cache.timeout.sec", 5);
 
-  public void init(List<String> solrUrls) throws Exception {
+  protected void initConfiguredNodes(List<String> solrUrls) throws Exception {
     this.configuredNodes =
         solrUrls.stream()
-            .map(
-                (solrUrl) -> {
-                  try {
-                    return new URI(solrUrl).toURL();
-                  } catch (MalformedURLException | URISyntaxException e) {
-                    throw new IllegalArgumentException(
-                        "Failed to parse base Solr URL " + solrUrl, e);
-                  }
-                })
+            .map(BaseHttpClusterStateProvider::stringToUrl)
             .collect(Collectors.toList());
+  }
 
-    for (String solrUrl : solrUrls) {
-      urlScheme = solrUrl.startsWith("https") ? "https" : "http";
-      try (SolrClient initialClient = getSolrClient(solrUrl)) {
-        this.liveNodes = fetchLiveNodes(initialClient);
-        liveNodesTimestamp = System.nanoTime();
-        break;
-      } catch (SolrServerException | IOException e) {
-        log.warn("Attempt to fetch cluster state from {} failed.", solrUrl, e);
-      }
+  private static URL stringToUrl(String solrUrl) {
+    try {
+      return new URI(solrUrl).toURL();
+    } catch (MalformedURLException | URISyntaxException e) {
+      throw new IllegalArgumentException("Failed to parse base Solr URL " + 
solrUrl, e);
     }
+  }
 
-    if (this.liveNodes == null || this.liveNodes.isEmpty()) {
-      throw new RuntimeException(
-          "Tried fetching live_nodes using Solr URLs provided, i.e. "
-              + solrUrls
-              + ". However, "
-              + "succeeded in obtaining the cluster state from none of them."
-              + "If you think your Solr cluster is up and is accessible,"
-              + " you could try re-creating a new CloudSolrClient using 
working"
-              + " solrUrl(s).");
-    }
+  @Override
+  public void connect() {
+    getLiveNodes();
   }
 
   /** Create a SolrClient implementation that uses the specified Solr node URL 
*/
@@ -114,7 +97,7 @@ public abstract class BaseHttpClusterStateProvider 
implements ClusterStateProvid
 
   @Override
   public ClusterState.CollectionRef getState(String collection) {
-    for (String nodeName : liveNodes) {
+    for (String nodeName : getLiveNodes()) {
       String baseUrl = Utils.getBaseUrlForNodeName(nodeName, urlScheme);
       try (SolrClient client = getSolrClient(baseUrl)) {
         DocCollection docCollection = fetchCollectionState(client, collection);
@@ -238,43 +221,44 @@ public abstract class BaseHttpClusterStateProvider 
implements ClusterStateProvid
   }
 
   @Override
-  public Set<String> getLiveNodes() {
-    if (TimeUnit.SECONDS.convert((System.nanoTime() - liveNodesTimestamp), 
TimeUnit.NANOSECONDS)
-        > getCacheTimeout()) {
+  public synchronized Set<String> getLiveNodes() {
+    // synchronized because there's no value in multiple doing this at the 
same time
+
+    // only in the initial state, liveNodes is null
+    if (liveNodes != null) {
+      if (TimeUnit.SECONDS.convert((System.nanoTime() - liveNodesTimestamp), 
TimeUnit.NANOSECONDS)
+          <= getCacheTimeout()) {
+        return this.liveNodes; // cached copy is fresh enough
+      }
 
       if (liveNodes.stream()
-          .anyMatch((node) -> 
updateLiveNodes(URLUtil.getBaseUrlForNodeName(node, urlScheme))))
-        return this.liveNodes;
+          .map(node -> URLUtil.getBaseUrlForNodeName(node, urlScheme))
+          .map(BaseHttpClusterStateProvider::stringToUrl)
+          .anyMatch(this::updateLiveNodes)) return this.liveNodes;
 
-      log.warn(
-          "Attempt to fetch cluster state from all known live nodes {} failed. 
Trying backup nodes",
-          liveNodes);
+      log.warn("Failed fetching live_nodes from {}. Trying configured 
nodes...", liveNodes);
+    }
 
-      if (configuredNodes.stream().anyMatch((node) -> 
updateLiveNodes(node.toString())))
-        return this.liveNodes;
+    if (configuredNodes.stream().anyMatch(this::updateLiveNodes)) return 
this.liveNodes;
 
-      throw new RuntimeException(
-          "Tried fetching live_nodes using all the node names we knew of, i.e. 
"
-              + liveNodes
-              + ". However, "
-              + "succeeded in obtaining the cluster state from none of them."
-              + "If you think your Solr cluster is up and is accessible,"
-              + " you could try re-creating a new CloudSolrClient using 
working"
-              + " solrUrl(s).");
-    } else {
-      return this.liveNodes; // cached copy is fresh enough
-    }
+    throw new RuntimeException(
+        "Failed fetching live_nodes from "
+            + configuredNodes
+            + ". If you think your Solr cluster is up and is accessible,"
+            + " you could try re-creating a new CloudSolrClient using working"
+            + " solr URLs.");
   }
 
-  private boolean updateLiveNodes(String liveNode) {
-    try (SolrClient client = getSolrClient(liveNode)) {
+  private boolean updateLiveNodes(URL url) {
+    try (SolrClient client = getSolrClient(url.toString())) {
       this.liveNodes = fetchLiveNodes(client);
       liveNodesTimestamp = System.nanoTime();
+      urlScheme = url.getProtocol();
       return true;
     } catch (Exception e) {
-      log.warn("Attempt to fetch cluster state from {} failed.", liveNode, e);
+      log.warn("Attempt to fetch live_nodes from {} failed.", url, e);
+      return false;
     }
-    return false;
   }
 
   @SuppressWarnings({"unchecked"})
@@ -299,20 +283,11 @@ public abstract class BaseHttpClusterStateProvider 
implements ClusterStateProvid
   }
 
   private Map<String, List<String>> getAliases(boolean forceFetch) {
-    if (this.liveNodes == null) {
-      throw new RuntimeException(
-          "We don't know of any live_nodes to fetch the"
-              + " latest aliases information from. "
-              + "If you think your Solr cluster is up and is accessible,"
-              + " you could try re-creating a new CloudSolrClient using 
working"
-              + " solrUrl(s).");
-    }
-
     if (forceFetch
         || this.aliases == null
         || TimeUnit.SECONDS.convert((System.nanoTime() - aliasesTimestamp), 
TimeUnit.NANOSECONDS)
             > getCacheTimeout()) {
-      for (String nodeName : liveNodes) {
+      for (String nodeName : getLiveNodes()) {
         String baseUrl = Utils.getBaseUrlForNodeName(nodeName, urlScheme);
         try (SolrClient client = getSolrClient(baseUrl)) {
 
@@ -359,7 +334,7 @@ public abstract class BaseHttpClusterStateProvider 
implements ClusterStateProvid
 
   @Override
   public ClusterState getClusterState() {
-    for (String nodeName : liveNodes) {
+    for (String nodeName : getLiveNodes()) {
       String baseUrl = Utils.getBaseUrlForNodeName(nodeName, urlScheme);
       try (SolrClient client = getSolrClient(baseUrl)) {
         return fetchClusterState(client);
@@ -385,7 +360,7 @@ public abstract class BaseHttpClusterStateProvider 
implements ClusterStateProvid
   @SuppressWarnings("unchecked")
   @Override
   public Map<String, Object> getClusterProperties() {
-    for (String nodeName : liveNodes) {
+    for (String nodeName : getLiveNodes()) {
       String baseUrl = Utils.getBaseUrlForNodeName(nodeName, urlScheme);
       try (SolrClient client = getSolrClient(baseUrl)) {
         SimpleOrderedMap<?> cluster =
@@ -421,9 +396,6 @@ public abstract class BaseHttpClusterStateProvider 
implements ClusterStateProvid
     return getClusterProperties().get(propertyName);
   }
 
-  @Override
-  public void connect() {}
-
   public int getCacheTimeout() {
     return cacheTimeout;
   }
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
index 3bb222ae430..a7abb80b71d 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
@@ -29,7 +29,7 @@ public class Http2ClusterStateProvider extends 
BaseHttpClusterStateProvider {
       throws Exception {
     this.httpClient = httpClient == null ? new 
Http2SolrClient.Builder().build() : httpClient;
     this.closeClient = httpClient == null;
-    init(solrUrls);
+    initConfiguredNodes(solrUrls);
   }
 
   @Override
diff --git 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
index 56c6b08c7d9..353c15fd1b7 100644
--- 
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
+++ 
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
@@ -35,7 +35,7 @@ public class HttpClusterStateProvider extends 
BaseHttpClusterStateProvider {
   public HttpClusterStateProvider(List<String> solrUrls, HttpClient 
httpClient) throws Exception {
     this.httpClient = httpClient == null ? HttpClientUtil.createClient(null) : 
httpClient;
     this.clientIsInternal = httpClient == null;
-    init(solrUrls);
+    initConfiguredNodes(solrUrls);
   }
 
   @Override
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
index 9d5212ad0e7..86659a42420 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
@@ -261,6 +261,7 @@ public class CloudHttp2SolrClientTest extends 
SolrCloudTestCase {
 
     try (LogListener adminLogs = 
LogListener.info(HttpSolrCall.class).substring("[admin]");
         CloudSolrClient solrClient = createHttpCSPBasedCloudSolrClient(); ) {
+      solrClient.getClusterStateProvider().getLiveNodes(); // talks to Solr
 
       assertEquals(1, adminLogs.getCount());
       assertTrue(

Reply via email to