SOLR-17204: REPLACENODE supports the source node not being live (#2353)
The REPLACENODE command now supports the source not being live
Co-authored-by: Vincent Primault <vprimault@salesforce.com>
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 21f1155..386c7d8 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -130,6 +130,8 @@
* SOLR-17211: New SolrJ JDK client supports Async (James Dyer)
+* SOLR-17204: REPLACENODE now supports the source node not being live (Vincent Primault)
+
Optimizations
---------------------
* SOLR-17144: Close searcherExecutor thread per core after 1 minute (Pierre Salagnac, Christine Poerschke)
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/ReplaceNodeCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/ReplaceNodeCmd.java
index ee3324f..5134e99 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/ReplaceNodeCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/ReplaceNodeCmd.java
@@ -60,19 +60,19 @@
boolean parallel = message.getBool("parallel", false);
ClusterState clusterState = zkStateReader.getClusterState();
- if (!clusterState.liveNodesContain(source)) {
- throw new SolrException(
- SolrException.ErrorCode.BAD_REQUEST, "Source Node: " + source + " is not live");
- }
if (target != null && !clusterState.liveNodesContain(target)) {
throw new SolrException(
- SolrException.ErrorCode.BAD_REQUEST, "Target Node: " + target + " is not live");
+ SolrException.ErrorCode.BAD_REQUEST, "Target node: " + target + " is not live");
} else if (clusterState.getLiveNodes().size() <= 1) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
"No nodes other than the source node: "
+ source
+ " are live, therefore replicas cannot be moved");
+ } else if (source.equals(target)) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "Target node: " + target + " cannot be the same as source node");
}
List<Replica> sourceReplicas = ReplicaMigrationUtils.getReplicasOfNode(source, clusterState);
Map<Replica, String> replicaMovements = CollectionUtil.newHashMap(sourceReplicas.size());
diff --git a/solr/core/src/test/org/apache/solr/cloud/ReplaceNodeTest.java b/solr/core/src/test/org/apache/solr/cloud/ReplaceNodeTest.java
index 1f40b6c..6333d7d 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ReplaceNodeTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ReplaceNodeTest.java
@@ -312,6 +312,28 @@
() -> createReplaceNodeRequest(liveNode, null, null).process(cloudClient));
}
+ @Test
+ public void testFailIfSourceIsSameAsTarget() throws Exception {
+ configureCluster(2)
+ .addConfig(
+ "conf1", TEST_PATH().resolve("configsets").resolve("cloud-dynamic").resolve("conf"))
+ .configure();
+ String coll = "replacesourceissameastarget_coll";
+ if (log.isInfoEnabled()) {
+ log.info("total_jettys: {}", cluster.getJettySolrRunners().size());
+ }
+
+ CloudSolrClient cloudClient = cluster.getSolrClient();
+ cloudClient.request(CollectionAdminRequest.createCollection(coll, "conf1", 5, 1, 0, 0));
+
+ cluster.waitForActiveCollection(coll, 5, 5);
+
+ String liveNode = cloudClient.getClusterState().getLiveNodes().iterator().next();
+ expectThrows(
+ SolrException.class,
+ () -> createReplaceNodeRequest(liveNode, liveNode, null).process(cloudClient));
+ }
+
public static CollectionAdminRequest.AsyncCollectionAdminRequest createReplaceNodeRequest(
String sourceNode, String targetNode, Boolean parallel) {
if (random().nextBoolean()) {