SOLR-9251: Support for a new tag 'role' in replica placement rules

This commit is contained in:
Noble Paul 2016-07-01 17:37:36 +05:30
parent 1dc7480bcd
commit df9fb16b46
7 changed files with 121 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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");

View File

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