You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ge...@apache.org on 2023/06/06 19:47:42 UTC
[solr] branch main updated: SOLR-16398: Tweak 4 v2 collection APIs to be more REST-ful (#1683)
This is an automated email from the ASF dual-hosted git repository.
gerlowskija pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new f73f6020aa3 SOLR-16398: Tweak 4 v2 collection APIs to be more REST-ful (#1683)
f73f6020aa3 is described below
commit f73f6020aa3e9fa506fb1329cdd235428b4cf620
Author: Jason Gerlowski <ge...@apache.org>
AuthorDate: Tue Jun 6 15:47:35 2023 -0400
SOLR-16398: Tweak 4 v2 collection APIs to be more REST-ful (#1683)
This commit tweaks several "command"-based v2 APIs to be more REST-ful, namely reload-collection, sync-shard, rename-collection, and force-shard-leader. The v2 APIs for this functionality is now:
- POST /api/collections/cName/reload {...}
- POST /api/collections/cName/shards/sName/sync {...}
- POST /api/collections/cName/rename {...}
- POST /api/collections/cName/shards/sName/force-leader {...}
This commit also converts these APIs to the JAX-RS framework.
---
solr/CHANGES.txt | 5 +
...istributedCollectionConfigSetCommandRunner.java | 7 +-
.../solr/handler/admin/CollectionsHandler.java | 133 +--------------
.../solr/handler/admin/api/ForceLeaderAPI.java | 184 +++++++++++++++++----
.../handler/admin/api/ReloadCollectionAPI.java | 108 ++++++++----
.../handler/admin/api/RenameCollectionAPI.java | 141 +++++++++-------
.../solr/handler/admin/api/SyncShardAPI.java | 117 +++++++++----
.../solr/handler/admin/TestApiFramework.java | 9 -
.../solr/handler/admin/TestCollectionAPIs.java | 3 -
.../solr/handler/admin/api/ForceLeaderAPITest.java | 53 ++++++
.../handler/admin/api/ReloadCollectionAPITest.java | 57 +++++++
.../solr/handler/admin/api/SyncShardAPITest.java | 53 ++++++
.../admin/api/V2CollectionAPIMappingTest.java | 30 ----
.../handler/admin/api/V2ShardsAPIMappingTest.java | 25 ---
.../solr/util/tracing/TestDistributedTracing.java | 6 +-
.../pages/collection-management.adoc | 27 +--
.../deployment-guide/pages/shard-management.adoc | 6 +-
.../solrj/request/beans/ForceLeaderPayload.java | 25 ---
.../request/beans/ReloadCollectionPayload.java | 24 ---
.../request/beans/RenameCollectionPayload.java | 29 ----
.../solrj/request/beans/SyncShardPayload.java | 23 ---
21 files changed, 583 insertions(+), 482 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 986f342884d..a01ab171ed8 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -154,6 +154,11 @@ Improvements
changing the path. The v2 functionality can now be accessed at: `POST /api/collections/cName/shards/sName/replicas {...}`
(Jason Gerlowski)
+* SOLR-16398: Several v2 "command" APIs have been tweaked to be more intuitive. "FORCELEADER" is now available at
+ `POST /api/collections/cName/shards/sName/force-leader`, "RELOAD" is now available at `POST /api/collections/cName/reload`,
+ "SYNCSHARD" is now available at `POST /api/collections/cName/shards/sName/sync-shard`, and "RENAME" is now available at
+ `POST /api/collections/cName/rename`. (Jason Gerlowski)
+
Optimizations
---------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionConfigSetCommandRunner.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionConfigSetCommandRunner.java
index 9c7697caef2..726d13812bf 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionConfigSetCommandRunner.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/DistributedCollectionConfigSetCommandRunner.java
@@ -275,10 +275,9 @@ public class DistributedCollectionConfigSetCommandRunner {
// Happens either in the CollectionCommandRunner below or in the catch when the runner would not
// execute.
if (!asyncTaskTracker.createNewAsyncJobTracker(asyncId)) {
- NamedList<Object> resp = new NamedList<>();
- resp.add("error", "Task with the same requestid already exists. (" + asyncId + ")");
- resp.add(CoreAdminParams.REQUESTID, asyncId);
- return new OverseerSolrResponse(resp);
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "Task with the same requestid already exists. (" + asyncId + ")");
}
CollectionCommandRunner commandRunner = new CollectionCommandRunner(message, action, asyncId);
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 86f7ef3e484..e593693eee5 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
@@ -121,15 +121,11 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
import org.apache.solr.api.AnnotatedApi;
import org.apache.solr.api.Api;
import org.apache.solr.api.JerseyResource;
-import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrResponse;
-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.cloud.OverseerSolrResponse;
import org.apache.solr.cloud.OverseerSolrResponseSerializer;
@@ -137,20 +133,17 @@ import org.apache.solr.cloud.OverseerTaskQueue;
import org.apache.solr.cloud.OverseerTaskQueue.QueueEvent;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.cloud.ZkController.NotInClusterStateException;
-import org.apache.solr.cloud.ZkShardTerms;
import org.apache.solr.cloud.api.collections.DistributedCollectionConfigSetCommandRunner;
import org.apache.solr.cloud.api.collections.ReindexCollectionCmd;
import org.apache.solr.cloud.overseer.SliceMutator;
import org.apache.solr.common.SolrException;
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.Replica;
import org.apache.solr.common.cloud.Replica.State;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.SolrZkClient;
-import org.apache.solr.common.cloud.ZkCoreNodeProps;
import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionAdminParams;
@@ -566,8 +559,8 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
RELOAD_OP(
RELOAD,
(req, rsp, h) -> {
- Map<String, Object> map = copy(req.getParams().required(), null, NAME);
- return copy(req.getParams(), map);
+ ReloadCollectionAPI.invokeFromV1Params(h.coreContainer, req, rsp);
+ return null;
}),
RENAME_OP(
@@ -613,27 +606,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
SYNCSHARD_OP(
SYNCSHARD,
(req, rsp, h) -> {
- String extCollection = req.getParams().required().get("collection");
- String collection = h.coreContainer.getAliases().resolveSimpleAlias(extCollection);
- String shard = req.getParams().required().get("shard");
-
- ClusterState clusterState = h.coreContainer.getZkController().getClusterState();
-
- DocCollection docCollection = clusterState.getCollection(collection);
- ZkNodeProps leaderProps = docCollection.getLeader(shard);
- ZkCoreNodeProps nodeProps = new ZkCoreNodeProps(leaderProps);
-
- try (SolrClient client =
- new Builder(nodeProps.getBaseUrl())
- .withConnectionTimeout(15000, TimeUnit.MILLISECONDS)
- .withSocketTimeout(60000, TimeUnit.MILLISECONDS)
- .build()) {
- RequestSyncShard reqSyncShard = new RequestSyncShard();
- reqSyncShard.setCollection(collection);
- reqSyncShard.setShard(shard);
- reqSyncShard.setCoreName(nodeProps.getCoreName());
- client.request(reqSyncShard);
- }
+ SyncShardAPI.invokeFromV1Params(h.coreContainer, req, rsp);
return null;
}),
@@ -749,7 +722,7 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
FORCELEADER_OP(
FORCELEADER,
(req, rsp, h) -> {
- forceLeaderElection(req, h);
+ ForceLeaderAPI.invokeFromV1Params(h.coreContainer, req, rsp);
return null;
}),
CREATESHARD_OP(
@@ -1313,96 +1286,6 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
}
}
- private static void forceLeaderElection(SolrQueryRequest req, CollectionsHandler handler) {
- ZkController zkController = handler.coreContainer.getZkController();
- ClusterState clusterState = zkController.getClusterState();
- String extCollectionName = req.getParams().required().get(COLLECTION_PROP);
- String collectionName =
- zkController.zkStateReader.getAliases().resolveSimpleAlias(extCollectionName);
- String sliceId = req.getParams().required().get(SHARD_ID_PROP);
-
- log.info("Force leader invoked, state: {}", clusterState);
- DocCollection collection = clusterState.getCollection(collectionName);
- Slice slice = collection.getSlice(sliceId);
- if (slice == null) {
- throw new SolrException(
- ErrorCode.BAD_REQUEST,
- "No shard with name " + sliceId + " exists for collection " + collectionName);
- }
-
- try (ZkShardTerms zkShardTerms =
- new ZkShardTerms(collectionName, slice.getName(), zkController.getZkClient())) {
- // if an active replica is the leader, then all is fine already
- Replica leader = slice.getLeader();
- if (leader != null && leader.getState() == State.ACTIVE) {
- throw new SolrException(
- ErrorCode.SERVER_ERROR,
- "The shard already has an active leader. Force leader is not applicable. State: "
- + slice);
- }
-
- final Set<String> liveNodes = clusterState.getLiveNodes();
- List<Replica> liveReplicas =
- slice.getReplicas().stream()
- .filter(rep -> liveNodes.contains(rep.getNodeName()))
- .collect(Collectors.toList());
- boolean shouldIncreaseReplicaTerms =
- liveReplicas.stream()
- .noneMatch(
- rep ->
- zkShardTerms.registered(rep.getName())
- && zkShardTerms.canBecomeLeader(rep.getName()));
- // we won't increase replica's terms if exist a live replica with term equals to leader
- if (shouldIncreaseReplicaTerms) {
- // TODO only increase terms of replicas less out-of-sync
- liveReplicas.stream()
- .filter(rep -> zkShardTerms.registered(rep.getName()))
- // TODO should this all be done at once instead of increasing each replica individually?
- .forEach(rep -> zkShardTerms.setTermEqualsToLeader(rep.getName()));
- }
-
- // Wait till we have an active leader
- boolean success = false;
- for (int i = 0; i < 9; i++) {
- Thread.sleep(5000);
- clusterState = handler.coreContainer.getZkController().getClusterState();
- collection = clusterState.getCollection(collectionName);
- slice = collection.getSlice(sliceId);
- if (slice.getLeader() != null && slice.getLeader().getState() == State.ACTIVE) {
- success = true;
- break;
- }
- log.warn(
- "Force leader attempt {}. Waiting 5 secs for an active leader. State of the slice: {}",
- (i + 1),
- slice); // nowarn
- }
-
- if (success) {
- log.info(
- "Successfully issued FORCELEADER command for collection: {}, shard: {}",
- collectionName,
- sliceId);
- } else {
- log.info(
- "Couldn't successfully force leader, collection: {}, shard: {}. Cluster state: {}",
- collectionName,
- sliceId,
- clusterState);
- }
- } catch (SolrException e) {
- throw e;
- } catch (Exception e) {
- throw new SolrException(
- ErrorCode.SERVER_ERROR,
- "Error executing FORCELEADER operation for collection: "
- + collectionName
- + " shard: "
- + sliceId,
- e);
- }
- }
-
public static void waitForActiveCollection(
String collectionName, CoreContainer cc, SolrResponse createCollResponse)
throws KeeperException, InterruptedException {
@@ -1511,11 +1394,15 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
DeleteReplicaAPI.class,
DeleteReplicaPropertyAPI.class,
DeleteShardAPI.class,
+ ForceLeaderAPI.class,
InstallShardDataAPI.class,
ListCollectionsAPI.class,
ListCollectionBackupsAPI.class,
+ ReloadCollectionAPI.class,
+ RenameCollectionAPI.class,
ReplaceNodeAPI.class,
RestoreCollectionAPI.class,
+ SyncShardAPI.class,
CollectionPropertyAPI.class,
DeleteNodeAPI.class,
ListAliasesAPI.class,
@@ -1529,16 +1416,12 @@ 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 SyncShardAPI(this)));
- apis.addAll(AnnotatedApi.getApis(new ForceLeaderAPI(this)));
apis.addAll(AnnotatedApi.getApis(new BalanceShardUniqueAPI(this)));
apis.addAll(AnnotatedApi.getApis(new MigrateDocsAPI(this)));
apis.addAll(AnnotatedApi.getApis(new ModifyCollectionAPI(this)));
apis.addAll(AnnotatedApi.getApis(new MoveReplicaAPI(this)));
apis.addAll(AnnotatedApi.getApis(new RebalanceLeadersAPI(this)));
- apis.addAll(AnnotatedApi.getApis(new ReloadCollectionAPI(this)));
apis.addAll(AnnotatedApi.getApis(new CollectionStatusAPI(this)));
- apis.addAll(AnnotatedApi.getApis(new RenameCollectionAPI(this)));
return apis;
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/ForceLeaderAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/ForceLeaderAPI.java
index c8bf6104770..fd32b52a1f7 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/ForceLeaderAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/ForceLeaderAPI.java
@@ -17,52 +17,166 @@
package org.apache.solr.handler.admin.api;
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
-import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
-import static org.apache.solr.common.params.CollectionParams.ACTION;
-import static org.apache.solr.common.params.CoreAdminParams.SHARD;
-import static org.apache.solr.handler.ClusterAPI.wrapParams;
+import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
+import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
-import java.util.HashMap;
-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.ForceLeaderPayload;
-import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.handler.admin.CollectionsHandler;
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 javax.ws.rs.core.MediaType;
+import org.apache.solr.cloud.ZkController;
+import org.apache.solr.cloud.ZkShardTerms;
+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.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.handler.api.V2ApiUtils;
+import org.apache.solr.jersey.PermissionName;
+import org.apache.solr.jersey.SolrJerseyResponse;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* V2 API for triggering a leader election on a particular collection and shard.
*
- * <p>This API (POST /v2/collections/collectionName/shards/shardName {'force-leader': {}}) is
- * analogous to the v1 /admin/collections?action=FORCELEADER command.
- *
- * @see ForceLeaderPayload
+ * <p>This API (POST /v2/collections/collectionName/shards/shardName/force-leader) is analogous to
+ * the v1 /admin/collections?action=FORCELEADER command.
*/
-@EndPoint(
- path = {"/c/{collection}/shards/{shard}", "/collections/{collection}/shards/{shard}"},
- method = POST,
- permission = COLL_EDIT_PERM)
-public class ForceLeaderAPI {
- private static final String V2_FORCE_LEADER_CMD = "force-leader";
+@Path("/collections/{collectionName}/shards/{shardName}/force-leader")
+public class ForceLeaderAPI extends AdminAPIBase {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ @Inject
+ public ForceLeaderAPI(
+ CoreContainer coreContainer,
+ SolrQueryRequest solrQueryRequest,
+ SolrQueryResponse solrQueryResponse) {
+ super(coreContainer, solrQueryRequest, solrQueryResponse);
+ }
+
+ @POST
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2})
+ @PermissionName(COLL_EDIT_PERM)
+ public SolrJerseyResponse forceLeader(
+ @PathParam("collectionName") String collectionName,
+ @PathParam("shardName") String shardName) {
+ final SolrJerseyResponse response = instantiateJerseyResponse(SolrJerseyResponse.class);
+ ensureRequiredParameterProvided(COLLECTION_PROP, collectionName);
+ ensureRequiredParameterProvided(SHARD_ID_PROP, shardName);
+ fetchAndValidateZooKeeperAwareCoreContainer();
+ recordCollectionForLogAndTracing(collectionName, solrQueryRequest);
+
+ doForceLeaderElection(collectionName, shardName);
+ return response;
+ }
- private final CollectionsHandler collectionsHandler;
+ public static void invokeFromV1Params(
+ CoreContainer coreContainer, SolrQueryRequest request, SolrQueryResponse response) {
+ final var api = new ForceLeaderAPI(coreContainer, request, response);
+ final var params = request.getParams();
+ params.required().check(COLLECTION_PROP, SHARD_ID_PROP);
- public ForceLeaderAPI(CollectionsHandler collectionsHandler) {
- this.collectionsHandler = collectionsHandler;
+ V2ApiUtils.squashIntoSolrResponseWithoutHeader(
+ response, api.forceLeader(params.get(COLLECTION_PROP), params.get(SHARD_ID_PROP)));
}
- @Command(name = V2_FORCE_LEADER_CMD)
- public void forceLeader(PayloadObj<ForceLeaderPayload> obj) throws Exception {
- final Map<String, Object> addedV1Params = new HashMap<>();
- final Map<String, String> pathParams = obj.getRequest().getPathTemplateValues();
- addedV1Params.put(ACTION, CollectionParams.CollectionAction.FORCELEADER.toLower());
- addedV1Params.put(COLLECTION, pathParams.get(COLLECTION));
- addedV1Params.put(SHARD, pathParams.get(SHARD));
+ private void doForceLeaderElection(String extCollectionName, String shardName) {
+ ZkController zkController = coreContainer.getZkController();
+ ClusterState clusterState = zkController.getClusterState();
+ String collectionName =
+ zkController.zkStateReader.getAliases().resolveSimpleAlias(extCollectionName);
+
+ log.info("Force leader invoked, state: {}", clusterState);
+ DocCollection collection = clusterState.getCollection(collectionName);
+ Slice slice = collection.getSlice(shardName);
+ if (slice == null) {
+ throw new SolrException(
+ SolrException.ErrorCode.BAD_REQUEST,
+ "No shard with name " + shardName + " exists for collection " + collectionName);
+ }
+
+ try (ZkShardTerms zkShardTerms =
+ new ZkShardTerms(collectionName, slice.getName(), zkController.getZkClient())) {
+ // if an active replica is the leader, then all is fine already
+ Replica leader = slice.getLeader();
+ if (leader != null && leader.getState() == Replica.State.ACTIVE) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR,
+ "The shard already has an active leader. Force leader is not applicable. State: "
+ + slice);
+ }
+
+ final Set<String> liveNodes = clusterState.getLiveNodes();
+ List<Replica> liveReplicas =
+ slice.getReplicas().stream()
+ .filter(rep -> liveNodes.contains(rep.getNodeName()))
+ .collect(Collectors.toList());
+ boolean shouldIncreaseReplicaTerms =
+ liveReplicas.stream()
+ .noneMatch(
+ rep ->
+ zkShardTerms.registered(rep.getName())
+ && zkShardTerms.canBecomeLeader(rep.getName()));
+ // we won't increase replica's terms if exist a live replica with term equals to leader
+ if (shouldIncreaseReplicaTerms) {
+ // TODO only increase terms of replicas less out-of-sync
+ liveReplicas.stream()
+ .filter(rep -> zkShardTerms.registered(rep.getName()))
+ // TODO should this all be done at once instead of increasing each replica individually?
+ .forEach(rep -> zkShardTerms.setTermEqualsToLeader(rep.getName()));
+ }
+
+ // Wait till we have an active leader
+ boolean success = false;
+ for (int i = 0; i < 9; i++) {
+ Thread.sleep(5000);
+ clusterState = coreContainer.getZkController().getClusterState();
+ collection = clusterState.getCollection(collectionName);
+ slice = collection.getSlice(shardName);
+ if (slice.getLeader() != null && slice.getLeader().getState() == Replica.State.ACTIVE) {
+ success = true;
+ break;
+ }
+ log.warn(
+ "Force leader attempt {}. Waiting 5 secs for an active leader. State of the slice: {}",
+ (i + 1),
+ slice); // nowarn
+ }
- collectionsHandler.handleRequestBody(
- wrapParams(obj.getRequest(), addedV1Params), obj.getResponse());
+ if (success) {
+ log.info(
+ "Successfully issued FORCELEADER command for collection: {}, shard: {}",
+ collectionName,
+ shardName);
+ } else {
+ log.info(
+ "Couldn't successfully force leader, collection: {}, shard: {}. Cluster state: {}",
+ collectionName,
+ shardName,
+ clusterState);
+ }
+ } catch (SolrException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR,
+ "Error executing FORCELEADER operation for collection: "
+ + collectionName
+ + " shard: "
+ + shardName,
+ e);
+ }
}
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCollectionAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCollectionAPI.java
index 00146f801fd..009ee727969 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCollectionAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/ReloadCollectionAPI.java
@@ -16,50 +16,102 @@
*/
package org.apache.solr.handler.admin.api;
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
-import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
-import static org.apache.solr.common.params.CommonParams.ACTION;
+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.common.cloud.ZkStateReader.COLLECTION_PROP;
+import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonParams.NAME;
-import static org.apache.solr.handler.ClusterAPI.wrapParams;
import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.lang.invoke.MethodHandles;
import java.util.HashMap;
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.ReloadCollectionPayload;
+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 javax.ws.rs.core.MediaType;
+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.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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* V2 API for reloading collections.
*
- * <p>The new API (POST /v2/collections/collectionName {'reload': {...}}) is analogous to the v1
+ * <p>The new API (POST /v2/collections/collectionName/reload {...}) is analogous to the v1
* /admin/collections?action=RELOAD command.
- *
- * @see ReloadCollectionPayload
*/
-@EndPoint(
- path = {"/c/{collection}", "/collections/{collection}"},
- method = POST,
- permission = COLL_EDIT_PERM) // TODO Does this permission make sense for reload?
-public class ReloadCollectionAPI {
- private static final String V2_RELOAD_COLLECTION_CMD = "reload";
+@Path("/collections/{collectionName}/reload")
+public class ReloadCollectionAPI extends AdminAPIBase {
- private final CollectionsHandler collectionsHandler;
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- public ReloadCollectionAPI(CollectionsHandler collectionsHandler) {
- this.collectionsHandler = collectionsHandler;
+ @Inject
+ public ReloadCollectionAPI(
+ CoreContainer coreContainer,
+ SolrQueryRequest solrQueryRequest,
+ SolrQueryResponse solrQueryResponse) {
+ super(coreContainer, solrQueryRequest, solrQueryResponse);
}
- @Command(name = V2_RELOAD_COLLECTION_CMD)
- public void reloadCollection(PayloadObj<ReloadCollectionPayload> obj) throws Exception {
- final ReloadCollectionPayload v2Body = obj.get();
- final Map<String, Object> v1Params = v2Body.toMap(new HashMap<>());
- v1Params.put(ACTION, CollectionParams.CollectionAction.RELOAD.toLower());
- v1Params.put(NAME, obj.getRequest().getPathTemplateValues().get(COLLECTION));
+ @POST
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2})
+ @PermissionName(COLL_EDIT_PERM)
+ public SubResponseAccumulatingJerseyResponse reloadCollection(
+ @PathParam("collectionName") String collectionName, ReloadCollectionRequestBody requestBody)
+ throws Exception {
+ final var response = instantiateJerseyResponse(SubResponseAccumulatingJerseyResponse.class);
+ ensureRequiredParameterProvided(COLLECTION_PROP, collectionName);
+ fetchAndValidateZooKeeperAwareCoreContainer();
+ recordCollectionForLogAndTracing(collectionName, solrQueryRequest);
+
+ final ZkNodeProps remoteMessage = createRemoteMessage(collectionName, requestBody);
+ submitRemoteMessageAndHandleResponse(
+ response,
+ CollectionParams.CollectionAction.RELOAD,
+ remoteMessage,
+ requestBody != null ? requestBody.asyncId : null);
+ return response;
+ }
+
+ public static ZkNodeProps createRemoteMessage(
+ String collectionName, ReloadCollectionRequestBody requestBody) {
+ final Map<String, Object> remoteMessage = new HashMap<>();
+ remoteMessage.put(QUEUE_OPERATION, CollectionParams.CollectionAction.RELOAD.toLower());
+ remoteMessage.put(NAME, collectionName);
+ if (requestBody != null) {
+ insertIfNotNull(remoteMessage, ASYNC, requestBody.asyncId);
+ }
+
+ return new ZkNodeProps(remoteMessage);
+ }
+
+ public static void invokeFromV1Params(
+ CoreContainer coreContainer, SolrQueryRequest request, SolrQueryResponse response)
+ throws Exception {
+ final var api = new ReloadCollectionAPI(coreContainer, request, response);
+ final var params = request.getParams();
+ params.required().check(NAME);
+ final var requestBody = new ReloadCollectionRequestBody();
+ requestBody.asyncId = params.get(ASYNC); // Note, 'async' may or may not have been provided.
+
+ V2ApiUtils.squashIntoSolrResponseWithoutHeader(
+ response, api.reloadCollection(params.get(NAME), requestBody));
+ }
- collectionsHandler.handleRequestBody(wrapParams(obj.getRequest(), v1Params), obj.getResponse());
+ // TODO Is it worth having this in a request body, or should we just make it a query param?
+ public static class ReloadCollectionRequestBody implements JacksonReflectMapWriter {
+ @JsonProperty(ASYNC)
+ public String asyncId;
}
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/RenameCollectionAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/RenameCollectionAPI.java
index de6e0106701..c63c518426b 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/RenameCollectionAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/RenameCollectionAPI.java
@@ -16,87 +16,110 @@
*/
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.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.FOLLOW_ALIASES;
import static org.apache.solr.common.params.CollectionAdminParams.TARGET;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
-import static org.apache.solr.common.params.CommonParams.ACTION;
import static org.apache.solr.common.params.CommonParams.NAME;
-import static org.apache.solr.handler.ClusterAPI.wrapParams;
import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.MapperFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import java.io.IOException;
-import java.util.Locale;
-import org.apache.solr.api.EndPoint;
-import org.apache.solr.client.solrj.request.beans.RenameCollectionPayload;
-import org.apache.solr.common.params.CollectionAdminParams;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.HashMap;
+import java.util.Map;
+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 javax.ws.rs.core.MediaType;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.cloud.ZkNodeProps;
import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.common.util.ContentStream;
-import org.apache.solr.handler.admin.CollectionsHandler;
+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;
-import org.apache.solr.util.SolrJacksonAnnotationInspector;
/**
* V2 API for "renaming" an existing collection
*
* <p>This API is analogous to the v1 /admin/collections?action=RENAME command.
*/
-public class RenameCollectionAPI {
+@Path("/collections/{collectionName}/rename")
+public class RenameCollectionAPI extends AdminAPIBase {
- private final CollectionsHandler collectionsHandler;
- private static final ObjectMapper REQUEST_BODY_PARSER =
- SolrJacksonAnnotationInspector.createObjectMapper()
- .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
- .disable(MapperFeature.AUTO_DETECT_FIELDS);
+ @Inject
+ public RenameCollectionAPI(
+ CoreContainer coreContainer,
+ SolrQueryRequest solrQueryRequest,
+ SolrQueryResponse solrQueryResponse) {
+ super(coreContainer, solrQueryRequest, solrQueryResponse);
+ }
+
+ @POST
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2})
+ @PermissionName(COLL_EDIT_PERM)
+ public SubResponseAccumulatingJerseyResponse renameCollection(
+ @PathParam("collectionName") String collectionName, RenameCollectionRequestBody requestBody)
+ throws Exception {
+ final var response = instantiateJerseyResponse(SubResponseAccumulatingJerseyResponse.class);
+ if (requestBody == null) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Missing required request body");
+ }
+ ensureRequiredParameterProvided(COLLECTION_PROP, collectionName);
+ ensureRequiredParameterProvided("to", requestBody.to);
+ fetchAndValidateZooKeeperAwareCoreContainer();
+ recordCollectionForLogAndTracing(collectionName, solrQueryRequest);
- public RenameCollectionAPI(CollectionsHandler collectionsHandler) {
- this.collectionsHandler = collectionsHandler;
+ final ZkNodeProps remoteMessage = createRemoteMessage(collectionName, requestBody);
+ submitRemoteMessageAndHandleResponse(
+ response,
+ CollectionParams.CollectionAction.RENAME,
+ remoteMessage,
+ requestBody != null ? requestBody.asyncId : null);
+ return response;
}
- @EndPoint(
- path = {"/collections/{collection}/rename"},
- method = POST,
- permission = COLL_EDIT_PERM)
- public void renameCollection(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
- final RenameCollectionPayload v2Body = parseRenameParamsFromRequestBody(req);
+ public static ZkNodeProps createRemoteMessage(
+ String collectionName, RenameCollectionRequestBody requestBody) {
+ final Map<String, Object> remoteMessage = new HashMap<>();
+ remoteMessage.put(QUEUE_OPERATION, CollectionParams.CollectionAction.RENAME.toLower());
+ remoteMessage.put(NAME, collectionName);
+ remoteMessage.put(TARGET, requestBody.to);
+ insertIfNotNull(remoteMessage, FOLLOW_ALIASES, requestBody.followAliases);
+ insertIfNotNull(remoteMessage, ASYNC, requestBody.asyncId);
- req =
- wrapParams(
- req,
- ACTION,
- CollectionParams.CollectionAction.RENAME.name().toLowerCase(Locale.ROOT),
- NAME,
- req.getPathTemplateValues().get(CollectionAdminParams.COLLECTION),
- TARGET,
- v2Body.to,
- ASYNC,
- v2Body.async,
- FOLLOW_ALIASES,
- v2Body.followAliases);
- collectionsHandler.handleRequestBody(req, rsp);
+ return new ZkNodeProps(remoteMessage);
}
- // TODO This is a bit hacky, but it's not worth investing in the request-body parsing code much
- // here, as it's
- // something that's already somewhat built-in when this eventually moves to JAX-RS
- private RenameCollectionPayload parseRenameParamsFromRequestBody(
- SolrQueryRequest solrQueryRequest) throws IOException {
- Iterable<ContentStream> contentStreams = solrQueryRequest.getContentStreams();
- ContentStream cs = null;
- if (contentStreams != null) {
- cs = contentStreams.iterator().next();
- }
- if (cs == null) {
- // An empty request-body is invalid (the 'to' field is required at a minimum), but we'll lean
- // on the input-validation in CollectionsHandler to
- // catch this, rather than duplicating the check for that here
- return new RenameCollectionPayload();
- }
+ public static void invokeFromV1Params(
+ CoreContainer coreContainer, SolrQueryRequest request, SolrQueryResponse response)
+ throws Exception {
+ final var api = new RenameCollectionAPI(coreContainer, request, response);
+ final var params = request.getParams();
+ params.required().check(COLLECTION_PROP, TARGET);
+ final var requestBody = new RenameCollectionRequestBody();
+ requestBody.to = params.get(TARGET);
+ // Optional parameters
+ requestBody.asyncId = params.get(ASYNC);
+ requestBody.followAliases = params.getBool(FOLLOW_ALIASES);
+
+ V2ApiUtils.squashIntoSolrResponseWithoutHeader(
+ response, api.renameCollection(params.get(COLLECTION_PROP), requestBody));
+ }
+
+ public static class RenameCollectionRequestBody implements JacksonReflectMapWriter {
+ @JsonProperty(required = true)
+ public String to;
+
+ @JsonProperty(ASYNC)
+ public String asyncId;
- return REQUEST_BODY_PARSER.readValue(cs.getStream(), RenameCollectionPayload.class);
+ @JsonProperty public Boolean followAliases;
}
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SyncShardAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SyncShardAPI.java
index 0819b09ad51..07488e3c5ed 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/SyncShardAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/SyncShardAPI.java
@@ -17,52 +17,99 @@
package org.apache.solr.handler.admin.api;
-import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
-import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
-import static org.apache.solr.common.params.CollectionParams.ACTION;
-import static org.apache.solr.common.params.CoreAdminParams.SHARD;
-import static org.apache.solr.handler.ClusterAPI.wrapParams;
+import static org.apache.solr.client.solrj.impl.BinaryResponseParser.BINARY_CONTENT_TYPE_V2;
+import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
+import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
import static org.apache.solr.security.PermissionNameProvider.Name.COLL_EDIT_PERM;
-import java.util.HashMap;
-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.SyncShardPayload;
-import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.handler.admin.CollectionsHandler;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+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 javax.ws.rs.core.MediaType;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.HttpSolrClient;
+import org.apache.solr.client.solrj.request.CoreAdminRequest;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.ZkCoreNodeProps;
+import org.apache.solr.common.cloud.ZkNodeProps;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.handler.api.V2ApiUtils;
+import org.apache.solr.jersey.PermissionName;
+import org.apache.solr.jersey.SolrJerseyResponse;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
/**
* V2 API for triggering a shard-sync operation within a particular collection and shard.
*
- * <p>This API (POST /v2/collections/collectionName/shards/shardName {'sync-shard': {}}) is
- * analogous to the v1 /admin/collections?action=SYNCSHARD command.
- *
- * @see SyncShardPayload
+ * <p>This API (POST /v2/collections/cName/shards/sName/sync {...}) is analogous to the v1
+ * /admin/collections?action=SYNCSHARD command.
*/
-@EndPoint(
- path = {"/c/{collection}/shards/{shard}", "/collections/{collection}/shards/{shard}"},
- method = POST,
- permission = COLL_EDIT_PERM)
-public class SyncShardAPI {
- private static final String V2_SYNC_SHARD_CMD = "sync-shard";
+@Path("/collections/{collectionName}/shards/{shardName}/sync")
+public class SyncShardAPI extends AdminAPIBase {
+
+ @Inject
+ public SyncShardAPI(
+ CoreContainer coreContainer,
+ SolrQueryRequest solrQueryRequest,
+ SolrQueryResponse solrQueryResponse) {
+ super(coreContainer, solrQueryRequest, solrQueryResponse);
+ }
+
+ @POST
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, BINARY_CONTENT_TYPE_V2})
+ @PermissionName(COLL_EDIT_PERM)
+ public SolrJerseyResponse syncShard(
+ @PathParam("collectionName") String collectionName, @PathParam("shardName") String shardName)
+ throws Exception {
+ final SolrJerseyResponse response = instantiateJerseyResponse(SolrJerseyResponse.class);
+ ensureRequiredParameterProvided(COLLECTION_PROP, collectionName);
+ ensureRequiredParameterProvided(SHARD_ID_PROP, shardName);
+ fetchAndValidateZooKeeperAwareCoreContainer();
+ recordCollectionForLogAndTracing(collectionName, solrQueryRequest);
+
+ doSyncShard(collectionName, shardName);
+
+ return response;
+ }
+
+ private void doSyncShard(String extCollectionName, String shardName)
+ throws IOException, SolrServerException {
+ String collection = coreContainer.getAliases().resolveSimpleAlias(extCollectionName);
+
+ ClusterState clusterState = coreContainer.getZkController().getClusterState();
- private final CollectionsHandler collectionsHandler;
+ DocCollection docCollection = clusterState.getCollection(collection);
+ ZkNodeProps leaderProps = docCollection.getLeader(shardName);
+ ZkCoreNodeProps nodeProps = new ZkCoreNodeProps(leaderProps);
- public SyncShardAPI(CollectionsHandler collectionsHandler) {
- this.collectionsHandler = collectionsHandler;
+ try (SolrClient client =
+ new HttpSolrClient.Builder(nodeProps.getBaseUrl())
+ .withConnectionTimeout(15000, TimeUnit.MILLISECONDS)
+ .withSocketTimeout(60000, TimeUnit.MILLISECONDS)
+ .build()) {
+ CoreAdminRequest.RequestSyncShard reqSyncShard = new CoreAdminRequest.RequestSyncShard();
+ reqSyncShard.setCollection(collection);
+ reqSyncShard.setShard(shardName);
+ reqSyncShard.setCoreName(nodeProps.getCoreName());
+ client.request(reqSyncShard);
+ }
}
- @Command(name = V2_SYNC_SHARD_CMD)
- public void syncShard(PayloadObj<SyncShardPayload> obj) throws Exception {
- final Map<String, Object> addedV1Params = new HashMap<>();
- final Map<String, String> pathParams = obj.getRequest().getPathTemplateValues();
- addedV1Params.put(ACTION, CollectionParams.CollectionAction.SYNCSHARD.toLower());
- addedV1Params.put(COLLECTION, pathParams.get(COLLECTION));
- addedV1Params.put(SHARD, pathParams.get(SHARD));
+ public static void invokeFromV1Params(
+ CoreContainer coreContainer, SolrQueryRequest request, SolrQueryResponse response)
+ throws Exception {
+ final var api = new SyncShardAPI(coreContainer, request, response);
+ final var params = request.getParams();
+ params.required().check(COLLECTION_PROP, SHARD_ID_PROP);
- collectionsHandler.handleRequestBody(
- wrapParams(obj.getRequest(), addedV1Params), obj.getResponse());
+ V2ApiUtils.squashIntoSolrResponseWithoutHeader(
+ response, api.syncShard(params.get(COLLECTION_PROP), params.get(SHARD_ID_PROP)));
}
}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java b/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java
index e416d35bf5b..deefc0e4df3 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/TestApiFramework.java
@@ -102,15 +102,6 @@ public class TestApiFramework extends SolrTestCaseJ4 {
V2HttpCall.getApiInfo(containerHandlers, "/collections/hello/shards", "POST", null, parts);
assertConditions(api.getSpec(), Map.of("/methods[0]", "POST", "/commands/split", NOT_NULL));
- parts = new HashMap<>();
- api =
- V2HttpCall.getApiInfo(
- containerHandlers, "/collections/hello/shards/shard1", "POST", null, parts);
- assertConditions(
- api.getSpec(), Map.of("/methods[0]", "POST", "/commands/force-leader", NOT_NULL));
- assertEquals("hello", parts.get("collection"));
- assertEquals("shard1", parts.get("shard"));
-
parts = new HashMap<>();
api = V2HttpCall.getApiInfo(containerHandlers, "/collections/hello", "POST", null, parts);
assertConditions(api.getSpec(), Map.of("/methods[0]", "POST", "/commands/modify", NOT_NULL));
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/TestCollectionAPIs.java b/solr/core/src/test/org/apache/solr/handler/admin/TestCollectionAPIs.java
index fa11d4aa4cb..4aa4ff96eb2 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/TestCollectionAPIs.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/TestCollectionAPIs.java
@@ -88,9 +88,6 @@ public class TestCollectionAPIs extends SolrTestCaseJ4 {
apiBag.registerObject(clusterAPI.commands);
}
- compareOutput(
- apiBag, "/collections/collName", POST, "{reload:{}}", "{name:collName, operation :reload}");
-
compareOutput(
apiBag,
"/collections/collName/shards",
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/ForceLeaderAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/ForceLeaderAPITest.java
new file mode 100644
index 00000000000..09f97a4757f
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/ForceLeaderAPITest.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
+import org.junit.Test;
+
+/** Unit tests for {@link ForceLeaderAPI} */
+public class ForceLeaderAPITest extends SolrTestCaseJ4 {
+ @Test
+ public void testReportsErrorIfCollectionNameMissing() {
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new ForceLeaderAPI(null, null, null);
+ api.forceLeader(null, "someShard");
+ });
+
+ assertEquals(400, thrown.code());
+ assertEquals("Missing required parameter: collection", thrown.getMessage());
+ }
+
+ @Test
+ public void testReportsErrorIfShardNameMissing() {
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new ForceLeaderAPI(null, null, null);
+ api.forceLeader("someCollection", null);
+ });
+
+ assertEquals(400, thrown.code());
+ assertEquals("Missing required parameter: shard", thrown.getMessage());
+ }
+}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/ReloadCollectionAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/ReloadCollectionAPITest.java
new file mode 100644
index 00000000000..e7534dc0c1a
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/ReloadCollectionAPITest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.params.CommonAdminParams.ASYNC;
+import static org.apache.solr.common.params.CoreAdminParams.NAME;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
+import org.junit.Test;
+
+/** Unit tests for {@link ReloadCollectionAPI} */
+public class ReloadCollectionAPITest extends SolrTestCaseJ4 {
+ @Test
+ public void testReportsErrorIfCollectionNameMissing() {
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new ReloadCollectionAPI(null, null, null);
+ api.reloadCollection(null, new ReloadCollectionAPI.ReloadCollectionRequestBody());
+ });
+
+ assertEquals(400, thrown.code());
+ assertEquals("Missing required parameter: collection", thrown.getMessage());
+ }
+
+ // TODO message creation
+ @Test
+ public void testCreateRemoteMessageAllProperties() {
+ final var requestBody = new ReloadCollectionAPI.ReloadCollectionRequestBody();
+ requestBody.asyncId = "someAsyncId";
+ final var remoteMessage =
+ ReloadCollectionAPI.createRemoteMessage("someCollName", requestBody).getProperties();
+
+ assertEquals(3, remoteMessage.size());
+ assertEquals("reload", remoteMessage.get(QUEUE_OPERATION));
+ assertEquals("someCollName", remoteMessage.get(NAME));
+ assertEquals("someAsyncId", remoteMessage.get(ASYNC));
+ }
+}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/SyncShardAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/SyncShardAPITest.java
new file mode 100644
index 00000000000..25ca5bb9cd1
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/SyncShardAPITest.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
+import org.junit.Test;
+
+/** Unit tests for {@link SyncShardAPI} */
+public class SyncShardAPITest extends SolrTestCaseJ4 {
+ @Test
+ public void testReportsErrorIfCollectionNameMissing() {
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new SyncShardAPI(null, null, null);
+ api.syncShard(null, "someShard");
+ });
+
+ assertEquals(400, thrown.code());
+ assertEquals("Missing required parameter: collection", thrown.getMessage());
+ }
+
+ @Test
+ public void testReportsErrorIfShardNameMissing() {
+ final SolrException thrown =
+ expectThrows(
+ SolrException.class,
+ () -> {
+ final var api = new SyncShardAPI(null, null, null);
+ api.syncShard("someCollection", null);
+ });
+
+ assertEquals(400, thrown.code());
+ assertEquals("Missing required parameter: shard", thrown.getMessage());
+ }
+}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/V2CollectionAPIMappingTest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/V2CollectionAPIMappingTest.java
index fa943c80740..503be4fa6f0 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/api/V2CollectionAPIMappingTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/V2CollectionAPIMappingTest.java
@@ -19,10 +19,8 @@ package org.apache.solr.handler.admin.api;
import static org.apache.solr.common.params.CollectionAdminParams.COLLECTION;
import static org.apache.solr.common.params.CollectionAdminParams.COLL_CONF;
-import static org.apache.solr.common.params.CollectionAdminParams.TARGET;
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonParams.ACTION;
-import static org.apache.solr.common.params.CommonParams.NAME;
import static org.apache.solr.common.params.CoreAdminParams.SHARD;
import java.util.Map;
@@ -59,9 +57,7 @@ public class V2CollectionAPIMappingTest extends V2ApiMappingTest<CollectionsHand
apiBag.registerObject(new ModifyCollectionAPI(collectionsHandler));
apiBag.registerObject(new MoveReplicaAPI(collectionsHandler));
apiBag.registerObject(new RebalanceLeadersAPI(collectionsHandler));
- apiBag.registerObject(new ReloadCollectionAPI(collectionsHandler));
apiBag.registerObject(new CollectionStatusAPI(collectionsHandler));
- apiBag.registerObject(new RenameCollectionAPI(collectionsHandler));
}
@Override
@@ -85,21 +81,6 @@ public class V2CollectionAPIMappingTest extends V2ApiMappingTest<CollectionsHand
assertEquals("shard2", v1Params.get(SHARD));
}
- @Test
- public void testRenameCollectionAllParams() throws Exception {
- final SolrParams v1Params =
- captureConvertedV1Params(
- "/collections/collName/rename",
- "POST",
- "{\"to\": \"targetColl\", \"async\": \"requestTrackingId\", \"followAliases\": true}");
-
- assertEquals("rename", v1Params.get(ACTION));
- assertEquals("collName", v1Params.get(NAME));
- assertEquals("targetColl", v1Params.get(TARGET));
- assertEquals("requestTrackingId", v1Params.get(ASYNC));
- assertEquals(true, v1Params.getPrimitiveBool("followAliases"));
- }
-
@Test
public void testModifyCollectionAllProperties() throws Exception {
final SolrParams v1Params =
@@ -128,17 +109,6 @@ public class V2CollectionAPIMappingTest extends V2ApiMappingTest<CollectionsHand
assertEquals(456, v1Params.getPrimitiveInt("property.baz"));
}
- @Test
- public void testReloadCollectionAllProperties() throws Exception {
- final SolrParams v1Params =
- captureConvertedV1Params(
- "/collections/collName", "POST", "{ 'reload': {'async': 'requestTrackingId'}}");
-
- assertEquals(CollectionParams.CollectionAction.RELOAD.lowerName, v1Params.get(ACTION));
- assertEquals("collName", v1Params.get(NAME));
- assertEquals("requestTrackingId", v1Params.get(ASYNC));
- }
-
@Test
public void testMoveReplicaAllProperties() throws Exception {
final SolrParams v1Params =
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 0d584973a7a..9731922de46 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
@@ -29,7 +29,6 @@ import static org.apache.solr.common.params.CommonAdminParams.SPLIT_METHOD;
import static org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STATE;
import static org.apache.solr.common.params.CommonParams.ACTION;
import static org.apache.solr.common.params.CommonParams.TIMING;
-import static org.apache.solr.common.params.CoreAdminParams.SHARD;
import org.apache.solr.common.params.CollectionParams;
import org.apache.solr.common.params.CoreAdminParams;
@@ -53,8 +52,6 @@ public class V2ShardsAPIMappingTest extends V2ApiMappingTest<CollectionsHandler>
public void populateApiBag() {
final CollectionsHandler collectionsHandler = getRequestHandler();
apiBag.registerObject(new SplitShardAPI(collectionsHandler));
- apiBag.registerObject(new SyncShardAPI(collectionsHandler));
- apiBag.registerObject(new ForceLeaderAPI(collectionsHandler));
}
@Override
@@ -67,28 +64,6 @@ public class V2ShardsAPIMappingTest extends V2ApiMappingTest<CollectionsHandler>
return false;
}
- @Test
- public void testForceLeaderAllProperties() throws Exception {
- final SolrParams v1Params =
- captureConvertedV1Params(
- "/collections/collName/shards/shardName", "POST", "{ 'force-leader': {}}");
-
- assertEquals(CollectionParams.CollectionAction.FORCELEADER.lowerName, v1Params.get(ACTION));
- assertEquals("collName", v1Params.get(COLLECTION));
- assertEquals("shardName", v1Params.get(SHARD));
- }
-
- @Test
- public void testSyncShardAllProperties() throws Exception {
- final SolrParams v1Params =
- captureConvertedV1Params(
- "/collections/collName/shards/shardName", "POST", "{ 'sync-shard': {}}");
-
- assertEquals(CollectionParams.CollectionAction.SYNCSHARD.lowerName, v1Params.get(ACTION));
- assertEquals("collName", v1Params.get(COLLECTION));
- assertEquals("shardName", v1Params.get(SHARD));
- }
-
@Test
public void testSplitShardAllProperties() throws Exception {
final SolrParams v1Params =
diff --git a/solr/core/src/test/org/apache/solr/util/tracing/TestDistributedTracing.java b/solr/core/src/test/org/apache/solr/util/tracing/TestDistributedTracing.java
index cbfd4a64df9..b28f15e18bd 100644
--- a/solr/core/src/test/org/apache/solr/util/tracing/TestDistributedTracing.java
+++ b/solr/core/src/test/org/apache/solr/util/tracing/TestDistributedTracing.java
@@ -134,13 +134,13 @@ public class TestDistributedTracing extends SolrCloudTestCase {
CloudSolrClient cloudClient = cluster.getSolrClient();
List<MockSpan> finishedSpans;
- new V2Request.Builder("/c/" + COLLECTION)
+ new V2Request.Builder("/collections/" + COLLECTION + "/reload")
.withMethod(SolrRequest.METHOD.POST)
- .withPayload("{\n" + " \"reload\" : {}\n" + "}")
+ .withPayload("{}")
.build()
.process(cloudClient);
finishedSpans = getAndClearSpans();
- assertEquals("reload:/c/{collection}", finishedSpans.get(0).operationName());
+ assertEquals("post:/collections/{collection}/reload", finishedSpans.get(0).operationName());
assertDbInstanceColl(finishedSpans.get(0));
new V2Request.Builder("/c/" + COLLECTION + "/update/json")
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 2c8de19cbc4..61e987509c6 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
@@ -321,28 +321,14 @@ http://localhost:8983/solr/admin/collections?action=RELOAD&name=techproducts_v2
====
[.tab-label]*V2 API*
-With the v2 API, the `reload` command is provided as part of the JSON data that contains the required parameters:
+With the v2 API, the `reload` command is provided as a part of URL path. The request body is optional if the optional `async` parameter is omitted:
[source,bash]
----
-curl -X POST http://localhost:8983/api/collections/techproducts_v2 -H 'Content-Type: application/json' -d '
- {
- "reload": {}
- }
-'
-----
-
-Additional parameters can be passed in via the `reload` key:
-
-[source,bash]
-----
-curl -X POST http://localhost:8983/api/collections/techproducts_v2 -H 'Content-Type: application/json' -d '
+curl -X POST http://localhost:8983/api/collections/techproducts_v2/reload -H 'Content-Type: application/json' -d '
{
- "reload": {
- "async": "reload1"
- }
+ "async": "someAsyncId"
}
-'
----
====
--
@@ -357,7 +343,8 @@ curl -X POST http://localhost:8983/api/collections/techproducts_v2 -H 'Content-T
|===
+
The name of the collection to reload.
-This parameter is required by the V1 API.
+This parameter is required.
+It appears as a query-parameter on v1 requests, and in the URL path of v2 requests.
`async`::
+
@@ -542,7 +529,7 @@ Aliases that refer to more than 1 collection are not supported.
[source,bash]
----
-http://localhost:8983/solr/admin/collections?action=RENAME&name=techproducts_v2&target=renamedCollection
+http://localhost:8983/solr/admin/collections?action=RENAME&name=techproducts_v2&target=newName
----
====
@@ -554,7 +541,7 @@ http://localhost:8983/solr/admin/collections?action=RENAME&name=techproducts_v2&
----
curl -X POST http://localhost:8983/api/collections/techproducts/rename -H 'Content-Type: application/json' -d '
{
- "to": "new_name"
+ "to": "newName"
}
'
----
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 c8548b573b9..672ffa199f6 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
@@ -591,11 +591,7 @@ http://localhost:8983/solr/admin/collections?action=FORCELEADER&collection=techp
[source,bash]
----
-curl -X POST http://localhost:8983/api/collections/techproducts/shards/shard1 -H 'Content-Type: application/json' -d '
- {
- "force-leader":{}
- }
-'
+curl -X POST http://localhost:8983/api/collections/techproducts/shards/shard1/force-leader
----
*Output*
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/ForceLeaderPayload.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/ForceLeaderPayload.java
deleted file mode 100644
index fb23dd95c50..00000000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/ForceLeaderPayload.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.client.solrj.request.beans;
-
-import org.apache.solr.common.util.ReflectMapWriter;
-
-/**
- * Empty payload for the POST /v2/collections/collName/shards/shardName {"force-leader": {}} API.
- */
-public class ForceLeaderPayload implements ReflectMapWriter {}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/ReloadCollectionPayload.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/ReloadCollectionPayload.java
deleted file mode 100644
index 91331d44008..00000000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/ReloadCollectionPayload.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.client.solrj.request.beans;
-
-import org.apache.solr.common.annotation.JsonProperty;
-import org.apache.solr.common.util.ReflectMapWriter;
-
-public class ReloadCollectionPayload implements ReflectMapWriter {
- @JsonProperty public String async;
-}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/RenameCollectionPayload.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/RenameCollectionPayload.java
deleted file mode 100644
index c85fd42daee..00000000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/RenameCollectionPayload.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.client.solrj.request.beans;
-
-import org.apache.solr.common.annotation.JsonProperty;
-import org.apache.solr.common.util.ReflectMapWriter;
-
-public class RenameCollectionPayload implements ReflectMapWriter {
- @JsonProperty public String async;
-
- @JsonProperty public Boolean followAliases;
-
- @JsonProperty(required = true)
- public String to;
-}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/SyncShardPayload.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/SyncShardPayload.java
deleted file mode 100644
index b6636e65b6e..00000000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/beans/SyncShardPayload.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.client.solrj.request.beans;
-
-import org.apache.solr.common.util.ReflectMapWriter;
-
-/** Empty payload for the /v2/collections/collName/shards/shardName {"sync-shard": {}} API. */
-public class SyncShardPayload implements ReflectMapWriter {}