diff --git a/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java b/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java index 27a2824336ea..ae731e261a53 100644 --- a/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java +++ b/solr/core/src/java/org/apache/solr/cloud/OverseerCollectionMessageHandler.java @@ -16,6 +16,36 @@ */ package org.apache.solr.cloud; +import static org.apache.solr.cloud.Assign.getNodesForNewReplicas; +import static org.apache.solr.common.cloud.DocCollection.SNITCH; +import static org.apache.solr.common.cloud.ZkStateReader.BASE_URL_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.CORE_NODE_NAME_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.ELECTION_NODE_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.MAX_SHARDS_PER_NODE; +import static org.apache.solr.common.cloud.ZkStateReader.PROPERTY_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.PROPERTY_VALUE_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.REJOIN_AT_HEAD_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR; +import static org.apache.solr.common.cloud.ZkStateReader.REPLICA_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICAPROP; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.BALANCESHARDUNIQUE; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATE; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATESHARD; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETEREPLICAPROP; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETESHARD; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.MIGRATESTATEFORMAT; +import static org.apache.solr.common.params.CollectionParams.CollectionAction.REMOVEROLE; +import static org.apache.solr.common.params.CommonAdminParams.ASYNC; +import static org.apache.solr.common.params.CommonParams.NAME; +import static org.apache.solr.common.util.StrUtils.formatString; +import static org.apache.solr.common.util.Utils.makeMap; + import java.io.IOException; import java.lang.invoke.MethodHandles; import java.net.URI; @@ -98,36 +128,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.apache.solr.cloud.Assign.getNodesForNewReplicas; -import static org.apache.solr.common.cloud.DocCollection.SNITCH; -import static org.apache.solr.common.cloud.ZkStateReader.BASE_URL_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.CORE_NAME_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.CORE_NODE_NAME_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.ELECTION_NODE_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.MAX_SHARDS_PER_NODE; -import static org.apache.solr.common.cloud.ZkStateReader.PROPERTY_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.PROPERTY_VALUE_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.REJOIN_AT_HEAD_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR; -import static org.apache.solr.common.cloud.ZkStateReader.REPLICA_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICAPROP; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDROLE; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.BALANCESHARDUNIQUE; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATE; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.CREATESHARD; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETE; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETEREPLICAPROP; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.DELETESHARD; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.MIGRATESTATEFORMAT; -import static org.apache.solr.common.params.CollectionParams.CollectionAction.REMOVEROLE; -import static org.apache.solr.common.params.CommonAdminParams.ASYNC; -import static org.apache.solr.common.params.CommonParams.NAME; -import static org.apache.solr.common.util.StrUtils.formatString; -import static org.apache.solr.common.util.Utils.makeMap; - /** * A {@link OverseerMessageHandler} that handles Collections API related * overseer messages. @@ -630,17 +630,20 @@ private void deleteReplica(ClusterState clusterState, ZkNodeProps message, Named requestMap = new HashMap<>(1, 1.0f); } - ModifiableSolrParams params = new ModifiableSolrParams(); - params.add(CoreAdminParams.ACTION, CoreAdminAction.UNLOAD.toString()); - params.add(CoreAdminParams.CORE, core); - - params.set(CoreAdminParams.DELETE_INDEX, message.getBool(CoreAdminParams.DELETE_INDEX, true)); - params.set(CoreAdminParams.DELETE_INSTANCE_DIR, message.getBool(CoreAdminParams.DELETE_INSTANCE_DIR, true)); - params.set(CoreAdminParams.DELETE_DATA_DIR, message.getBool(CoreAdminParams.DELETE_DATA_DIR, true)); - - sendShardRequest(replica.getNodeName(), params, shardHandler, asyncId, requestMap); + //Don't send the request if the node isn't up. Getting the baseUrl will fail if there is no liveNode + if(zkStateReader.getClusterState().liveNodesContain(replica.getNodeName())) { + ModifiableSolrParams params = new ModifiableSolrParams(); + params.add(CoreAdminParams.ACTION, CoreAdminAction.UNLOAD.toString()); + params.add(CoreAdminParams.CORE, core); + + params.set(CoreAdminParams.DELETE_INDEX, message.getBool(CoreAdminParams.DELETE_INDEX, true)); + params.set(CoreAdminParams.DELETE_INSTANCE_DIR, message.getBool(CoreAdminParams.DELETE_INSTANCE_DIR, true)); + params.set(CoreAdminParams.DELETE_DATA_DIR, message.getBool(CoreAdminParams.DELETE_DATA_DIR, true)); - processResponses(results, shardHandler, false, null, asyncId, requestMap); + sendShardRequest(replica.getNodeName(), params, shardHandler, asyncId, requestMap); + processResponses(results, shardHandler, false, null, asyncId, requestMap); + } + //check if the core unload removed the corenode zk entry if (waitForCoreNodeGone(collectionName, shard, replicaName, 5000)) return; diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java index a6a15082acf3..7d01ba6590ea 100644 --- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java +++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java @@ -180,7 +180,8 @@ public boolean equals(Object obj) { private final int localHostPort; // example: 54065 private final String hostName; // example: 127.0.0.1 - private final String nodeName; // example: 127.0.0.1:54065_solr + private final String genericNodeName; // example: 127.0.0.1:54065_solr + private final String nodeName; //set in solr.xml or genericNodeName as default private String baseURL; // example: http://127.0.0.1:54065/solr private final CloudConfig cloudConfig; @@ -270,8 +271,11 @@ public ZkController(final CoreContainer cc, String zkServerAddress, int zkClient this.zkServerAddress = zkServerAddress; this.localHostPort = cloudConfig.getSolrHostPort(); this.hostName = normalizeHostName(cloudConfig.getHost()); - this.nodeName = generateNodeName(this.hostName, Integer.toString(this.localHostPort), localHostContext); - MDCLoggingContext.setNode(nodeName); + this.genericNodeName = generateNodeName(this.hostName, Integer.toString(this.localHostPort), localHostContext); + String nodeName = cloudConfig.getNodeName(); + this.nodeName = nodeName == null || nodeName.isEmpty() ? this.genericNodeName : nodeName; + String loggingContext = this.nodeName == this.genericNodeName ? this.genericNodeName : String.format("%s[%s]", this.nodeName, this.genericNodeName); + MDCLoggingContext.setNode(loggingContext); this.leaderVoteWait = cloudConfig.getLeaderVoteWait(); this.leaderConflictResolveWait = cloudConfig.getLeaderConflictResolveWait(); @@ -670,7 +674,7 @@ private void init(CurrentCoreDescriptorProvider registerOnReconnect) { try { createClusterZkNodes(zkClient); zkStateReader.createClusterStateWatchersAndUpdate(); - this.baseURL = zkStateReader.getBaseUrlForNodeName(this.nodeName); + this.baseURL = zkStateReader.getBaseUrlFromGenericNodeName(this.genericNodeName); checkForExistingEphemeralNode(); @@ -737,9 +741,10 @@ private void checkForExistingEphemeralNode() throws KeeperException, Interrupted } boolean deleted = deletedLatch.await(zkClient.getSolrZooKeeper().getSessionTimeout() * 2, TimeUnit.MILLISECONDS); + if (!deleted) { - throw new SolrException(ErrorCode.SERVER_ERROR, "A previous ephemeral live node still exists. " + - "Solr cannot continue. Please ensure that no other Solr process using the same port is running already."); + throw new SolrException(ErrorCode.SERVER_ERROR, "An ephemeral live node still exists named '" + this.nodeName + + "'. Solr cannot continue. Please ensure that no other Solr process using the same port is running already or configured with the same nodeName in solr.xml"); } } @@ -818,7 +823,11 @@ private void createEphemeralLiveNode() throws KeeperException, String nodeName = getNodeName(); String nodePath = ZkStateReader.LIVE_NODES_ZKNODE + "/" + nodeName; log.info("Register node as live in ZooKeeper:" + nodePath); - zkClient.makePath(nodePath, CreateMode.EPHEMERAL, true); + zkClient.makePath(nodePath, getGenericNodeName().getBytes(), CreateMode.EPHEMERAL, true); + } + + private String getGenericNodeName() { + return genericNodeName; } public String getNodeName() { @@ -2099,7 +2108,7 @@ public void updateLeaderInitiatedRecoveryState(String collection, String shardId stateObj.put(ZkStateReader.STATE_PROP, state.toString()); // only update the createdBy value if it's not set if (stateObj.get("createdByNodeName") == null) { - stateObj.put("createdByNodeName", this.nodeName); + stateObj.put("createdByNodeName", this.genericNodeName); } if (stateObj.get("createdByCoreNodeName") == null && leaderCoreNodeName != null) { stateObj.put("createdByCoreNodeName", leaderCoreNodeName); diff --git a/solr/core/src/java/org/apache/solr/cloud/rule/ImplicitSnitch.java b/solr/core/src/java/org/apache/solr/cloud/rule/ImplicitSnitch.java index ac1d7ad35776..476b139f55a3 100644 --- a/solr/core/src/java/org/apache/solr/cloud/rule/ImplicitSnitch.java +++ b/solr/core/src/java/org/apache/solr/cloud/rule/ImplicitSnitch.java @@ -60,18 +60,19 @@ public class ImplicitSnitch extends Snitch implements CoreAdminHandler.Invocable @Override - public void getTags(String solrNode, Set requestedTags, SnitchContext ctx) { - if (requestedTags.contains(NODE)) ctx.getTags().put(NODE, solrNode); + public void getTags(String solrNodeName, Set requestedTags, SnitchContext ctx) { + String baseUrl = ctx.getSnitchInfo().getCoreContainer().getZkStateReader().getBaseUrlForNodeName(solrNodeName); + if (requestedTags.contains(NODE)) ctx.getTags().put(NODE, solrNodeName); if (requestedTags.contains(HOST)) { - Matcher hostAndPortMatcher = hostAndPortPattern.matcher(solrNode); + Matcher hostAndPortMatcher = hostAndPortPattern.matcher(baseUrl); if (hostAndPortMatcher.find()) ctx.getTags().put(HOST, hostAndPortMatcher.group(1)); } if (requestedTags.contains(PORT)) { - Matcher hostAndPortMatcher = hostAndPortPattern.matcher(solrNode); + Matcher hostAndPortMatcher = hostAndPortPattern.matcher(baseUrl); if (hostAndPortMatcher.find()) ctx.getTags().put(PORT, hostAndPortMatcher.group(2)); } - if (requestedTags.contains(ROLE)) fillRole(solrNode, ctx); - addIpTags(solrNode, requestedTags, ctx); + if (requestedTags.contains(ROLE)) fillRole(solrNodeName, ctx); + addIpTags(baseUrl, requestedTags, ctx); ModifiableSolrParams params = new ModifiableSolrParams(); if (requestedTags.contains(CORES)) params.add(CORES, "1"); @@ -79,7 +80,7 @@ public void getTags(String solrNode, Set requestedTags, SnitchContext ct for (String tag : requestedTags) { if (tag.startsWith(SYSPROP)) params.add(SYSPROP, tag.substring(SYSPROP.length())); } - if (params.size() > 0) ctx.invokeRemote(solrNode, params, ImplicitSnitch.class.getName(), null); + if (params.size() > 0) ctx.invokeRemote(solrNodeName, params, ImplicitSnitch.class.getName(), null); } private void fillRole(String solrNode, SnitchContext ctx) { diff --git a/solr/core/src/java/org/apache/solr/core/CloudConfig.java b/solr/core/src/java/org/apache/solr/core/CloudConfig.java index 447dd22293b1..7ed832ae6832 100644 --- a/solr/core/src/java/org/apache/solr/core/CloudConfig.java +++ b/solr/core/src/java/org/apache/solr/core/CloudConfig.java @@ -49,11 +49,13 @@ public class CloudConfig { private final int createCollectionWaitTimeTillActive; private final boolean createCollectionCheckLeaderActive; + + private final String nodeName; CloudConfig(String zkHost, int zkClientTimeout, int hostPort, String hostName, String hostContext, boolean useGenericCoreNames, int leaderVoteWait, int leaderConflictResolveWait, int autoReplicaFailoverWaitAfterExpiration, int autoReplicaFailoverWorkLoopDelay, int autoReplicaFailoverBadNodeExpiration, String zkCredentialsProviderClass, - String zkACLProviderClass, int createCollectionWaitTimeTillActive, boolean createCollectionCheckLeaderActive) { + String zkACLProviderClass, int createCollectionWaitTimeTillActive, boolean createCollectionCheckLeaderActive, String nodeName) { this.zkHost = zkHost; this.zkClientTimeout = zkClientTimeout; this.hostPort = hostPort; @@ -69,6 +71,7 @@ public class CloudConfig { this.zkACLProviderClass = zkACLProviderClass; this.createCollectionWaitTimeTillActive = createCollectionWaitTimeTillActive; this.createCollectionCheckLeaderActive = createCollectionCheckLeaderActive; + this.nodeName = nodeName; if (this.hostPort == -1) throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "'hostPort' must be configured to run SolrCloud"); @@ -135,6 +138,10 @@ public int getCreateCollectionWaitTimeTillActive() { public boolean isCreateCollectionCheckLeaderActive() { return createCollectionCheckLeaderActive; } + + public String getNodeName() { + return nodeName; + } public static class CloudConfigBuilder { @@ -164,6 +171,7 @@ public static class CloudConfigBuilder { private String zkACLProviderClass; private int createCollectionWaitTimeTillActive = DEFAULT_CREATE_COLLECTION_ACTIVE_WAIT; private boolean createCollectionCheckLeaderActive = DEFAULT_CREATE_COLLECTION_CHECK_LEADER_ACTIVE; + private String nodeName = null; public CloudConfigBuilder(String hostName, int hostPort) { this(hostName, hostPort, null); @@ -235,11 +243,16 @@ public CloudConfigBuilder setCreateCollectionCheckLeaderActive(boolean createCol return this; } + public CloudConfigBuilder setNodeName(String nodeName) { + this.nodeName = nodeName; + return this; + } + public CloudConfig build() { return new CloudConfig(zkHost, zkClientTimeout, hostPort, hostName, hostContext, useGenericCoreNames, leaderVoteWait, leaderConflictResolveWait, autoReplicaFailoverWaitAfterExpiration, autoReplicaFailoverWorkLoopDelay, autoReplicaFailoverBadNodeExpiration, zkCredentialsProviderClass, zkACLProviderClass, createCollectionWaitTimeTillActive, - createCollectionCheckLeaderActive); + createCollectionCheckLeaderActive, nodeName); } } } diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java index ff3843b7d697..b75a0bda4fdb 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -1231,6 +1231,10 @@ public ZkController getZkController() { return zkSys.getZkController(); } + public ZkStateReader getZkStateReader() { + return isZooKeeperAware() ? zkSys.getZkController().getZkStateReader() : null; + } + public NodeConfig getConfig() { return cfg; } diff --git a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java index 65b248d62595..6d58567705a0 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java +++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java @@ -364,6 +364,9 @@ private static CloudConfig fillSolrCloudSection(NamedList nl) { case "genericCoreNodeNames": builder.setUseGenericCoreNames(Boolean.parseBoolean(value)); break; + case "nodeName": + builder.setNodeName(value); + break; case "zkACLProvider": builder.setZkACLProviderClass(value); break; diff --git a/solr/core/src/test-files/solr/solr.xml b/solr/core/src/test-files/solr/solr.xml index f381475ab1a5..c94638c6ae6f 100644 --- a/solr/core/src/test-files/solr/solr.xml +++ b/solr/core/src/test-files/solr/solr.xml @@ -37,6 +37,7 @@ ${hostContext:solr} ${solr.zkclienttimeout:30000} ${genericCoreNodeNames:true} + ${solr.nodeName:} ${leaderVoteWait:10000} ${distribUpdateConnTimeout:45000} ${distribUpdateSoTimeout:340000} diff --git a/solr/core/src/test/org/apache/solr/cloud/HttpPartitionTest.java b/solr/core/src/test/org/apache/solr/cloud/HttpPartitionTest.java index 63dab3bcdf25..e1209c1e15b4 100644 --- a/solr/core/src/test/org/apache/solr/cloud/HttpPartitionTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/HttpPartitionTest.java @@ -618,14 +618,6 @@ private NamedList realTimeGetDocId(HttpSolrClient solr, String docId) throws Sol return solr.request(qr); } - protected int getReplicaPort(Replica replica) { - String replicaNode = replica.getNodeName(); - String tmp = replicaNode.substring(replicaNode.indexOf(':')+1); - if (tmp.indexOf('_') != -1) - tmp = tmp.substring(0,tmp.indexOf('_')); - return Integer.parseInt(tmp); - } - protected void waitToSeeReplicasActive(String testCollectionName, String shardId, Set replicasToCheck, int maxWaitSecs) throws Exception { final RTimer timer = new RTimer(); diff --git a/solr/core/src/test/org/apache/solr/cloud/RollingRestartTest.java b/solr/core/src/test/org/apache/solr/cloud/RollingRestartTest.java index 6a906bbc0199..d6254b308e87 100644 --- a/solr/core/src/test/org/apache/solr/cloud/RollingRestartTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/RollingRestartTest.java @@ -66,6 +66,7 @@ public void restartWithRolesTest() throws Exception { numDesignateOverseers = Math.max(getShardCount(), numDesignateOverseers); List designates = new ArrayList<>(); List designateJettys = new ArrayList<>(); + for (int i = 0; i < numDesignateOverseers; i++) { int n = random().nextInt(getShardCount()); String nodeName = cloudJettys.get(n).nodeName; diff --git a/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java b/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java index b86543919770..8aa861422722 100644 --- a/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java @@ -99,41 +99,41 @@ public void testNodeNameUrlConversion() throws Exception { // getBaseUrlForNodeName assertEquals("http://zzz.xxx:1234/solr", - zkStateReader.getBaseUrlForNodeName("zzz.xxx:1234_solr")); + zkStateReader.getBaseUrlFromGenericNodeName("zzz.xxx:1234_solr")); assertEquals("http://xxx:99", - zkStateReader.getBaseUrlForNodeName("xxx:99_")); + zkStateReader.getBaseUrlFromGenericNodeName("xxx:99_")); assertEquals("http://foo-bar.baz.org:9999/some_dir", - zkStateReader.getBaseUrlForNodeName("foo-bar.baz.org:9999_some_dir")); + zkStateReader.getBaseUrlFromGenericNodeName("foo-bar.baz.org:9999_some_dir")); assertEquals("http://foo-bar.baz.org:9999/solr/sub_dir", - zkStateReader.getBaseUrlForNodeName("foo-bar.baz.org:9999_solr%2Fsub_dir")); + zkStateReader.getBaseUrlFromGenericNodeName("foo-bar.baz.org:9999_solr%2Fsub_dir")); // generateNodeName + getBaseUrlForNodeName assertEquals("http://foo:9876/solr", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo", "9876", "solr"))); assertEquals("http://foo:9876/solr", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo", "9876", "/solr"))); assertEquals("http://foo:9876/solr", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo", "9876", "/solr/"))); assertEquals("http://foo.bar.com:9876/solr/sub_dir", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo.bar.com", "9876", "solr/sub_dir"))); assertEquals("http://foo.bar.com:9876/solr/sub_dir", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo.bar.com", "9876", "/solr/sub_dir/"))); assertEquals("http://foo-bar:9876", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo-bar", "9876", ""))); assertEquals("http://foo-bar:9876", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo-bar", "9876", "/"))); assertEquals("http://foo-bar.com:80/some_dir", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo-bar.com", "80", "some_dir"))); assertEquals("http://foo-bar.com:80/some_dir", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo-bar.com", "80", "/some_dir"))); } @@ -148,10 +148,10 @@ public void testNodeNameUrlConversion() throws Exception { zkStateReader.createClusterStateWatchersAndUpdate(); assertEquals("https://zzz.xxx:1234/solr", - zkStateReader.getBaseUrlForNodeName("zzz.xxx:1234_solr")); + zkStateReader.getBaseUrlFromGenericNodeName("zzz.xxx:1234_solr")); assertEquals("https://foo-bar.com:80/some_dir", - zkStateReader.getBaseUrlForNodeName + zkStateReader.getBaseUrlFromGenericNodeName (ZkController.generateNodeName("foo-bar.com", "80", "/some_dir"))); } diff --git a/solr/core/src/test/org/apache/solr/cloud/rule/ImplicitSnitchTest.java b/solr/core/src/test/org/apache/solr/cloud/rule/ImplicitSnitchTest.java index 5ae9710ab396..519b397aac4b 100644 --- a/solr/core/src/test/org/apache/solr/cloud/rule/ImplicitSnitchTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/rule/ImplicitSnitchTest.java @@ -4,6 +4,12 @@ import java.util.Map; import com.google.common.collect.Sets; + +import org.apache.solr.cloud.MockZkStateReader; +import org.apache.solr.cloud.rule.SnitchContext.SnitchInfo; +import org.apache.solr.common.cloud.ZkStateReader; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.util.MockCoreContainer; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -35,7 +41,6 @@ public class ImplicitSnitchTest { private ImplicitSnitch snitch; - private SnitchContext context; private static final String IP_1 = "ip_1"; private static final String IP_2 = "ip_2"; @@ -45,14 +50,29 @@ public class ImplicitSnitchTest { @Before public void beforeImplicitSnitchTest() { snitch = new ImplicitSnitch(); - context = new SnitchContext(null, null, new HashMap<>()); + } + + private SnitchContext getContext(String nodeName) { + Map liveNodes = new HashMap(); + liveNodes.put(nodeName, nodeName); + + + ZkStateReader mockZkStateReader = new MockZkStateReader(null, null, liveNodes); + + CoreContainer mockCoreContainer = Mockito.spy(new MockCoreContainer()); + + Mockito.when(mockCoreContainer.getZkStateReader()).thenReturn(mockZkStateReader); + + SnitchInfo info = new ReplicaAssigner.SnitchInfoImpl(null, snitch, mockCoreContainer); + + return new SnitchContext(info, nodeName, new HashMap<>()); } @Test public void testGetTags_withAllIPv4RequestedTags_with_omitted_zeros_returns_four_tags() throws Exception { String node = "5:8983_solr"; - + SnitchContext context = getContext(node); snitch.getTags(node, Sets.newHashSet(IP_1, IP_2, IP_3, IP_4), context); Map tags = context.getTags(); @@ -67,7 +87,7 @@ public void testGetTags_withAllIPv4RequestedTags_with_omitted_zeros_returns_four @Test public void testGetTags_withAllIPv4RequestedTags_returns_four_tags() throws Exception { String node = "192.168.1.2:8983_solr"; - + SnitchContext context = getContext(node); snitch.getTags(node, Sets.newHashSet(IP_1, IP_2, IP_3, IP_4), context); Map tags = context.getTags(); @@ -81,8 +101,7 @@ public void testGetTags_withAllIPv4RequestedTags_returns_four_tags() throws Exce @Test 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, new HashMap<>()); + SnitchContext context = getContext(node); snitch.getTags(node, Sets.newHashSet(IP_2, IP_4), context); Map tags = context.getTags(); @@ -94,8 +113,8 @@ public void testGetTags_withIPv4RequestedTags_ip2_and_ip4_returns_two_tags() thr @Test public void testGetTags_with_wrong_ipv4_format_ip_returns_nothing() throws Exception { String node = "192.168.1.2.1:8983_solr"; + SnitchContext context = getContext(node); - SnitchContext context = new SnitchContext(null, node, new HashMap<>()); snitch.getTags(node, Sets.newHashSet(IP_1), context); Map tags = context.getTags(); @@ -107,7 +126,7 @@ public void testGetTags_with_wrong_ipv4_format_ip_returns_nothing() throws Excep 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, new HashMap<>()); + SnitchContext context = getContext(node); snitch.getTags(node, Sets.newHashSet(IP_1), context); Map tags = context.getTags(); @@ -118,6 +137,7 @@ public void testGetTags_with_correct_ipv6_format_ip_returns_nothing() throws Exc @Test public void testGetTags_withEmptyRequestedTag_returns_nothing() throws Exception { String node = "192.168.1.2:8983_solr"; + SnitchContext context = getContext(node); snitch.getTags(node, Sets.newHashSet(), context); @@ -130,7 +150,7 @@ public void testGetTags_withEmptyRequestedTag_returns_nothing() throws Exception 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, new HashMap<>()); + SnitchContext context = getContext(node); //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"); @@ -149,7 +169,7 @@ public void testGetTags_withAllHostNameRequestedTags_returns_all_Tags() throws E 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, new HashMap<>()); + SnitchContext context = getContext(node); //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"); @@ -164,7 +184,7 @@ public void testGetTags_withHostNameRequestedTag_ip3_returns_1_tag() throws Exce 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, new HashMap<>()); + SnitchContext context = getContext(node); //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"); @@ -184,4 +204,4 @@ public void testIsKnownTag_ip1() throws Exception { assertFalse(snitch.isKnownTag("ip_5")); } -} \ No newline at end of file +} diff --git a/solr/core/src/test/org/apache/solr/cloud/rule/RuleEngineTest.java b/solr/core/src/test/org/apache/solr/cloud/rule/RuleEngineTest.java index 01dd868b964c..ca87e1f3e0ce 100644 --- a/solr/core/src/test/org/apache/solr/cloud/rule/RuleEngineTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/rule/RuleEngineTest.java @@ -28,10 +28,14 @@ import com.google.common.collect.ImmutableList; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.cloud.MockZkStateReader; import org.apache.solr.cloud.rule.ReplicaAssigner.Position; import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.common.util.Utils; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.core.SolrResourceLoader; import org.junit.Test; +import org.mockito.Mockito; import static java.util.Collections.singletonList; import static org.apache.solr.cloud.rule.Rule.parseRule; @@ -40,8 +44,6 @@ public class RuleEngineTest extends SolrTestCaseJ4{ @Test public void testPlacement2(){ - - String s = "{" + " '127.0.0.1:49961_':{" + " 'node':'127.0.0.1:49961_'," + @@ -64,6 +66,8 @@ public void testPlacement2(){ " 'freedisk':992," + " 'cores':1}}"; MockSnitch.nodeVsTags = (Map) Utils.fromJSON(s.getBytes(StandardCharsets.UTF_8)); + CoreContainer cc = getMockCoreContainer(MockSnitch.nodeVsTags.keySet()); + Map shardVsReplicaCount = makeMap("shard1", 2, "shard2", 2); List rules = parseRules("[{'cores':'<4'}, {" + @@ -73,13 +77,13 @@ public void testPlacement2(){ Map mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null ).getNodeMappings(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null ).getNodeMappings(); assertNotNull(mapping); mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null ).getNodeMappings(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null ).getNodeMappings(); assertNotNull(mapping); rules = parseRules("[{role:'!overseer'}]" ); @@ -88,7 +92,7 @@ shardVsReplicaCount, singletonList(MockSnitch.class.getName()), ReplicaAssigner replicaAssigner = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null) { + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null) { @Override protected SnitchContext getSnitchCtx(String node, SnitchInfoImpl info) { @@ -135,6 +139,8 @@ public void testPlacement3(){ " 'freedisk':970," + " 'cores':1}}"; MockSnitch.nodeVsTags = (Map) Utils.fromJSON(s.getBytes(StandardCharsets.UTF_8)); + CoreContainer cc = getMockCoreContainer(MockSnitch.nodeVsTags.keySet()); + //test not List rules = parseRules( "[{cores:'<4'}, " + @@ -145,7 +151,7 @@ public void testPlacement3(){ Map mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null).getNodeMappings(); assertNotNull(mapping); assertFalse(mapping.containsValue("127.0.0.1:49947_")); @@ -157,7 +163,7 @@ shardVsReplicaCount, singletonList(MockSnitch.class.getName()), mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null).getNodeMappings0(); assertNull(mapping); @@ -169,7 +175,7 @@ shardVsReplicaCount, singletonList(MockSnitch.class.getName()), mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null).getNodeMappings0(); assertNotNull(mapping); assertFalse(mapping.containsValue("127.0.0.2:49958_")); @@ -181,7 +187,7 @@ shardVsReplicaCount, singletonList(MockSnitch.class.getName()), mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null).getNodeMappings(); assertNotNull(mapping); rules = parseRules( @@ -192,7 +198,7 @@ shardVsReplicaCount, singletonList(MockSnitch.class.getName()), mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null).getNodeMappings0(); assertNull(mapping); rules = parseRules( @@ -203,7 +209,7 @@ shardVsReplicaCount, singletonList(MockSnitch.class.getName()), mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, null).getNodeMappings(); assertNotNull(mapping); @@ -231,17 +237,19 @@ public void testPlacement() throws Exception { "node5:80", makeMap("rack", "182") ); MockSnitch.nodeVsTags = nodeVsTags; + CoreContainer cc = getMockCoreContainer(MockSnitch.nodeVsTags.keySet()); + Map mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null, null).getNodeMappings0(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc, 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(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc ,null).getNodeMappings(); assertNotNull(mapping); rulesStr = "rack:*,shard:*,replica:<2";//for each shard there can be a max of 1 replica @@ -249,7 +257,7 @@ shardVsReplicaCount, singletonList(MockSnitch.class.getName()), mapping = new ReplicaAssigner( rules, shardVsReplicaCount, singletonList(MockSnitch.class.getName()), - new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), null,null ).getNodeMappings(); + new HashMap(), new ArrayList<>(MockSnitch.nodeVsTags.keySet()), cc ,null ).getNodeMappings(); assertNotNull(mapping); } @@ -277,4 +285,22 @@ public static List parse(List rules) throws IOException { } return result; } + + private CoreContainer getMockCoreContainer(Set nodes) { + Map liveNodes = new HashMap(); + SolrResourceLoader loader = new SolrResourceLoader(); + + for(String node: nodes) { + liveNodes.put(node, node); + } + + ZkStateReader mockZkStateReader = new MockZkStateReader(null, null, liveNodes); + + CoreContainer mockCoreContainer = Mockito.mock(CoreContainer.class); + + Mockito.when(mockCoreContainer.getZkStateReader()).thenReturn(mockZkStateReader); + Mockito.when(mockCoreContainer.getResourceLoader()).thenReturn(loader); + + return mockCoreContainer; + } } diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java index 3f422faa0460..7002da45fc89 100644 --- a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java +++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkStateReader.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -30,7 +31,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Set; -import java.util.TreeSet; +import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -130,7 +131,8 @@ public class ZkStateReader implements Closeable { /** Collections with format2 state.json, not "interesting" and not actively watched. */ private final ConcurrentHashMap lazyCollectionStates = new ConcurrentHashMap<>(); - private volatile Set liveNodes = emptySet(); + /** visibility relaxed for testing **/ + protected volatile Map liveNodes = emptyMap(); private volatile Map clusterProperties = Collections.emptyMap(); @@ -484,7 +486,7 @@ public void process(WatchedEvent event) { */ private void constructState(Set changedCollections) { - Set liveNodes = this.liveNodes; // volatile read + Set liveNodes = this.liveNodes.keySet(); // volatile read // Legacy clusterstate is authoritative, for backwards compatibility. // To move a collection's state to format2, first create the new state2 format node, then remove legacy entry. @@ -652,25 +654,35 @@ public String toString() { // We don't get a Stat or track versions on getChildren() calls, so force linearization. private final Object refreshLiveNodesLock = new Object(); // Ensures that only the latest getChildren fetch gets applied. - private final AtomicReference> lastFetchedLiveNodes = new AtomicReference<>(); + private final AtomicReference> lastFetchedLiveNodes = new AtomicReference<>(); /** * Refresh live_nodes. */ private void refreshLiveNodes(Watcher watcher) throws KeeperException, InterruptedException { synchronized (refreshLiveNodesLock) { - Set newLiveNodes; + Map newLiveNodes; try { List nodeList = zkClient.getChildren(LIVE_NODES_ZKNODE, watcher, true); - newLiveNodes = new HashSet<>(nodeList); + newLiveNodes = new HashMap(); + for(String c: nodeList) { + byte[] ndb = zkClient.getData(LIVE_NODES_ZKNODE + "/" + c , null, null, false); + String nodeData; + if(null == ndb || ndb.length == 0) { + nodeData = c; + } else { + nodeData = new String(ndb); + } + newLiveNodes.put(c, nodeData); + } } catch (KeeperException.NoNodeException e) { - newLiveNodes = emptySet(); + newLiveNodes = emptyMap(); } lastFetchedLiveNodes.set(newLiveNodes); } // Can't lock getUpdateLock() until we release the other, it would cause deadlock. - Set oldLiveNodes, newLiveNodes; + Map oldLiveNodes, newLiveNodes; synchronized (getUpdateLock()) { newLiveNodes = lastFetchedLiveNodes.getAndSet(null); if (newLiveNodes == null) { @@ -681,12 +693,12 @@ private void refreshLiveNodes(Watcher watcher) throws KeeperException, Interrupt oldLiveNodes = this.liveNodes; this.liveNodes = newLiveNodes; if (clusterState != null) { - clusterState.setLiveNodes(newLiveNodes); + clusterState.setLiveNodes(newLiveNodes.keySet()); } } LOG.info("Updated live nodes from ZooKeeper... ({}) -> ({})", oldLiveNodes.size(), newLiveNodes.size()); if (LOG.isDebugEnabled()) { - LOG.debug("Updated live nodes from ZooKeeper... {} -> {}", new TreeSet<>(oldLiveNodes), new TreeSet<>(newLiveNodes)); + LOG.debug("Updated live nodes from ZooKeeper... {} -> {}", new TreeMap<>(oldLiveNodes), new TreeMap<>(newLiveNodes)); } } @@ -894,20 +906,43 @@ public ConfigData getSecurityProps(boolean getFresh) { return null; } + /** + * Returns the baseURL corresponding to a given node's nodeName -- + * NOTE: THIS NOW REQUIRES THAT THE nodeName EXISTS in liveNodes in the cluster. + * @lucene.experimental + */ + public String getBaseUrlForNodeName(final String nodeName) { + final String genericNodeName = this.liveNodes.get(nodeName); //volatile read + + if(null == genericNodeName) { + //TODO how to handle this? + //In here means that someone might be using this method to convert + //:_ url to a base url as this method used to do + // + //should we fallback to original behavior or throw an exception? + //when parsing the liveNodes map the key will still be + //genericNodeName -> genericNodeName if no data value is found in liveNodes + //return getBaseUrlFromGenericNodeName(nodeName); + throw new IllegalArgumentException(String.format("nodeName: '%s' is not one of %s", nodeName, this.liveNodes.keySet())); + } + + return getBaseUrlFromGenericNodeName(genericNodeName); + } + /** * Returns the baseURL corresponding to a given node's nodeName -- * NOTE: does not (currently) imply that the nodeName (or resulting * baseURL) exists in the cluster. * @lucene.experimental */ - public String getBaseUrlForNodeName(final String nodeName) { - final int _offset = nodeName.indexOf("_"); + public String getBaseUrlFromGenericNodeName(final String genericNodeName) { + final int _offset = genericNodeName.indexOf("_"); if (_offset < 0) { - throw new IllegalArgumentException("nodeName does not contain expected '_' seperator: " + nodeName); + throw new IllegalArgumentException("nodeName does not contain expected '_' seperator: " + genericNodeName); } - final String hostAndPort = nodeName.substring(0,_offset); + final String hostAndPort = genericNodeName.substring(0,_offset); try { - final String path = URLDecoder.decode(nodeName.substring(1+_offset), "UTF-8"); + final String path = URLDecoder.decode(genericNodeName.substring(1+_offset), "UTF-8"); String urlScheme = getClusterProperty(URL_SCHEME, "http"); return urlScheme + "://" + hostAndPort + (path.isEmpty() ? "" : ("/" + path)); } catch (UnsupportedEncodingException e) { @@ -936,7 +971,7 @@ public void process(WatchedEvent event) { return; } - Set liveNodes = ZkStateReader.this.liveNodes; + Set liveNodes = ZkStateReader.this.liveNodes.keySet(); LOG.info("A cluster state change: [{}] for collection [{}] has occurred - updating... (live nodes size: [{}])", event, coll, liveNodes.size()); @@ -1188,7 +1223,7 @@ public void registerCollectionStateWatcher(String collection, CollectionStateWat } DocCollection state = clusterState.getCollectionOrNull(collection); - if (stateWatcher.onStateChanged(liveNodes, state) == true) { + if (stateWatcher.onStateChanged(liveNodes.keySet(), state) == true) { removeCollectionStateWatcher(collection, stateWatcher); } } @@ -1260,7 +1295,6 @@ Set getStateWatchers(String collection) { // returns true if the state has changed private boolean updateWatchedCollection(String coll, DocCollection newState) { - if (newState == null) { LOG.info("Deleting data for [{}]", coll); watchedCollectionStates.remove(coll); diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java index 4b0f7abbe73c..ad25f3fef29d 100644 --- a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java +++ b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; import java.lang.invoke.MethodHandles; +import java.net.MalformedURLException; import java.net.ServerSocket; import java.net.URI; import java.net.URL; @@ -369,6 +370,8 @@ protected String getStateFormat() { } return defaultStateFormat; // random } + + protected boolean randomNameNodes = true; protected List createJettys(int numJettys) throws Exception { List jettys = new ArrayList<>(); @@ -394,6 +397,11 @@ protected List createJettys(int numJettys) throws Exception { File jettyDir = createTempDir("shard-" + i).toFile(); jettyDir.mkdirs(); + + if(randomNameNodes && random().nextBoolean()) { + System.setProperty("solr.nodeName", "node"+cnt); + } + setupJettySolrHome(jettyDir); log.info("create jetty {} in directory {}", i, jettyDir); JettySolrRunner j = createJetty(jettyDir, useJettyDataDir ? getDataDir(testDir + "/jetty" @@ -401,6 +409,8 @@ protected List createJettys(int numJettys) throws Exception { jettys.add(j); SolrClient client = createNewSolrClient(j.getLocalPort()); clients.add(client); + + System.clearProperty("solr.nodeName"); } this.jettys.addAll(jettys); @@ -506,6 +516,10 @@ public JettySolrRunner createJetty(File solrHome, String dataDir, String shardLi props.setProperty("solr.data.dir", getDataDir(dataDir)); props.setProperty("coreRootDirectory", solrHome.toPath().resolve("cores").toAbsolutePath().toString()); + if(null != System.getProperty("solr.nodeName")){ + props.setProperty("solr.nodeName", System.getProperty("solr.nodeName")); + } + JettySolrRunner jetty = new JettySolrRunner(solrHome.getPath(), props, jettyconfig); jetty.start(); @@ -550,12 +564,9 @@ public JettySolrRunner createProxiedJetty(File solrHome, String dataDir, return jetty; } - protected int getReplicaPort(Replica replica) { - String replicaNode = replica.getNodeName(); - String tmp = replicaNode.substring(replicaNode.indexOf(':')+1); - if (tmp.indexOf('_') != -1) - tmp = tmp.substring(0,tmp.indexOf('_')); - return Integer.parseInt(tmp); + protected int getReplicaPort(Replica replica) throws MalformedURLException { + URL replicaNode = new URL(replica.getCoreUrl()); + return replicaNode.getPort(); } protected JettySolrRunner getJettyOnPort(int port) { diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/MockZkStateReader.java b/solr/test-framework/src/java/org/apache/solr/cloud/MockZkStateReader.java index b0ba518f2bf7..dbfe02ad9cfb 100644 --- a/solr/test-framework/src/java/org/apache/solr/cloud/MockZkStateReader.java +++ b/solr/test-framework/src/java/org/apache/solr/cloud/MockZkStateReader.java @@ -16,6 +16,8 @@ */ package org.apache.solr.cloud; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import org.apache.solr.common.cloud.ClusterState; @@ -27,9 +29,22 @@ public class MockZkStateReader extends ZkStateReader { private Set collections; public MockZkStateReader(ClusterState clusterState, Set collections) { + this(clusterState, collections, null); + } + + public MockZkStateReader(ClusterState clusterState, Set collections, Map liveNodes) { super(new MockSolrZkClient()); this.clusterState = clusterState; this.collections = collections; + + if(liveNodes == null && clusterState != null) { + liveNodes = new HashMap(); + for(String n: clusterState.getLiveNodes()) { + liveNodes.put(n, n); + } + } + + this.liveNodes = liveNodes; } public Set getAllCollections(){