This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_9x by this push:
     new cec23b880c5 SOLR-17006: Collections/AddReplica: Persist user-defined 
properties to state.json (#1973)
cec23b880c5 is described below

commit cec23b880c59b7edbf379025081b6301e843b631
Author: Vincent P <[email protected]>
AuthorDate: Fri Oct 20 14:54:15 2023 +0200

    SOLR-17006: Collections/AddReplica: Persist user-defined properties to 
state.json (#1973)
    
    User-defined properties are persisted to state.json so that any new replica 
will use them.  Adding a replica can also specify properties and they are saved 
to state.json and override that of the collection.
    
    ---------
    
    Co-authored-by: Vincent Primault <[email protected]>
---
 solr/CHANGES.txt                                   |  3 +
 .../solr/cloud/api/collections/AddReplicaCmd.java  | 14 +++--
 .../solr/cloud/overseer/ClusterStateMutator.java   |  7 +++
 .../apache/solr/cloud/overseer/SliceMutator.java   | 41 +++++++------
 .../test/org/apache/solr/cloud/AddReplicaTest.java | 34 +++++++++++
 .../cloud/CreateCollectionWithPropertiesTest.java  | 68 ++++++++++++++++++++++
 .../pages/collection-management.adoc               |  1 +
 7 files changed, 146 insertions(+), 22 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index ac0a9254310..dc86f0b22fd 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -10,6 +10,9 @@ New Features
 ---------------------
 (No changes)
 
+* SOLR-17006: Collection creation & adding replicas: User-defined properties 
are persisted to state.json and
+  applied to new replicas, available for use as property substitution in 
configuration files.  (Vincent Primault)
+
 Improvements
 ---------------------
 * SOLR-16924: RESTORECORE now sets the UpdateLog to ACTIVE state instead of 
requiring a separate
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/AddReplicaCmd.java 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/AddReplicaCmd.java
index f306fc3726c..42e4409f7f7 100644
--- 
a/solr/core/src/java/org/apache/solr/cloud/api/collections/AddReplicaCmd.java
+++ 
b/solr/core/src/java/org/apache/solr/cloud/api/collections/AddReplicaCmd.java
@@ -255,10 +255,10 @@ public class AddReplicaCmd implements 
CollApiCmds.CollectionApiCommand {
       CreateReplica createReplica)
       throws InterruptedException, KeeperException {
     if (!skipCreateReplicaInClusterState) {
-      ZkNodeProps props =
-          new ZkNodeProps(
+      Map<String, Object> replicaProps =
+          Utils.makeMap(
               Overseer.QUEUE_OPERATION,
-              ADDREPLICA.toLower(),
+              (Object) ADDREPLICA.toLower(),
               ZkStateReader.COLLECTION_PROP,
               collectionName,
               ZkStateReader.SHARD_ID_PROP,
@@ -274,8 +274,11 @@ public class AddReplicaCmd implements 
CollApiCmds.CollectionApiCommand {
               ZkStateReader.REPLICA_TYPE,
               createReplica.replicaType.name());
       if (createReplica.coreNodeName != null) {
-        props = props.plus(ZkStateReader.CORE_NODE_NAME_PROP, 
createReplica.coreNodeName);
+        replicaProps.put(ZkStateReader.CORE_NODE_NAME_PROP, 
createReplica.coreNodeName);
       }
+      CollectionHandlingUtils.addPropertyParams(message, replicaProps);
+
+      ZkNodeProps props = new ZkNodeProps(replicaProps);
       if (ccc.getDistributedClusterStateUpdater().isDistributedStateUpdate()) {
         ccc.getDistributedClusterStateUpdater()
             .doSingleStateUpdate(
@@ -342,6 +345,9 @@ public class AddReplicaCmd implements 
CollApiCmds.CollectionApiCommand {
     if (createReplica.coreNodeName != null) {
       params.set(CoreAdminParams.CORE_NODE_NAME, createReplica.coreNodeName);
     }
+    // Inherit user-defined properties from collection.
+    CollectionHandlingUtils.addPropertyParams(coll, params);
+    // Inherit user-defined properties from replica.
     CollectionHandlingUtils.addPropertyParams(message, params);
 
     return params;
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/overseer/ClusterStateMutator.java 
b/solr/core/src/java/org/apache/solr/cloud/overseer/ClusterStateMutator.java
index 2c625b2c5c4..11b3795745b 100644
--- a/solr/core/src/java/org/apache/solr/cloud/overseer/ClusterStateMutator.java
+++ b/solr/core/src/java/org/apache/solr/cloud/overseer/ClusterStateMutator.java
@@ -125,6 +125,13 @@ public class ClusterStateMutator {
       collectionProps.put(ZkStateReader.CONFIGNAME_PROP, configName);
     }
 
+    // add user-defined properties
+    for (String prop : message.keySet()) {
+      if (prop.startsWith(CollectionAdminParams.PROPERTY_PREFIX)) {
+        collectionProps.put(prop, message.get(prop));
+      }
+    }
+
     assert !collectionProps.containsKey(CollectionAdminParams.COLL_CONF);
     DocCollection newCollection =
         DocCollection.create(
diff --git 
a/solr/core/src/java/org/apache/solr/cloud/overseer/SliceMutator.java 
b/solr/core/src/java/org/apache/solr/cloud/overseer/SliceMutator.java
index c83a965d5a1..c016fa5489b 100644
--- a/solr/core/src/java/org/apache/solr/cloud/overseer/SliceMutator.java
+++ b/solr/core/src/java/org/apache/solr/cloud/overseer/SliceMutator.java
@@ -94,25 +94,30 @@ public class SliceMutator {
             cloudManager
                 .getClusterStateProvider()
                 .getClusterProperty(ZkStateReader.URL_SCHEME, "http"));
-    Replica replica =
-        new Replica(
-            coreNodeName,
-            Utils.makeMap(
-                ZkStateReader.CORE_NAME_PROP,
-                message.getStr(ZkStateReader.CORE_NAME_PROP),
-                ZkStateReader.STATE_PROP,
-                message.getStr(ZkStateReader.STATE_PROP),
-                ZkStateReader.NODE_NAME_PROP,
-                nodeName,
-                ZkStateReader.BASE_URL_PROP,
-                baseUrl,
-                ZkStateReader.FORCE_SET_STATE_PROP,
-                "false",
-                ZkStateReader.REPLICA_TYPE,
-                message.get(ZkStateReader.REPLICA_TYPE)),
-            coll,
-            slice);
 
+    Map<String, Object> replicaProps =
+        Utils.makeMap(
+            ZkStateReader.CORE_NAME_PROP,
+            message.getStr(ZkStateReader.CORE_NAME_PROP),
+            ZkStateReader.STATE_PROP,
+            message.getStr(ZkStateReader.STATE_PROP),
+            ZkStateReader.NODE_NAME_PROP,
+            nodeName,
+            ZkStateReader.BASE_URL_PROP,
+            baseUrl,
+            ZkStateReader.FORCE_SET_STATE_PROP,
+            "false",
+            ZkStateReader.REPLICA_TYPE,
+            message.get(ZkStateReader.REPLICA_TYPE));
+
+    // add user-defined properties
+    for (String prop : message.keySet()) {
+      if (prop.startsWith(CollectionAdminParams.PROPERTY_PREFIX)) {
+        replicaProps.put(prop, message.get(prop));
+      }
+    }
+
+    Replica replica = new Replica(coreNodeName, replicaProps, coll, slice);
     return new ZkWriteCommand(coll, updateReplica(collection, sl, 
replica.getName(), replica));
   }
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/AddReplicaTest.java 
b/solr/core/src/test/org/apache/solr/cloud/AddReplicaTest.java
index 7702012713d..fa2bdf84695 100644
--- a/solr/core/src/test/org/apache/solr/cloud/AddReplicaTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/AddReplicaTest.java
@@ -27,6 +27,7 @@ import 
org.apache.solr.client.solrj.response.RequestStatusState;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.DocCollection;
 import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.core.CoreDescriptor;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -187,4 +188,37 @@ public class AddReplicaTest extends SolrCloudTestCase {
       assertSame(coll + "\n" + replica, replica.getState(), 
Replica.State.ACTIVE);
     }
   }
+
+  @Test
+  public void testAddReplicaWithUserDefinedProperties() throws Exception {
+    // When creating a collection with user-defined properties
+    CloudSolrClient cloudClient = cluster.getSolrClient();
+    String collectionName = "testAddReplicaWithUserDefinedProperties";
+    CollectionAdminRequest.createCollection(collectionName, "conf1", 1, 1)
+        .withProperty("customProp1", "val1")
+        .withProperty("customProp2", "val2")
+        .process(cloudClient);
+    cluster.waitForActiveCollection(collectionName, 1, 1);
+
+    // When adding a replica to the collection with user-defined properties
+    CollectionAdminRequest.AddReplica addReplica =
+        CollectionAdminRequest.addReplicaToShard(collectionName, "shard1");
+    addReplica.withProperty("customProp2", "val2.1");
+    addReplica.withProperty("customProp3", "val3");
+    addReplica.setWaitForFinalState(true);
+    addReplica.process(cloudClient);
+
+    // Verify that the new core was created with user-defined properties 
coming from the request
+    // and inherited from the collection (the former taking precedence over 
the latter).
+    Replica replica =
+        
cloudClient.getClusterState().getCollection(collectionName).getReplicas().get(1);
+    CoreDescriptor coreDescriptor =
+        cluster
+            .getReplicaJetty(replica)
+            .getCoreContainer()
+            .getCoreDescriptor(replica.getCoreName());
+    assertEquals("val1", coreDescriptor.getCoreProperty("customProp1", ""));
+    assertEquals("val2.1", coreDescriptor.getCoreProperty("customProp2", ""));
+    assertEquals("val3", coreDescriptor.getCoreProperty("customProp3", ""));
+  }
 }
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/CreateCollectionWithPropertiesTest.java
 
b/solr/core/src/test/org/apache/solr/cloud/CreateCollectionWithPropertiesTest.java
new file mode 100644
index 00000000000..c90e9d21190
--- /dev/null
+++ 
b/solr/core/src/test/org/apache/solr/cloud/CreateCollectionWithPropertiesTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.core.CoreDescriptor;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class CreateCollectionWithPropertiesTest extends SolrCloudTestCase {
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    configureCluster(3)
+        .addConfig(
+            "conf1", 
TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+        .configure();
+  }
+
+  @Override
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();
+    cluster.deleteAllCollections();
+  }
+
+  @Test
+  public void testCreateCollectionWithUserDefinedProperties() throws Exception 
{
+    // When creating a collection with user-defined properties
+    CloudSolrClient cloudClient = cluster.getSolrClient();
+    String collectionName = "testCreateCollectionWithUserDefinedProperties";
+    CollectionAdminRequest.createCollection(collectionName, "conf1", 1, 1)
+        .withProperty("customProp1", "val1")
+        .process(cloudClient);
+    cluster.waitForActiveCollection(collectionName, 1, 1);
+
+    // Verify that user-defined properties are stored in cluster state
+    DocCollection collection = 
cloudClient.getClusterState().getCollection(collectionName);
+    assertEquals("val1", 
collection.getProperties().get("property.customProp1"));
+
+    // Verify that the core was created with user-defined properties
+    Replica replica =
+        
cloudClient.getClusterState().getCollection(collectionName).getReplicas().get(0);
+    CoreDescriptor coreDescriptor =
+        cluster
+            .getReplicaJetty(replica)
+            .getCoreContainer()
+            .getCoreDescriptor(replica.getCoreName());
+    assertEquals("val1", coreDescriptor.getCoreProperty("customProp1", ""));
+  }
+}
diff --git 
a/solr/solr-ref-guide/modules/deployment-guide/pages/collection-management.adoc 
b/solr/solr-ref-guide/modules/deployment-guide/pages/collection-management.adoc
index 107b4849209..63da9c86e80 100644
--- 
a/solr/solr-ref-guide/modules/deployment-guide/pages/collection-management.adoc
+++ 
b/solr/solr-ref-guide/modules/deployment-guide/pages/collection-management.adoc
@@ -250,6 +250,7 @@ If `true` the states of individual replicas will be 
maintained as individual chi
 +
 Set core property _name_ to _value_.
 See the section xref:configuration-guide:core-discovery.adoc[] for details on 
supported properties and values.
+Those properties are also applied to every new core that will be created when 
adding replicas to the collection later on.
 +
 [WARNING]
 ====

Reply via email to