From 429588097cdd0ec86dbf960d49bb1c0ac5d78b72 Mon Sep 17 00:00:00 2001 From: Timothy Potter Date: Sat, 14 Feb 2015 02:37:34 +0000 Subject: [PATCH] SOLR-6832: Queries be served locally rather than being forwarded to another replica git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1659748 13f79535-47bb-0310-9956-ffa450edef68 --- solr/CHANGES.txt | 3 + .../handler/component/HttpShardHandler.java | 59 ++++++++- .../handler/component/ResponseBuilder.java | 1 + .../solr/handler/component/ShardRequest.java | 1 + .../conf/solrconfig.xml | 15 +++ .../solr/common/params/CommonParams.java | 4 + .../solrj/impl/CloudSolrClientTest.java | 123 ++++++++++++++++++ 7 files changed, 204 insertions(+), 2 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 7159f67f343..97e0e67a408 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -111,6 +111,9 @@ New Features * SOLR-7019: Support changing field key when using interval faceting. (Tomás Fernández Löbbe) +* SOLR-6832: Queries be served locally rather than being forwarded to another replica. + (Sachin Goyal, Timothy Potter) + Bug Fixes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java index 88d548a077e..46ccf09f609 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java @@ -40,13 +40,18 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.CoreDescriptor; +import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.net.ConnectException; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; @@ -63,6 +68,7 @@ public class HttpShardHandler extends ShardHandler { private Map> shardToURLs; private HttpClient httpClient; + protected static Logger log = LoggerFactory.getLogger(HttpShardHandler.class); public HttpShardHandler(HttpShardHandlerFactory httpShardHandlerFactory, HttpClient httpClient) { this.httpClient = httpClient; @@ -101,20 +107,69 @@ public class HttpShardHandler extends ShardHandler { // Not thread safe... don't use in Callable. // Don't modify the returned URL list. - private List getURLs(String shard) { + private List getURLs(ShardRequest sreq, String shard) { List urls = shardToURLs.get(shard); if (urls == null) { urls = httpShardHandlerFactory.makeURLList(shard); + preferCurrentHostForDistributedReq(sreq, urls); shardToURLs.put(shard, urls); } return urls; } + /** + * A distributed request is made via {@link LBHttpSolrClient} to the first live server in the URL list. + * This means it is just as likely to choose current host as any of the other hosts. + * This function makes sure that the cores of current host are always put first in the URL list. + * If all nodes prefer local-cores then a bad/heavily-loaded node will receive less requests from healthy nodes. + * This will help prevent a distributed deadlock or timeouts in all the healthy nodes due to one bad node. + */ + private void preferCurrentHostForDistributedReq(final ShardRequest sreq, final List urls) { + if (sreq == null || sreq.rb == null || sreq.rb.req == null || urls == null || urls.size() <= 1) + return; + + SolrQueryRequest req = sreq.rb.req; + + // determine if we should apply the local preference + if (!req.getOriginalParams().getBool(CommonParams.PREFER_LOCAL_SHARDS, false)) + return; + + // Get this node's base URL from ZK + SolrCore core = req.getCore(); + ZkController zkController = (core != null) ? core.getCoreDescriptor().getCoreContainer().getZkController() : null; + String currentHostAddress = (zkController != null) ? zkController.getBaseUrl() : null; + if (currentHostAddress == null) { + log.debug("Couldn't determine current host address to prefer local shards " + + "because either core is null? {} or there is no ZkController? {}", + Boolean.valueOf(core == null), Boolean.valueOf(zkController == null)); + return; + } + + if (log.isDebugEnabled()) + log.debug("Trying to prefer local shard on {} among the urls: {}", + currentHostAddress, Arrays.toString(urls.toArray())); + + ListIterator itr = urls.listIterator(); + while (itr.hasNext()) { + String url = itr.next(); + if (url.startsWith(currentHostAddress)) { + // move current URL to the fore-front + itr.remove(); + urls.add(0, url); + + if (log.isDebugEnabled()) + log.debug("Applied local shard preference for urls: {}", + Arrays.toString(urls.toArray())); + + break; + } + } + } @Override public void submit(final ShardRequest sreq, final String shard, final ModifiableSolrParams params) { // do this outside of the callable for thread safety reasons - final List urls = getURLs(shard); + final List urls = getURLs(sreq, shard); Callable task = new Callable() { @Override diff --git a/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java b/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java index 682d381060f..46f5718b945 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java +++ b/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java @@ -147,6 +147,7 @@ public class ResponseBuilder public void addRequest(SearchComponent me, ShardRequest sreq) { outgoing.add(sreq); + sreq.rb = this; if ((sreq.purpose & ShardRequest.PURPOSE_PRIVATE) == 0) { // if this isn't a private request, let other components modify it. for (SearchComponent component : components) { diff --git a/solr/core/src/java/org/apache/solr/handler/component/ShardRequest.java b/solr/core/src/java/org/apache/solr/handler/component/ShardRequest.java index f7c05d236bf..0248190c04d 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/ShardRequest.java +++ b/solr/core/src/java/org/apache/solr/handler/component/ShardRequest.java @@ -49,6 +49,7 @@ public class ShardRequest { public ModifiableSolrParams params; + public ResponseBuilder rb; /** list of responses... filled out by framework */ public List responses = new ArrayList<>(); diff --git a/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml b/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml index b8c90ca7175..d2b46cf142a 100755 --- a/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml +++ b/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml @@ -805,6 +805,21 @@ explicit 10 + + false