This is an automated email from the ASF dual-hosted git repository. gerlowskija pushed a commit to branch branch_9x in repository https://gitbox.apache.org/repos/asf/solr.git
commit a0cd45394f3c99e4c3918d007e7b963819019c7b Author: Jason Gerlowski <[email protected]> AuthorDate: Thu Jun 1 09:09:58 2023 -0400 SOLR-16392: Tweak v2 CREATESHARD to be more REST-ful (#1671) The "create" command-specifier has been removed, but the API otherwise remains unchanged. This commit also converts the API over to the JAX-RS framework. --- solr/CHANGES.txt | 9 + .../solr/handler/admin/CollectionsHandler.java | 41 +--- .../solr/handler/admin/api/CreateShardAPI.java | 220 +++++++++++++++++---- .../solr/handler/admin/api/CreateShardAPITest.java | 172 ++++++++++++++++ .../handler/admin/api/V2ShardsAPIMappingTest.java | 41 ---- .../deployment-guide/pages/shard-management.adoc | 6 +- 6 files changed, 372 insertions(+), 117 deletions(-) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 7e8cd9cf1a5..1a99ef812b6 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -109,6 +109,15 @@ Improvements * SOLR-16687: Add support of SolrClassLoader to SolrZkClient (Lamine Idjeraoui via Jason Gerlowski & Houston Putman) +* SOLR-9378: Internal shard requests no longer include the wasteful shard.url param. [shard] transformer now defaults to returning + only the shard id (based on luceneMatchVersion), but can be configured to return the legacy list of replicas. (hossman) + +* SOLR-16816: Update node metrics while making affinityPlacement selections. Therefore selections can be made given the expected cluster + information after the previous selections are implemented. (Houston Putman) + +* SOLR-16392: The v2 "create shard" API has been tweaked to be more intuitive, by removing the top-level "create" + command specifier. The rest of the API remains unchanged. (Jason Gerlowski) + Optimizations --------------------- diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java index f222fa2e801..c1ca2a1df69 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java @@ -136,7 +136,6 @@ import org.apache.solr.client.solrj.impl.HttpSolrClient.Builder; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.CoreAdminRequest.RequestSyncShard; import org.apache.solr.client.solrj.response.RequestStatusState; -import org.apache.solr.client.solrj.util.SolrIdentifierValidator; import org.apache.solr.cloud.OverseerSolrResponse; import org.apache.solr.cloud.OverseerSolrResponseSerializer; import org.apache.solr.cloud.OverseerTaskQueue; @@ -152,8 +151,6 @@ import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.cloud.ClusterProperties; import org.apache.solr.common.cloud.ClusterState; import org.apache.solr.common.cloud.DocCollection; -import org.apache.solr.common.cloud.DocCollection.CollectionStateProps; -import org.apache.solr.common.cloud.ImplicitDocRouter; import org.apache.solr.common.cloud.Replica; import org.apache.solr.common.cloud.Replica.State; import org.apache.solr.common.cloud.Slice; @@ -768,40 +765,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission CREATESHARD_OP( CREATESHARD, (req, rsp, h) -> { - Map<String, Object> map = - copy(req.getParams().required(), null, COLLECTION_PROP, SHARD_ID_PROP); - ClusterState clusterState = h.coreContainer.getZkController().getClusterState(); - final String newShardName = - SolrIdentifierValidator.validateShardName(req.getParams().get(SHARD_ID_PROP)); - boolean followAliases = req.getParams().getBool(FOLLOW_ALIASES, false); - String extCollectionName = req.getParams().get(COLLECTION_PROP); - String collectionName = - followAliases - ? h.coreContainer - .getZkController() - .getZkStateReader() - .getAliases() - .resolveSimpleAlias(extCollectionName) - : extCollectionName; - if (!ImplicitDocRouter.NAME.equals( - ((Map<?, ?>) - clusterState - .getCollection(collectionName) - .get(CollectionStateProps.DOC_ROUTER)) - .get(NAME))) - throw new SolrException( - ErrorCode.BAD_REQUEST, "shards can be added only to 'implicit' collections"); - copy( - req.getParams(), - map, - REPLICATION_FACTOR, - NRT_REPLICAS, - TLOG_REPLICAS, - PULL_REPLICAS, - CREATE_NODE_SET, - WAIT_FOR_FINAL_STATE, - FOLLOW_ALIASES); - return copyPropertiesWithPrefix(req.getParams(), map, PROPERTY_PREFIX); + CreateShardAPI.invokeFromV1Params(h.coreContainer, req, rsp); + return null; }), DELETEREPLICA_OP( DELETEREPLICA, @@ -1558,6 +1523,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission CreateAliasAPI.class, CreateCollectionAPI.class, CreateCollectionBackupAPI.class, + CreateShardAPI.class, DeleteAliasAPI.class, DeleteCollectionBackupAPI.class, DeleteCollectionAPI.class, @@ -1582,7 +1548,6 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission public Collection<Api> getApis() { final List<Api> apis = new ArrayList<>(); apis.addAll(AnnotatedApi.getApis(new SplitShardAPI(this))); - apis.addAll(AnnotatedApi.getApis(new CreateShardAPI(this))); apis.addAll(AnnotatedApi.getApis(new AddReplicaAPI(this))); apis.addAll(AnnotatedApi.getApis(new SyncShardAPI(this))); apis.addAll(AnnotatedApi.getApis(new ForceLeaderAPI(this))); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java index 080068c4d69..3f39404f45a 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/api/CreateShardAPI.java @@ -17,65 +17,215 @@ package org.apache.solr.handler.admin.api; -import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST; +import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2; +import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION; +import static org.apache.solr.cloud.api.collections.CollectionHandlingUtils.CREATE_NODE_SET; +import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS; +import static org.apache.solr.common.cloud.ZkStateReader.PULL_REPLICAS; +import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR; +import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS; import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION; import static org.apache.solr.common.params.CollectionAdminParams.CREATE_NODE_SET_PARAM; +import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES; import static org.apache.solr.common.params.CollectionAdminParams.PROPERTY_PREFIX; -import static org.apache.solr.common.params.CommonParams.ACTION; -import static org.apache.solr.handler.ClusterAPI.wrapParams; -import static org.apache.solr.handler.api.V2ApiUtils.flattenMapWithPrefix; +import static org.apache.solr.common.params.CollectionAdminParams.SHARD; +import static org.apache.solr.common.params.CommonAdminParams.ASYNC; +import static org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STATE; +import static org.apache.solr.common.params.CommonParams.NAME; +import static org.apache.solr.handler.admin.api.CreateCollectionAPI.copyPrefixedPropertiesWithoutPrefix; import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.solr.api.Command; -import org.apache.solr.api.EndPoint; -import org.apache.solr.api.PayloadObj; -import org.apache.solr.client.solrj.request.beans.CreateShardPayload; +import javax.inject.Inject; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import org.apache.solr.client.solrj.util.SolrIdentifierValidator; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.cloud.ClusterState; +import org.apache.solr.common.cloud.DocCollection; +import org.apache.solr.common.cloud.ImplicitDocRouter; +import org.apache.solr.common.cloud.ZkNodeProps; import org.apache.solr.common.params.CollectionParams; -import org.apache.solr.handler.admin.CollectionsHandler; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.handler.api.V2ApiUtils; +import org.apache.solr.jersey.JacksonReflectMapWriter; +import org.apache.solr.jersey.PermissionName; +import org.apache.solr.jersey.SubResponseAccumulatingJerseyResponse; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; /** * V2 API for creating a new shard in a collection. * - * <p>This API (POST /v2/collections/collectionName/shards {'create': {...}}) is analogous to the v1 + * <p>This API (POST /v2/collections/collectionName/shards {...}) is analogous to the v1 * /admin/collections?action=CREATESHARD command. - * - * @see CreateShardAPI */ -@EndPoint( - path = {"/c/{collection}/shards", "/collections/{collection}/shards"}, - method = POST, - permission = COLL_EDIT_PERM) -public class CreateShardAPI { - private static final String V2_CREATE_CMD = "create"; +@Path("/collections/{collectionName}/shards") +public class CreateShardAPI extends AdminAPIBase { + + @Inject + public CreateShardAPI( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) { + super(coreContainer, solrQueryRequest, solrQueryResponse); + } - private final CollectionsHandler collectionsHandler; + @POST + @Produces({"application/json", "application/xml", BINARY_CONTENT_TYPE_V2}) + @PermissionName(COLL_EDIT_PERM) + public SubResponseAccumulatingJerseyResponse createShard( + @PathParam("collectionName") String collectionName, CreateShardRequestBody requestBody) + throws Exception { + final var response = instantiateJerseyResponse(SubResponseAccumulatingJerseyResponse.class); + if (requestBody == null) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, "Required request-body is missing"); + } + ensureRequiredParameterProvided(COLLECTION_PROP, collectionName); + ensureRequiredParameterProvided(SHARD_ID_PROP, requestBody.shardName); + SolrIdentifierValidator.validateShardName(requestBody.shardName); + final String resolvedCollectionName = + resolveAndValidateAliasIfEnabled( + collectionName, Boolean.TRUE.equals(requestBody.followAliases)); + ensureCollectionUsesImplicitRouter(resolvedCollectionName); - public CreateShardAPI(CollectionsHandler collectionsHandler) { - this.collectionsHandler = collectionsHandler; + final ZkNodeProps remoteMessage = createRemoteMessage(resolvedCollectionName, requestBody); + submitRemoteMessageAndHandleResponse( + response, + CollectionParams.CollectionAction.CREATESHARD, + remoteMessage, + requestBody.asyncId); + return response; } - @Command(name = V2_CREATE_CMD) - public void createShard(PayloadObj<CreateShardPayload> obj) throws Exception { - final CreateShardPayload v2Body = obj.get(); - final Map<String, Object> v1Params = v2Body.toMap(new HashMap<>()); - v1Params.put(ACTION, CollectionParams.CollectionAction.CREATESHARD.toLower()); - v1Params.put(COLLECTION, obj.getRequest().getPathTemplateValues().get(COLLECTION)); + public static class CreateShardRequestBody implements JacksonReflectMapWriter { + @JsonProperty(NAME) + public String shardName; + + @JsonProperty(REPLICATION_FACTOR) + public Integer replicationFactor; + + @JsonProperty(NRT_REPLICAS) + public Integer nrtReplicas; + + @JsonProperty(TLOG_REPLICAS) + public Integer tlogReplicas; + + @JsonProperty(PULL_REPLICAS) + public Integer pullReplicas; + + @JsonProperty("createReplicas") + public Boolean createReplicas; + + @JsonProperty("nodeSet") + public List<String> nodeSet; + + @JsonProperty(WAIT_FOR_FINAL_STATE) + public Boolean waitForFinalState; - if (v2Body.nodeSet != null) { - v1Params.put(CREATE_NODE_SET_PARAM, buildV1CreateNodeSetValue(v2Body.nodeSet)); + @JsonProperty(FOLLOW_ALIASES) + public Boolean followAliases; + + @JsonProperty(ASYNC) + public String asyncId; + + @JsonProperty public Map<String, String> properties; + + public static CreateShardRequestBody fromV1Params(SolrParams params) { + params.required().check(COLLECTION, SHARD); + + final var requestBody = new CreateShardRequestBody(); + requestBody.shardName = params.get(SHARD); + requestBody.replicationFactor = params.getInt(REPLICATION_FACTOR); + requestBody.nrtReplicas = params.getInt(NRT_REPLICAS); + requestBody.tlogReplicas = params.getInt(TLOG_REPLICAS); + requestBody.pullReplicas = params.getInt(PULL_REPLICAS); + if (params.get(CREATE_NODE_SET_PARAM) != null) { + final String nodeSetStr = params.get(CREATE_NODE_SET_PARAM); + if ("EMPTY".equals(nodeSetStr)) { + requestBody.createReplicas = false; + } else { + requestBody.nodeSet = Arrays.asList(nodeSetStr.split(",")); + } + } + requestBody.waitForFinalState = params.getBool(WAIT_FOR_FINAL_STATE); + requestBody.followAliases = params.getBool(FOLLOW_ALIASES); + requestBody.asyncId = params.get(ASYNC); + requestBody.properties = + copyPrefixedPropertiesWithoutPrefix(params, new HashMap<>(), PROPERTY_PREFIX); + + return requestBody; } + } - flattenMapWithPrefix(v2Body.coreProperties, v1Params, PROPERTY_PREFIX); - collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), v1Params), obj.getResponse()); + public static void invokeFromV1Params( + CoreContainer coreContainer, + SolrQueryRequest solrQueryRequest, + SolrQueryResponse solrQueryResponse) + throws Exception { + final var requestBody = CreateShardRequestBody.fromV1Params(solrQueryRequest.getParams()); + final var createShardApi = + new CreateShardAPI(coreContainer, solrQueryRequest, solrQueryResponse); + final var response = + createShardApi.createShard(solrQueryRequest.getParams().get(COLLECTION), requestBody); + V2ApiUtils.squashIntoSolrResponseWithoutHeader(solrQueryResponse, response); } - private String buildV1CreateNodeSetValue(List<String> nodeSet) { - if (nodeSet.size() > 0) { - return String.join(",", nodeSet); + public static ZkNodeProps createRemoteMessage( + String collectionName, CreateShardRequestBody requestBody) { + final Map<String, Object> remoteMessage = new HashMap<>(); + remoteMessage.put(QUEUE_OPERATION, CollectionParams.CollectionAction.CREATESHARD.toLower()); + remoteMessage.put(COLLECTION_PROP, collectionName); + remoteMessage.put(SHARD_ID_PROP, requestBody.shardName); + if (requestBody.createReplicas == null || requestBody.createReplicas) { + // The remote message expects a single comma-delimited string, so nodeSet requires flattening + if (requestBody.nodeSet != null) { + remoteMessage.put(CREATE_NODE_SET_PARAM, String.join(",", requestBody.nodeSet)); + } + } else { + remoteMessage.put(CREATE_NODE_SET, "EMPTY"); } - return "EMPTY"; + insertIfNotNull(remoteMessage, REPLICATION_FACTOR, requestBody.replicationFactor); + insertIfNotNull(remoteMessage, NRT_REPLICAS, requestBody.nrtReplicas); + insertIfNotNull(remoteMessage, TLOG_REPLICAS, requestBody.tlogReplicas); + insertIfNotNull(remoteMessage, PULL_REPLICAS, requestBody.pullReplicas); + insertIfNotNull(remoteMessage, WAIT_FOR_FINAL_STATE, requestBody.waitForFinalState); + insertIfNotNull(remoteMessage, FOLLOW_ALIASES, requestBody.followAliases); + insertIfNotNull(remoteMessage, ASYNC, requestBody.asyncId); + + if (requestBody.properties != null) { + requestBody + .properties + .entrySet() + .forEach( + entry -> { + remoteMessage.put(PROPERTY_PREFIX + entry.getKey(), entry.getValue()); + }); + } + + return new ZkNodeProps(remoteMessage); + } + + private void ensureCollectionUsesImplicitRouter(String collectionName) { + final ClusterState clusterState = coreContainer.getZkController().getClusterState(); + if (!ImplicitDocRouter.NAME.equals( + ((Map<?, ?>) + clusterState + .getCollection(collectionName) + .get(DocCollection.CollectionStateProps.DOC_ROUTER)) + .get(NAME))) + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "shards can be added only to 'implicit' collections"); } } diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/CreateShardAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateShardAPITest.java new file mode 100644 index 00000000000..07b71d8a805 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/CreateShardAPITest.java @@ -0,0 +1,172 @@ +/* + * 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.handler.admin.api; + +import static org.apache.solr.cloud.Overseer.QUEUE_OPERATION; +import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS; +import static org.apache.solr.common.cloud.ZkStateReader.PULL_REPLICAS; +import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR; +import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP; +import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS; +import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION; +import static org.apache.solr.common.params.CollectionAdminParams.CREATE_NODE_SET_PARAM; +import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES; +import static org.apache.solr.common.params.CommonAdminParams.ASYNC; +import static org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STATE; +import static org.hamcrest.Matchers.containsString; + +import java.util.List; +import java.util.Map; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.hamcrest.MatcherAssert; +import org.junit.Test; + +/** Unit tests for {@link CreateShardAPI} */ +public class CreateShardAPITest extends SolrTestCaseJ4 { + + @Test + public void testReportsErrorIfRequestBodyMissing() { + final SolrException thrown = + expectThrows( + SolrException.class, + () -> { + final var api = new CreateShardAPI(null, null, null); + api.createShard("someCollName", null); + }); + + assertEquals(400, thrown.code()); + assertEquals("Required request-body is missing", thrown.getMessage()); + } + + @Test + public void testReportsErrorIfCollectionNameMissing() { + final var requestBody = new CreateShardAPI.CreateShardRequestBody(); + requestBody.shardName = "someShardName"; + final SolrException thrown = + expectThrows( + SolrException.class, + () -> { + final var api = new CreateShardAPI(null, null, null); + api.createShard(null, requestBody); + }); + + assertEquals(400, thrown.code()); + assertEquals("Missing required parameter: collection", thrown.getMessage()); + } + + @Test + public void testReportsErrorIfShardNameMissing() { + final var requestBody = new CreateShardAPI.CreateShardRequestBody(); + requestBody.shardName = null; + final SolrException thrown = + expectThrows( + SolrException.class, + () -> { + final var api = new CreateShardAPI(null, null, null); + api.createShard("someCollectionName", requestBody); + }); + + assertEquals(400, thrown.code()); + assertEquals("Missing required parameter: shard", thrown.getMessage()); + } + + @Test + public void testReportsErrorIfShardNameIsInvalid() { + final var requestBody = new CreateShardAPI.CreateShardRequestBody(); + requestBody.shardName = "invalid$shard@name"; + final SolrException thrown = + expectThrows( + SolrException.class, + () -> { + final var api = new CreateShardAPI(null, null, null); + api.createShard("someCollectionName", requestBody); + }); + + assertEquals(400, thrown.code()); + MatcherAssert.assertThat( + thrown.getMessage(), containsString("Invalid shard: [invalid$shard@name]")); + } + + @Test + public void testCreateRemoteMessageAllProperties() { + final var requestBody = new CreateShardAPI.CreateShardRequestBody(); + requestBody.shardName = "someShardName"; + requestBody.replicationFactor = 123; + requestBody.nrtReplicas = 123; + requestBody.tlogReplicas = 456; + requestBody.pullReplicas = 789; + requestBody.createReplicas = true; + requestBody.nodeSet = List.of("node1", "node2"); + requestBody.waitForFinalState = true; + requestBody.followAliases = true; + requestBody.asyncId = "someAsyncId"; + requestBody.properties = Map.of("propName1", "propVal1", "propName2", "propVal2"); + + final var remoteMessage = + CreateShardAPI.createRemoteMessage("someCollectionName", requestBody).getProperties(); + + assertEquals(13, remoteMessage.size()); + assertEquals("createshard", remoteMessage.get(QUEUE_OPERATION)); + assertEquals("someCollectionName", remoteMessage.get(COLLECTION)); + assertEquals("someShardName", remoteMessage.get(SHARD_ID_PROP)); + assertEquals(123, remoteMessage.get(REPLICATION_FACTOR)); + assertEquals(123, remoteMessage.get(NRT_REPLICAS)); + assertEquals(456, remoteMessage.get(TLOG_REPLICAS)); + assertEquals(789, remoteMessage.get(PULL_REPLICAS)); + assertEquals("node1,node2", remoteMessage.get(CREATE_NODE_SET_PARAM)); + assertEquals(true, remoteMessage.get(WAIT_FOR_FINAL_STATE)); + assertEquals(true, remoteMessage.get(FOLLOW_ALIASES)); + assertEquals("someAsyncId", remoteMessage.get(ASYNC)); + assertEquals("propVal1", remoteMessage.get("property.propName1")); + assertEquals("propVal2", remoteMessage.get("property.propName2")); + } + + @Test + public void testCanConvertV1ParamsToV2RequestBody() { + final var v1Params = new ModifiableSolrParams(); + v1Params.add(COLLECTION, "someCollectionName"); + v1Params.add(SHARD_ID_PROP, "someShardName"); + v1Params.set(REPLICATION_FACTOR, 123); + v1Params.set(NRT_REPLICAS, 123); + v1Params.set(TLOG_REPLICAS, 456); + v1Params.set(PULL_REPLICAS, 789); + v1Params.add(CREATE_NODE_SET_PARAM, "node1,node2"); + v1Params.set(WAIT_FOR_FINAL_STATE, true); + v1Params.set(FOLLOW_ALIASES, true); + v1Params.add(ASYNC, "someAsyncId"); + v1Params.add("property.propName1", "propVal1"); + v1Params.add("property.propName2", "propVal2"); + + final var requestBody = CreateShardAPI.CreateShardRequestBody.fromV1Params(v1Params); + + assertEquals("someShardName", requestBody.shardName); + assertEquals(Integer.valueOf(123), requestBody.replicationFactor); + assertEquals(Integer.valueOf(123), requestBody.nrtReplicas); + assertEquals(Integer.valueOf(456), requestBody.tlogReplicas); + assertEquals(Integer.valueOf(789), requestBody.pullReplicas); + assertNull(requestBody.createReplicas); + assertEquals(List.of("node1", "node2"), requestBody.nodeSet); + assertEquals(Boolean.TRUE, requestBody.waitForFinalState); + assertEquals(Boolean.TRUE, requestBody.followAliases); + assertEquals("someAsyncId", requestBody.asyncId); + assertEquals("propVal1", requestBody.properties.get("propName1")); + assertEquals("propVal2", requestBody.properties.get("propName2")); + } +} diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java index af4fda1cd43..3d8d1448329 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/api/V2ShardsAPIMappingTest.java @@ -17,11 +17,7 @@ package org.apache.solr.handler.admin.api; -import static org.apache.solr.common.cloud.ZkStateReader.NRT_REPLICAS; -import static org.apache.solr.common.cloud.ZkStateReader.PULL_REPLICAS; -import static org.apache.solr.common.cloud.ZkStateReader.REPLICATION_FACTOR; import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP; -import static org.apache.solr.common.cloud.ZkStateReader.TLOG_REPLICAS; import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION; import static org.apache.solr.common.params.CollectionAdminParams.CREATE_NODE_SET_PARAM; import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES; @@ -59,7 +55,6 @@ public class V2ShardsAPIMappingTest extends V2ApiMappingTest<CollectionsHandler> public void populateApiBag() { final CollectionsHandler collectionsHandler = getRequestHandler(); apiBag.registerObject(new SplitShardAPI(collectionsHandler)); - apiBag.registerObject(new CreateShardAPI(collectionsHandler)); apiBag.registerObject(new AddReplicaAPI(collectionsHandler)); apiBag.registerObject(new SyncShardAPI(collectionsHandler)); apiBag.registerObject(new ForceLeaderAPI(collectionsHandler)); @@ -137,42 +132,6 @@ public class V2ShardsAPIMappingTest extends V2ApiMappingTest<CollectionsHandler> assertEquals("bar1", v1Params.get("property.bar")); } - @Test - public void testCreateShardAllProperties() throws Exception { - final SolrParams v1Params = - captureConvertedV1Params( - "/collections/collName/shards", - "POST", - "{ 'create': {" - + "'shard': 'shard1', " - + "'nodeSet': ['foo', 'bar', 'baz'], " - + "'followAliases': true, " - + "'async': 'some_async_id', " - + "'waitForFinalState': true, " - + "'replicationFactor': 123, " - + "'nrtReplicas': 456, " - + "'tlogReplicas': 789, " - + "'pullReplicas': 101, " - + "'coreProperties': {" - + " 'foo': 'foo1', " - + " 'bar': 'bar1', " - + "}}}"); - - assertEquals(CollectionParams.CollectionAction.CREATESHARD.lowerName, v1Params.get(ACTION)); - assertEquals("collName", v1Params.get(COLLECTION)); - assertEquals("shard1", v1Params.get(SHARD_ID_PROP)); - assertEquals("foo,bar,baz", v1Params.get(CREATE_NODE_SET_PARAM)); - assertTrue(v1Params.getPrimitiveBool(FOLLOW_ALIASES)); - assertEquals("some_async_id", v1Params.get(ASYNC)); - assertTrue(v1Params.getPrimitiveBool(WAIT_FOR_FINAL_STATE)); - assertEquals(123, v1Params.getPrimitiveInt(REPLICATION_FACTOR)); - assertEquals(456, v1Params.getPrimitiveInt(NRT_REPLICAS)); - assertEquals(789, v1Params.getPrimitiveInt(TLOG_REPLICAS)); - assertEquals(101, v1Params.getPrimitiveInt(PULL_REPLICAS)); - assertEquals("foo1", v1Params.get("property.foo")); - assertEquals("bar1", v1Params.get("property.bar")); - } - @Test public void testAddReplicaAllProperties() throws Exception { final SolrParams v1Params = diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/shard-management.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/shard-management.adoc index a691743c1e5..c8548b573b9 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/shard-management.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/shard-management.adoc @@ -324,9 +324,7 @@ http://localhost:8983/solr/admin/collections?action=CREATESHARD&shard=newShardNa ---- curl -X POST http://localhost:8983/api/collections/techproducts/shards -H 'Content-Type: application/json' -d ' { - "create":{ - "shard":"newShardName" - } + "shard":"newShardName" } ' ---- @@ -358,6 +356,8 @@ s|Required |Default: none + The name of the collection that includes the shard to be split. +Provided as a query parameter in v1 requests, and as a path parameter for v2 requests. + `shard`:: +
