/* * 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.cloud.rule; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; 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.cloud.rule.Snitch; import org.apache.solr.common.cloud.rule.SnitchContext; import org.apache.solr.common.util.Utils; import org.apache.solr.core.CoreContainer; 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; public class RuleEngineTest extends SolrTestCaseJ4{ @Test public void testPlacement2(){ String s = "{" + " '127.0.0.1:49961_':{" + " 'node':'127.0.0.1:49961_'," + " 'freedisk':992," + " 'cores':1}," + " '127.0.0.1:49955_':{" + " 'node':'127.0.0.1:49955_'," + " 'freedisk':992," + " 'cores':1}," + " '127.0.0.1:49952_':{" + " 'node':'127.0.0.1:49952_'," + " 'freedisk':992," + " 'cores':1}," + " '127.0.0.1:49947_':{" + " 'node':'127.0.0.1:49947_'," + " 'freedisk':992," + " 'cores':1}," + " '127.0.0.1:49958_':{" + " 'node':'127.0.0.1:49958_'," + " 'freedisk':992," + " 'cores':1}}"; MockSnitch.nodeVsTags = (Map) Utils.fromJSON(s.getBytes(StandardCharsets.UTF_8)); Map shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2); List<Rule> rules = parseRules("[{'cores':'<4'}, {" + "'replica':'1',shard:'*','node':'*'}," + " {'freedisk':'>1'}]"); Map<Position, String> mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null ).getNodeMappings(); assertNotNull(mapping); mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null ).getNodeMappings(); assertNotNull(mapping); rules = parseRules("[{role:'!overseer'}, {'freedisk':'>1'}]" ); 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, CoreContainer cc) { return new ServerSnitchContext(info, node, snitchSession,cc){ @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)); } } public void testPlacement3(){ String s = "{" + " '127.0.0.1:49961_':{" + " 'node':'127.0.0.1:49961_'," + " 'freedisk':992," + " 'cores':1}," + " '127.0.0.2:49955_':{" + " 'node':'127.0.0.1:49955_'," + " 'freedisk':995," + " 'cores':1}," + " '127.0.0.3:49952_':{" + " 'node':'127.0.0.1:49952_'," + " 'freedisk':990," + " 'cores':1}," + " '127.0.0.1:49947_':{" + " 'node':'127.0.0.1:49947_'," + " 'freedisk':980," + " 'cores':1}," + " '127.0.0.2:49958_':{" + " 'node':'127.0.0.1:49958_'," + " 'freedisk':970," + " 'cores':1}}"; MockSnitch.nodeVsTags = (Map) Utils.fromJSON(s.getBytes(StandardCharsets.UTF_8)); //test not List<Rule> rules = parseRules( "[{cores:'<4'}, " + "{replica:'1',shard:'*',node:'*'}," + "{node:'!127.0.0.1:49947_'}," + "{freedisk:'>1'}]"); Map shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2); Map<Position, String> mapping = new ReplicaAssigner( rules, 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_")); rules = parseRules( "[{cores:'<4'}, " + "{replica:'1',node:'*'}," + "{freedisk:'>980'}]"); shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2); mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0(); assertNull(mapping); rules = parseRules( "[{cores:'<4'}, " + "{replica:'1',node:'*'}," + "{freedisk:'>980~'}]"); shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2); mapping = new ReplicaAssigner( rules, 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_")); rules = parseRules( "[{cores:'<4'}, " + "{replica:'1',shard:'*',host:'*'}]" ); shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2); mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings(); assertNotNull(mapping); rules = parseRules( "[{cores:'<4'}, " + "{replica:'1',shard:'**',host:'*'}]" ); shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2); mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0(); assertNull(mapping); rules = parseRules( "[{cores:'<4'}, " + "{replica:'1~',shard:'**',host:'*'}]" ); shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2); mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings(); assertNotNull(mapping); } private List<Rule> parseRules(String s) { List maps = (List) Utils.fromJSON(s.getBytes(StandardCharsets.UTF_8)); List<Rule> rules = new ArrayList<>(); for (Object map : maps) rules.add(new Rule((Map) map)); return rules; } @Test public void testPlacement() throws Exception { String rulesStr = "rack:*,replica:<2"; List<Rule> rules = parse(Arrays.asList(rulesStr)); Map shardVsReplicaCount = makeMap("shard1", 3, "shard2", 3); Map nodeVsTags = makeMap( "node1:80", makeMap("rack", "178"), "node2:80", makeMap("rack", "179"), "node3:80", makeMap("rack", "180"), "node4:80", makeMap("rack", "181"), "node5:80", makeMap("rack", "182") ); MockSnitch.nodeVsTags = nodeVsTags; Map<Position, String> mapping = new ReplicaAssigner( rules, 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, singletonList(MockSnitch.class.getName()), new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null ,null).getNodeMappings(); assertNotNull(mapping); rulesStr = "rack:*,shard:*,replica:<2";//for each shard there can be a max of 1 replica rules = parse(Arrays.asList(rulesStr)); mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null,null ).getNodeMappings(); assertNotNull(mapping); } public static class MockSnitch extends Snitch { static Map nodeVsTags = Collections.emptyMap(); @Override public void getTags(String solrNode, Set<String> requestedTags, SnitchContext ctx) { ctx.getTags().putAll((Map<? extends String, ?>) nodeVsTags.get(solrNode)); } @Override public boolean isKnownTag(String tag) { Map next = (Map) nodeVsTags.values().iterator().next(); return next.containsKey(tag); } } public static List<Rule> parse(List<String> rules) throws IOException { assert rules != null && !rules.isEmpty(); ArrayList<Rule> result = new ArrayList<>(); for (String s : rules) { if (s == null || s.trim().isEmpty()) continue; result.add(new Rule(parseRule(s))); } return result; } }