diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 3e4a9221465..8ba8fc5bdfa 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -164,8 +164,8 @@ New Features * SOLR-10446: CloudSolrClient can now be initialized using the base URL of a Solr instance instead of ZooKeeper hosts. This is possible through the use of newly introduced HttpClusterStateProvider. To fetch a list of collection aliases, this depends on LISTALIASES command, and hence this way of - initializing CloudSolrClient would not work with older versions of Solr that doesn't support LISTALIASES. - (Ishan Chattopadhyaya, Noble Paul) + initializing CloudSolrClient would not work if you have collection aliases on older versions of Solr + server that doesn't support LISTALIASES. (Ishan Chattopadhyaya, Noble Paul) Optimizations ---------------------- 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 183896f2c5b..ac388d2c9cd 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 @@ -16,7 +16,6 @@ */ package org.apache.solr.client.solrj.impl; -import java.io.Closeable; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.net.ConnectException; @@ -1783,34 +1782,4 @@ public class CloudSolrClient extends SolrClient { shardLeadersOnly, directUpdatesToLeadersOnly, stateProvider); } } - - interface ClusterStateProvider extends Closeable { - - /** - * Obtain the state of the collection (cluster status). - * @return the collection state, or null is collection doesn't exist - */ - ClusterState.CollectionRef getState(String collection); - - /** - * Obtain set of live_nodes for the cluster. - */ - Set liveNodes(); - - String getAlias(String collection); - - String getCollectionName(String name); - - /** - * Obtain a cluster property, or null if it doesn't exist. - */ - Object getClusterProperty(String propertyName); - - /** - * Obtain a cluster property, or the default value if it doesn't exist. - */ - Object getClusterProperty(String propertyName, String def); - - void connect(); - } } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClusterStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClusterStateProvider.java new file mode 100644 index 00000000000..b913cd4642f --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClusterStateProvider.java @@ -0,0 +1,59 @@ +/* + * 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.Closeable; +import java.util.Set; + +import org.apache.solr.common.cloud.ClusterState; + +public interface ClusterStateProvider extends Closeable { + + /** + * Obtain the state of the collection (cluster status). + * @return the collection state, or null is collection doesn't exist + */ + ClusterState.CollectionRef getState(String collection); + + /** + * Obtain set of live_nodes for the cluster. + */ + Set liveNodes(); + + /** + * Given an alias, returns the collection name that this alias points to + */ + String getAlias(String alias); + + /** + * Given a name, returns the collection name if an alias by that name exists, or + * returns the name itself, if no alias exists. + */ + String getCollectionName(String name); + + /** + * Obtain a cluster property, or null if it doesn't exist. + */ + Object getClusterProperty(String propertyName); + + /** + * Obtain a cluster property, or the default value if it doesn't exist. + */ + Object getClusterProperty(String propertyName, String def); + + void connect(); +} \ No newline at end of file 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 b5cf41444b6..1fb94150894 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 @@ -40,7 +40,7 @@ import org.apache.solr.common.util.SimpleOrderedMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class HttpClusterStateProvider implements CloudSolrClient.ClusterStateProvider { +public class HttpClusterStateProvider implements ClusterStateProvider { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private String urlScheme; @@ -72,8 +72,8 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro 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 a working" - + " solrUrl or zkUrl."); + + " you could try re-creating a new CloudSolrClient using working" + + " solrUrl(s) or zkHost(s)."); } } @@ -106,8 +106,8 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro throw new RuntimeException("Tried fetching cluster state using 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 a working" - + " solrUrl or zkUrl."); + + " you could try re-creating a new CloudSolrClient using working" + + " solrUrl(s) or zkHost(s)."); } @SuppressWarnings({"rawtypes", "unchecked"}) @@ -118,7 +118,8 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro QueryRequest request = new QueryRequest(params); request.setPath("/admin/collections"); NamedList cluster = (SimpleOrderedMap) client.request(request).get("cluster"); - Map collectionsMap = ((NamedList) cluster.get("collections")).asShallowMap(); + Map collectionsMap = Collections.singletonMap(collection, + ((NamedList) cluster.get("collections")).get(collection)); int znodeVersion = (int)((Map)(collectionsMap).get(collection)).get("znodeVersion"); Set liveNodes = new HashSet((List)(cluster.get("live_nodes"))); this.liveNodes = liveNodes; @@ -133,8 +134,8 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro throw new RuntimeException("We don't know of any live_nodes to fetch the" + " latest live_nodes information from. " + "If you think your Solr cluster is up and is accessible," - + " you could try re-creating a new CloudSolrClient using a working" - + " solrUrl or zkUrl."); + + " you could try re-creating a new CloudSolrClient using working" + + " solrUrl(s) or zkHost(s)."); } if (TimeUnit.SECONDS.convert((System.nanoTime() - liveNodesTimestamp), TimeUnit.NANOSECONDS) > getCacheTimeout()) { for (String nodeName: liveNodes) { @@ -153,8 +154,8 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro 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 a working" - + " solrUrl or zkUrl."); + + " you could try re-creating a new CloudSolrClient using working" + + " solrUrl(s) or zkHost(s)."); } else { return liveNodes; // cached copy is fresh enough } @@ -171,9 +172,9 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro } @Override - public String getAlias(String collection) { + public String getAlias(String alias) { Map aliases = getAliases(false); - return aliases.get(collection); + return aliases.get(alias); } private Map getAliases(boolean forceFetch) { @@ -181,8 +182,8 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro 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 a working" - + " solrUrl or zkUrl."); + + " you could try re-creating a new CloudSolrClient using working" + + " solrUrl(s) or zkHost(s)."); } if (forceFetch || this.aliases == null || @@ -197,6 +198,14 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro this.aliasesTimestamp = System.nanoTime(); return Collections.unmodifiableMap(aliases); } catch (SolrServerException | RemoteSolrException | IOException e) { + // Situation where we're hitting an older Solr which doesn't have LISTALIASES + if (e instanceof RemoteSolrException && ((RemoteSolrException)e).code()==400) { + log.warn("LISTALIASES not found, possibly using older Solr server. Aliases won't work" + + " unless you re-create the CloudSolrClient using zkHost(s) or upgrade Solr server", e); + this.aliases = Collections.emptyMap(); + this.aliasesTimestamp = System.nanoTime(); + return aliases; + } log.warn("Attempt to fetch cluster state from " + ZkStateReader.getBaseUrlForNodeName(nodeName, urlScheme) + " failed.", e); } @@ -206,7 +215,7 @@ public class HttpClusterStateProvider implements CloudSolrClient.ClusterStatePro + "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 a working" - + " solrUrl or zkUrl."); + + " solrUrl or zkHost."); } else { return Collections.unmodifiableMap(this.aliases); // cached copy is fresh enough } diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java index 6b37eb71134..c9972894d3b 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java @@ -34,7 +34,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ZkClientClusterStateProvider implements CloudSolrClient.ClusterStateProvider { +public class ZkClientClusterStateProvider implements ClusterStateProvider { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @@ -63,9 +63,9 @@ public class ZkClientClusterStateProvider implements CloudSolrClient.ClusterStat @Override - public String getAlias(String collection) { + public String getAlias(String alias) { Aliases aliases = zkStateReader.getAliases(); - return aliases.getCollectionAlias(collection); + return aliases.getCollectionAlias(alias); } @Override diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientCacheTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientCacheTest.java index d94d7e414a2..0a83138a8e6 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientCacheTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientCacheTest.java @@ -117,9 +117,9 @@ public class CloudSolrClientCacheTest extends SolrTestCaseJ4 { return mockLbclient; } - private CloudSolrClient.ClusterStateProvider getStateProvider(Set livenodes, + private ClusterStateProvider getStateProvider(Set livenodes, Map colls) { - return new CloudSolrClient.ClusterStateProvider() { + return new ClusterStateProvider() { @Override public ClusterState.CollectionRef getState(String collection) { return colls.get(collection);