mirror of https://github.com/apache/lucene.git
SOLR-9251: Support for a new tag 'role' in replica placement rules
This commit is contained in:
parent
1dc7480bcd
commit
df9fb16b46
|
@ -71,6 +71,8 @@ New Features
|
|||
|
||||
* SOLR-9216: Support collection.configName in MODIFYCOLLECTION request (Keith Laban, noble)
|
||||
|
||||
* SOLR-9251: Support for a new tag 'role' in replica placement rules (noble)
|
||||
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.net.InetAddress;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -31,6 +32,7 @@ import java.util.regex.Pattern;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.apache.solr.common.cloud.ZkStateReader;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.handler.admin.CoreAdminHandler;
|
||||
|
@ -49,10 +51,11 @@ public class ImplicitSnitch extends Snitch implements CoreAdminHandler.Invocable
|
|||
public static final String HOST = "host";
|
||||
public static final String CORES = "cores";
|
||||
public static final String DISK = "freedisk";
|
||||
public static final String ROLE = "role";
|
||||
public static final String SYSPROP = "sysprop.";
|
||||
public static final List<String> IP_SNITCHES = ImmutableList.of("ip_1", "ip_2", "ip_3", "ip_4");
|
||||
|
||||
public static final Set<String> tags = ImmutableSet.<String>builder().add(NODE, PORT, HOST, CORES, DISK).addAll(IP_SNITCHES).build();
|
||||
public static final Set<String> tags = ImmutableSet.<String>builder().add(NODE, PORT, HOST, CORES, DISK, ROLE).addAll(IP_SNITCHES).build();
|
||||
|
||||
|
||||
|
||||
|
@ -67,7 +70,7 @@ public class ImplicitSnitch extends Snitch implements CoreAdminHandler.Invocable
|
|||
Matcher hostAndPortMatcher = hostAndPortPattern.matcher(solrNode);
|
||||
if (hostAndPortMatcher.find()) ctx.getTags().put(PORT, hostAndPortMatcher.group(2));
|
||||
}
|
||||
|
||||
if (requestedTags.contains(ROLE)) fillRole(solrNode, ctx);
|
||||
addIpTags(solrNode, requestedTags, ctx);
|
||||
|
||||
ModifiableSolrParams params = new ModifiableSolrParams();
|
||||
|
@ -79,6 +82,24 @@ public class ImplicitSnitch extends Snitch implements CoreAdminHandler.Invocable
|
|||
if (params.size() > 0) ctx.invokeRemote(solrNode, params, ImplicitSnitch.class.getName(), null);
|
||||
}
|
||||
|
||||
private void fillRole(String solrNode, SnitchContext ctx) {
|
||||
Map roles = (Map) ctx.retrieve(ZkStateReader.ROLES); // we don't want to hit the ZK for each node
|
||||
// so cache and reuse
|
||||
if(roles == null) roles = ctx.getZkJson(ZkStateReader.ROLES);
|
||||
ctx.store(ZkStateReader.ROLES, roles == null ? Collections.emptyMap() : roles);
|
||||
if (roles != null) {
|
||||
for (Object o : roles.entrySet()) {
|
||||
Map.Entry e = (Map.Entry) o;
|
||||
if (e.getValue() instanceof List) {
|
||||
if(((List) e.getValue()).contains(solrNode)) {
|
||||
ctx.getTags().put(ROLE, e.getKey());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static long getUsableSpaceInGB() throws IOException {
|
||||
long space = Files.getFileStore(Paths.get("/")).getUsableSpace();
|
||||
long spaceInGB = space / 1024 / 1024 / 1024;
|
||||
|
|
|
@ -254,7 +254,8 @@ public class ReplicaAssigner {
|
|||
Integer n = nodeNames.get(liveNode);
|
||||
n = n == null ? 1 : n + 1;
|
||||
nodeNames.put(liveNode, n);
|
||||
Number coreCount = (Number) nodeVsTagsCopy.get(liveNode).get(ImplicitSnitch.CORES);
|
||||
Map<String, Object> tagsMap = nodeVsTagsCopy.get(liveNode);
|
||||
Number coreCount = tagsMap == null ? null: (Number) tagsMap.get(ImplicitSnitch.CORES);
|
||||
if (coreCount != null) {
|
||||
nodeVsTagsCopy.get(liveNode).put(ImplicitSnitch.CORES, coreCount.intValue() + 1);
|
||||
}
|
||||
|
@ -398,7 +399,7 @@ public class ReplicaAssigner {
|
|||
//now use the Snitch to get the tags
|
||||
for (SnitchInfoImpl info : snitches.values()) {
|
||||
if (!info.myTags.isEmpty()) {
|
||||
SnitchContext context = new SnitchContext(info, node);
|
||||
SnitchContext context = getSnitchCtx(node, info);
|
||||
info.nodeVsContext.put(node, context);
|
||||
try {
|
||||
info.snitch.getTags(node, info.myTags, context);
|
||||
|
@ -441,6 +442,11 @@ public class ReplicaAssigner {
|
|||
|
||||
}
|
||||
|
||||
private Map<String, Object> snitchSession = new HashMap<>();
|
||||
protected SnitchContext getSnitchCtx( String node, SnitchInfoImpl info) {
|
||||
return new SnitchContext(info, node, snitchSession);
|
||||
}
|
||||
|
||||
public static void verifySnitchConf(CoreContainer cc, List snitchConf) {
|
||||
getSnitchInfos(cc, snitchConf);
|
||||
}
|
||||
|
|
|
@ -137,7 +137,8 @@ public class Rule {
|
|||
if (replica.isWildCard()) {
|
||||
//this means for each replica, the value must match
|
||||
//shard match is already tested
|
||||
if (tag.canMatch(nodeVsTags.get(testNode).get(tag.name), phase)) return NODE_CAN_BE_ASSIGNED;
|
||||
Map<String, Object> tags = nodeVsTags.get(testNode);
|
||||
if (tag.canMatch(tags == null ? null : tags.get(tag.name), phase)) return NODE_CAN_BE_ASSIGNED;
|
||||
else return CANNOT_ASSIGN_FAIL;
|
||||
} else {
|
||||
int v = getNumberOfNodesWithSameTagVal(shard, nodeVsTags, shardVsNodeSet, shardName, tag, phase);
|
||||
|
@ -367,7 +368,12 @@ public class Rule {
|
|||
}
|
||||
|
||||
public int compare(String n1, String n2, Map<String, Map<String, Object>> nodeVsTags) {
|
||||
return isWildCard() ? 0 : operand.compare(nodeVsTags.get(n1).get(name), nodeVsTags.get(n2).get(name));
|
||||
Map<String, Object> tags = nodeVsTags.get(n1);
|
||||
Object n1Val = tags == null ? null : tags.get(name);
|
||||
tags = nodeVsTags.get(n2);
|
||||
Object n2Val = tags == null ? null : tags.get(name);
|
||||
if (n1Val == null || n2Val == null) return -1;
|
||||
return isWildCard() ? 0 : operand.compare(n1Val, n2Val);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,8 +32,10 @@ import org.apache.solr.common.params.CommonParams;
|
|||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.Utils;
|
||||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.update.UpdateShardHandler;
|
||||
import org.apache.zookeeper.data.Stat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -48,13 +50,15 @@ public class SnitchContext implements RemoteCallback {
|
|||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
private final Map<String, Object> tags = new HashMap<>();
|
||||
private String node;
|
||||
private Map<String, Object> session;
|
||||
final SnitchInfo snitchInfo;
|
||||
Exception exception;
|
||||
|
||||
|
||||
SnitchContext(SnitchInfo perSnitch, String node) {
|
||||
SnitchContext(SnitchInfo perSnitch, String node, Map<String, Object> session) {
|
||||
this.snitchInfo = perSnitch;
|
||||
this.node = node;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public SnitchInfo getSnitchInfo() {
|
||||
|
@ -65,6 +69,33 @@ public class SnitchContext implements RemoteCallback {
|
|||
return tags;
|
||||
}
|
||||
|
||||
public void store(String s, Object val) {
|
||||
if (session != null) session.put(s, val);
|
||||
|
||||
}
|
||||
|
||||
public Object retrieve(String s) {
|
||||
return session != null ? session.get(s) : null;
|
||||
|
||||
}
|
||||
|
||||
public Map getZkJson(String path) {
|
||||
if (snitchInfo.getCoreContainer().isZooKeeperAware()) {
|
||||
try {
|
||||
byte[] data = snitchInfo.getCoreContainer().getZkController().getZkClient().getData(path, null, new Stat(), true);
|
||||
if (data == null) return null;
|
||||
return (Map) Utils.fromJSON(data);
|
||||
} catch (Exception e) {
|
||||
log.warn("Unable to read from ZK path : " + path, e);
|
||||
return null;
|
||||
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getNode() {
|
||||
return node;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.apache.solr.cloud.rule;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -44,7 +45,7 @@ public class ImplicitSnitchTest {
|
|||
@Before
|
||||
public void beforeImplicitSnitchTest() {
|
||||
snitch = new ImplicitSnitch();
|
||||
context = new SnitchContext(null, null);
|
||||
context = new SnitchContext(null, null, new HashMap<>());
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,7 +82,7 @@ public class ImplicitSnitchTest {
|
|||
public void testGetTags_withIPv4RequestedTags_ip2_and_ip4_returns_two_tags() throws Exception {
|
||||
String node = "192.168.1.2:8983_solr";
|
||||
|
||||
SnitchContext context = new SnitchContext(null, node);
|
||||
SnitchContext context = new SnitchContext(null, node, new HashMap<>());
|
||||
snitch.getTags(node, Sets.newHashSet(IP_2, IP_4), context);
|
||||
|
||||
Map<String, Object> tags = context.getTags();
|
||||
|
@ -94,7 +95,7 @@ public class ImplicitSnitchTest {
|
|||
public void testGetTags_with_wrong_ipv4_format_ip_returns_nothing() throws Exception {
|
||||
String node = "192.168.1.2.1:8983_solr";
|
||||
|
||||
SnitchContext context = new SnitchContext(null, node);
|
||||
SnitchContext context = new SnitchContext(null, node, new HashMap<>());
|
||||
snitch.getTags(node, Sets.newHashSet(IP_1), context);
|
||||
|
||||
Map<String, Object> tags = context.getTags();
|
||||
|
@ -106,7 +107,7 @@ public class ImplicitSnitchTest {
|
|||
public void testGetTags_with_correct_ipv6_format_ip_returns_nothing() throws Exception {
|
||||
String node = "[0:0:0:0:0:0:0:1]:8983_solr";
|
||||
|
||||
SnitchContext context = new SnitchContext(null, node);
|
||||
SnitchContext context = new SnitchContext(null, node, new HashMap<>());
|
||||
snitch.getTags(node, Sets.newHashSet(IP_1), context);
|
||||
|
||||
Map<String, Object> tags = context.getTags();
|
||||
|
@ -129,7 +130,7 @@ public class ImplicitSnitchTest {
|
|||
public void testGetTags_withAllHostNameRequestedTags_returns_all_Tags() throws Exception {
|
||||
String node = "serv01.dc01.london.uk.apache.org:8983_solr";
|
||||
|
||||
SnitchContext context = new SnitchContext(null, node);
|
||||
SnitchContext context = new SnitchContext(null, node, new HashMap<>());
|
||||
//We need mocking here otherwise, we would need proper DNS entry for this test to pass
|
||||
ImplicitSnitch mockedSnitch = Mockito.spy(snitch);
|
||||
when(mockedSnitch.getHostIp(anyString())).thenReturn("10.11.12.13");
|
||||
|
@ -148,7 +149,7 @@ public class ImplicitSnitchTest {
|
|||
public void testGetTags_withHostNameRequestedTag_ip3_returns_1_tag() throws Exception {
|
||||
String node = "serv01.dc01.london.uk.apache.org:8983_solr";
|
||||
|
||||
SnitchContext context = new SnitchContext(null, node);
|
||||
SnitchContext context = new SnitchContext(null, node, new HashMap<>());
|
||||
//We need mocking here otherwise, we would need proper DNS entry for this test to pass
|
||||
ImplicitSnitch mockedSnitch = Mockito.spy(snitch);
|
||||
when(mockedSnitch.getHostIp(anyString())).thenReturn("10.11.12.13");
|
||||
|
@ -163,7 +164,7 @@ public class ImplicitSnitchTest {
|
|||
public void testGetTags_withHostNameRequestedTag_ip99999_returns_nothing() throws Exception {
|
||||
String node = "serv01.dc01.london.uk.apache.org:8983_solr";
|
||||
|
||||
SnitchContext context = new SnitchContext(null, node);
|
||||
SnitchContext context = new SnitchContext(null, node, new HashMap<>());
|
||||
//We need mocking here otherwise, we would need proper DNS entry for this test to pass
|
||||
ImplicitSnitch mockedSnitch = Mockito.spy(snitch);
|
||||
when(mockedSnitch.getHostIp(anyString())).thenReturn("10.11.12.13");
|
||||
|
|
|
@ -26,11 +26,14 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.cloud.rule.ReplicaAssigner.Position;
|
||||
import org.apache.solr.common.cloud.ZkStateReader;
|
||||
import org.apache.solr.common.util.Utils;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.apache.solr.cloud.rule.Rule.parseRule;
|
||||
import static org.apache.solr.common.util.Utils.makeMap;
|
||||
|
||||
|
@ -69,17 +72,43 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
|
||||
Map<Position, String> mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null ).getNodeMappings();
|
||||
assertNotNull(mapping);
|
||||
|
||||
mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null ).getNodeMappings();
|
||||
assertNotNull(mapping);
|
||||
|
||||
rules = parseRules("[{role:'!overseer'}]" );
|
||||
Map<String, Object> snitchSession = new HashMap<>();
|
||||
List<String> preferredOverseerNodes = ImmutableList.of("127.0.0.1:49947_", "127.0.0.1:49952_");
|
||||
ReplicaAssigner replicaAssigner = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null) {
|
||||
|
||||
@Override
|
||||
protected SnitchContext getSnitchCtx(String node, SnitchInfoImpl info) {
|
||||
return new SnitchContext(info, node, snitchSession){
|
||||
@Override
|
||||
public Map getZkJson(String path) {
|
||||
if(ZkStateReader.ROLES.equals(path)){
|
||||
return Collections.singletonMap("overseer", preferredOverseerNodes);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
mapping = replicaAssigner.getNodeMappings();
|
||||
assertNotNull(mapping);
|
||||
|
||||
for (String nodeName : mapping.values()) {
|
||||
assertFalse(preferredOverseerNodes.contains(nodeName));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -115,7 +144,7 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
Map shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2);
|
||||
Map<Position, String> mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings();
|
||||
assertNotNull(mapping);
|
||||
assertFalse(mapping.containsValue("127.0.0.1:49947_"));
|
||||
|
@ -127,7 +156,7 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2);
|
||||
mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0();
|
||||
assertNull(mapping);
|
||||
|
||||
|
@ -139,7 +168,7 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2);
|
||||
mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0();
|
||||
assertNotNull(mapping);
|
||||
assertFalse(mapping.containsValue("127.0.0.2:49958_"));
|
||||
|
@ -151,7 +180,7 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2);
|
||||
mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings();
|
||||
assertNotNull(mapping);
|
||||
|
||||
|
@ -162,7 +191,7 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2);
|
||||
mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0();
|
||||
assertNull(mapping);
|
||||
|
||||
|
@ -173,7 +202,7 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2);
|
||||
mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings();
|
||||
assertNotNull(mapping);
|
||||
|
||||
|
@ -204,14 +233,14 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
MockSnitch.nodeVsTags = nodeVsTags;
|
||||
Map<Position, String> mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0();
|
||||
assertNull(mapping);
|
||||
rulesStr = "rack:*,replica:<2~";
|
||||
rules = parse(Arrays.asList(rulesStr));
|
||||
mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null ,null).getNodeMappings();
|
||||
assertNotNull(mapping);
|
||||
|
||||
|
@ -219,7 +248,7 @@ public class RuleEngineTest extends SolrTestCaseJ4{
|
|||
rules = parse(Arrays.asList(rulesStr));
|
||||
mapping = new ReplicaAssigner(
|
||||
rules,
|
||||
shardVsReplicaCount, Collections.singletonList(MockSnitch.class.getName()),
|
||||
shardVsReplicaCount, singletonList(MockSnitch.class.getName()),
|
||||
new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null,null ).getNodeMappings();
|
||||
assertNotNull(mapping);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue