SOLR-10446: Making HttpClusterStateProvider work with server that doesn't have LISTALIASES

This commit is contained in:
Ishan Chattopadhyaya 2017-04-18 03:09:46 +05:30
parent 66715a4761
commit fd4125ea41
6 changed files with 90 additions and 53 deletions

View File

@ -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
----------------------

View File

@ -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<String> 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();
}
}

View File

@ -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<String> 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();
}

View File

@ -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<String, Object> collectionsMap = ((NamedList) cluster.get("collections")).asShallowMap();
Map<String, Object> collectionsMap = Collections.singletonMap(collection,
((NamedList) cluster.get("collections")).get(collection));
int znodeVersion = (int)((Map<String, Object>)(collectionsMap).get(collection)).get("znodeVersion");
Set<String> liveNodes = new HashSet((List<String>)(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<String, String> aliases = getAliases(false);
return aliases.get(collection);
return aliases.get(alias);
}
private Map<String, String> 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
}

View File

@ -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

View File

@ -117,9 +117,9 @@ public class CloudSolrClientCacheTest extends SolrTestCaseJ4 {
return mockLbclient;
}
private CloudSolrClient.ClusterStateProvider getStateProvider(Set<String> livenodes,
private ClusterStateProvider getStateProvider(Set<String> livenodes,
Map<String, ClusterState.CollectionRef> colls) {
return new CloudSolrClient.ClusterStateProvider() {
return new ClusterStateProvider() {
@Override
public ClusterState.CollectionRef getState(String collection) {
return colls.get(collection);