You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by th...@apache.org on 2021/06/23 18:21:59 UTC

[solr] branch main updated: SOLR-15472: New shards.preference option for preferring replicas based on their leader status (#188)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new a6d92b1  SOLR-15472: New shards.preference option for preferring replicas based on their leader status (#188)
a6d92b1 is described below

commit a6d92b10aed60d6bdf43684d3c5359b4f49ccd85
Author: Timothy Potter <th...@gmail.com>
AuthorDate: Wed Jun 23 12:21:51 2021 -0600

    SOLR-15472: New shards.preference option for preferring replicas based on their leader status (#188)
---
 solr/CHANGES.txt                                   |  3 +-
 solr/solr-ref-guide/src/distributed-requests.adoc  |  8 +++
 .../routing/NodePreferenceRulesComparator.java     | 13 +++++
 .../org/apache/solr/common/params/ShardParams.java |  3 +
 .../routing/NodePreferenceRulesComparatorTest.java | 66 +++++++++++++++++++++-
 ...RequestReplicaListTransformerGeneratorTest.java |  5 +-
 6 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 0227c27..d450ae2 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -352,7 +352,8 @@ Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this r
 
 New Features
 ---------------------
-(No changes)
+* SOLR-15472: New shards.preference option for preferring replicas based on their leader status, i.e.
+  `shards.preference=replica.leader:false` would prefer non-leader replicas. (wei wang via Timothy Potter)
 
 Improvements
 ---------------------
diff --git a/solr/solr-ref-guide/src/distributed-requests.adoc b/solr/solr-ref-guide/src/distributed-requests.adoc
index 334b2e6..894bf19 100644
--- a/solr/solr-ref-guide/src/distributed-requests.adoc
+++ b/solr/solr-ref-guide/src/distributed-requests.adoc
@@ -181,6 +181,11 @@ Applied after sorting by inherent replica attributes, this property defines a fa
 `node.sysprop`::
 Query will be routed to nodes with same defined system properties as the current one. For example, if you start Solr nodes on different racks, you'll want to identify those nodes by a <<configuring-solrconfig-xml.adoc#jvm-system-properties,system property>> (e.g., `-Drack=rack1`). Then, queries can contain `shards.preference=node.sysprop:sysprop.rack`, to make sure you always hit shards with the same value of `rack`.
 
+`replica.leader`::
+Prefer replicas based on their leader status, set to either `true` or `false`. Consider a shard with two `TLOG` replicas and four `PULL` replicas (six replicas in total, one of which is the leader).
+With `shards.preference=replica.leader:false`, 5 out of 6 replicas will be preferred. Contrast this with `shards.preference=replica.type:PULL` where only 4 of 6 replicas will be preferred.
+Note that the non-leader `TLOG` replica behaves like a `PULL` replica from a search perspective; it pulls index updates from the leader just like a `PULL` replica and does not perform soft-commits.
+The difference is that the non-leader `TLOG` replica also captures updates in its TLOG, so that it is a candidate to replace the current leader if it is lost.
 
 Examples:
 
@@ -205,4 +210,7 @@ Examples:
 * Prefer local replicas, and among them PULL replicas when available TLOG otherwise:
    `shards.preference=replica.location:local,replica.type:PULL,replica.type:TLOG`
 
+* Prefer any replica that is not a leader:
+    `shards.preference=replica.leader:false`
+
 Note that if you provide the settings in a query string, they need to be properly URL-encoded.
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/routing/NodePreferenceRulesComparator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/routing/NodePreferenceRulesComparator.java
index 7bbc742..f15f217 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/routing/NodePreferenceRulesComparator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/routing/NodePreferenceRulesComparator.java
@@ -117,6 +117,10 @@ public class NodePreferenceRulesComparator implements Comparator<Object> {
             lhs = hasCoreUrlPrefix(left, preferenceRule.value);
             rhs = hasCoreUrlPrefix(right, preferenceRule.value);
             break;
+          case ShardParams.SHARDS_PREFERENCE_REPLICA_LEADER:
+            lhs = hasLeaderStatus(left, preferenceRule.value);
+            rhs = hasLeaderStatus(right, preferenceRule.value);
+            break;
           case ShardParams.SHARDS_PREFERENCE_NODE_WITH_SAME_SYSPROP:
             if (sysPropsCache == null) {
               throw new IllegalArgumentException("Unable to get the NodesSysPropsCacher on sorting replicas by preference:"+ preferenceRule.value);
@@ -166,6 +170,7 @@ public class NodePreferenceRulesComparator implements Comparator<Object> {
       return s.startsWith(prefix);
     }
   }
+
   private static boolean hasReplicaType(Object o, String preferred) {
     if (!(o instanceof Replica)) {
       return false;
@@ -174,6 +179,14 @@ public class NodePreferenceRulesComparator implements Comparator<Object> {
     return s.equalsIgnoreCase(preferred);
   }
 
+  private static boolean hasLeaderStatus(Object o, String status) {
+    if (!(o instanceof Replica)) {
+      return false;
+    }
+    final boolean leaderStatus = ((Replica) o).isLeader();
+    return leaderStatus == Boolean.parseBoolean(status);
+  }
+
   public List<PreferenceRule> getSortRules() {
     return sortRules;
   }
diff --git a/solr/solrj/src/java/org/apache/solr/common/params/ShardParams.java b/solr/solrj/src/java/org/apache/solr/common/params/ShardParams.java
index 05cdef3..26695c0 100644
--- a/solr/solrj/src/java/org/apache/solr/common/params/ShardParams.java
+++ b/solr/solrj/src/java/org/apache/solr/common/params/ShardParams.java
@@ -69,6 +69,9 @@ public interface ShardParams {
   /** Replica location sort rule */
   String SHARDS_PREFERENCE_REPLICA_LOCATION = "replica.location";
 
+  /** Replica leader status sort rule, value= true/false */
+  String SHARDS_PREFERENCE_REPLICA_LEADER = "replica.leader";
+
   /** Node with same system property sort rule */
   String SHARDS_PREFERENCE_NODE_WITH_SAME_SYSPROP = "node.sysprop";
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/routing/NodePreferenceRulesComparatorTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/routing/NodePreferenceRulesComparatorTest.java
index 4131400..1e06d4b 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/routing/NodePreferenceRulesComparatorTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/routing/NodePreferenceRulesComparatorTest.java
@@ -22,8 +22,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.common.cloud.UrlScheme;
 import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.UrlScheme;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.params.ShardParams;
 import org.junit.Test;
@@ -93,6 +93,67 @@ public class NodePreferenceRulesComparatorTest extends SolrTestCaseJ4 {
     assertEquals("node3", getHost(replicas.get(3).getNodeName()));
   }
 
+  @Test
+  public void replicaLeaderTest() {
+    List<Replica> replicas = getBasicReplicaList();
+    // Add a non-leader NRT replica so that sorting by replica.type:NRT will cause a tie, order is decided by leader status
+    replicas.add(
+        new Replica(
+            "node4",
+            Map.of(ZkStateReader.BASE_URL_PROP, "http://host2:8983/solr",
+                ZkStateReader.NODE_NAME_PROP, "node4:8983_solr",
+                ZkStateReader.CORE_NAME_PROP, "collection1",
+                ZkStateReader.REPLICA_TYPE, "NRT"
+            ), "collection1", "shard1"
+        )
+    );
+    // Prefer non-leader only, therefore node1 has lowest priority
+    List<PreferenceRule> rules = PreferenceRule.from(ShardParams.SHARDS_PREFERENCE_REPLICA_LEADER + ":false");
+    NodePreferenceRulesComparator comparator = new NodePreferenceRulesComparator(rules, null);
+    replicas.sort(comparator);
+    assertEquals("node1:8983_solr", replicas.get(3).getNodeName());
+
+    // Prefer NRT replica, followed by non-leader
+    rules = PreferenceRule.from(
+        ShardParams.SHARDS_PREFERENCE_REPLICA_TYPE + ":NRT," +
+            ShardParams.SHARDS_PREFERENCE_REPLICA_LEADER + ":false");
+    comparator = new NodePreferenceRulesComparator(rules, null);
+    replicas.sort(comparator);
+    assertEquals("node4", getHost(replicas.get(0).getNodeName()));
+    assertEquals("node1", getHost(replicas.get(1).getNodeName()));
+
+    // Prefer non-leader first, followed by PULL replica and location host2
+    rules = PreferenceRule.from(
+        ShardParams.SHARDS_PREFERENCE_REPLICA_LEADER + ":false," +
+            ShardParams.SHARDS_PREFERENCE_REPLICA_TYPE + ":PULL," +
+            ShardParams.SHARDS_PREFERENCE_REPLICA_LOCATION + ":http://host2");
+    comparator = new NodePreferenceRulesComparator(rules, null);
+
+    replicas.sort(comparator);
+    assertEquals("node3", getHost(replicas.get(0).getNodeName()));
+    assertEquals("node4", getHost(replicas.get(1).getNodeName()));
+    assertEquals("node2", getHost(replicas.get(2).getNodeName()));
+    assertEquals("node1", getHost(replicas.get(3).getNodeName()));
+
+    // make sure a single leader only still gets selected
+    List<Replica> onlyLeader = new ArrayList<>();
+    onlyLeader.add(
+        new Replica(
+            "node1",
+            Map.of(
+                ZkStateReader.NODE_NAME_PROP, "node1:8983_solr",
+                ZkStateReader.CORE_NAME_PROP, "collection1",
+                ZkStateReader.REPLICA_TYPE, "NRT",
+                ZkStateReader.LEADER_PROP, "true"
+            ),"collection1","shard1"
+        )
+    );
+    rules = PreferenceRule.from(ShardParams.SHARDS_PREFERENCE_REPLICA_LEADER + ":false");
+    comparator = new NodePreferenceRulesComparator(rules, null);
+    replicas.sort(comparator);
+    assertEquals("node1:8983_solr", onlyLeader.get(0).getNodeName());
+  }
+
   @Test(expected = IllegalArgumentException.class)
   public void badRuleTest() {
     try {
@@ -125,7 +186,8 @@ public class NodePreferenceRulesComparatorTest extends SolrTestCaseJ4 {
             Map.of(
                 ZkStateReader.NODE_NAME_PROP, "node1:8983_solr",
                 ZkStateReader.CORE_NAME_PROP, "collection1",
-                ZkStateReader.REPLICA_TYPE, "NRT"
+                ZkStateReader.REPLICA_TYPE, "NRT",
+                ZkStateReader.LEADER_PROP, "true"
             ),"collection1","shard1"
         )
     );
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/routing/RequestReplicaListTransformerGeneratorTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/routing/RequestReplicaListTransformerGeneratorTest.java
index 499565a..5dcc6a1 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/routing/RequestReplicaListTransformerGeneratorTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/routing/RequestReplicaListTransformerGeneratorTest.java
@@ -104,8 +104,9 @@ public class RequestReplicaListTransformerGeneratorTest extends SolrTestCaseJ4 {
         )
     );
 
-    // replicaType and replicaBase combined rule param
-    String rulesParam = ShardParams.SHARDS_PREFERENCE_REPLICA_TYPE + ":NRT," +
+    // replica leader status, replicaType and replicaBase combined rule param
+    String rulesParam = ShardParams.SHARDS_PREFERENCE_REPLICA_LEADER + ":true," +
+        ShardParams.SHARDS_PREFERENCE_REPLICA_TYPE + ":NRT," +
         ShardParams.SHARDS_PREFERENCE_REPLICA_TYPE + ":TLOG," +
         ShardParams.SHARDS_PREFERENCE_REPLICA_BASE + ":stable:dividend:routingPreference";