Skip to content

Commit

Permalink
SOLR-16397: Tweak v2 'REQUESTSTATUS' API to be more REST-ful (#2144)
Browse files Browse the repository at this point in the history
This commit changes the v2 "requeststatus" (core level) API to be more in line
with the REST-ful design we're targeting for Solr's v2 APIs.

Following these changes, the v2 API now appears as:
    `GET /api/node/commands/someCommandId`

---------

Co-authored-by: iamsanjay <[email protected]>
  • Loading branch information
iamsanjay and iamsanjay authored Jan 25, 2024
1 parent 571c887 commit 06a3751
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 111 deletions.
3 changes: 3 additions & 0 deletions solr/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ Improvements

* SOLR-17096: solr.xml now supports declaring clusterSingleton plugins (Paul McArthur, David Smiley)

* SOLR-16397: The v2 endpoint to request the status of asynchronous CoreAdmin commands has been updated to be more REST-ful.
Now available at `GET /api/node/commands/someRequestId` (Sanjay Dutt via Jason Gerlowski)

Optimizations
---------------------
* SOLR-17084: LBSolrClient (used by CloudSolrClient) now returns the count of core tracked as not live AKA zombies
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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.api.endpoint;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.apache.solr.client.api.model.GetNodeCommandStatusResponse;

/**
* V2 API for checking the status of a core-level asynchronous command.
*
* <p>This API is analogous to the v1 /admin/cores?action=REQUESTSTATUS command. It is not to be
* confused with the more robust asynchronous command support offered under the v2
* `/cluster/command-status` path (or the corresponding v1 path
* `/solr/admin/collections?action=REQUESTSTATUS`). Async support at the core level differs in that
* command IDs are local to individual Solr nodes and are not persisted across restarts.
*
* @see GetNodeCommandStatusResponse
*/
@Path("/node/commands/")
public interface GetNodeCommandStatusApi {
@Path("/{requestId}")
@GET
@Operation(
summary = "Request the status of an already submitted asynchronous CoreAdmin API call.",
tags = {"node"})
GetNodeCommandStatusResponse getCommandStatus(
@Parameter(
description = "The user defined request-id for the asynchronous request.",
required = true)
@PathParam("requestId")
String id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.api.model;

import com.fasterxml.jackson.annotation.JsonProperty;

public class GetNodeCommandStatusResponse extends SolrJerseyResponse {
@JsonProperty("STATUS")
public String status;

@JsonProperty public String msg;
@JsonProperty public Object response;
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.apache.solr.handler.admin.api.CoreSnapshot;
import org.apache.solr.handler.admin.api.CreateCoreAPI;
import org.apache.solr.handler.admin.api.CreateCoreBackup;
import org.apache.solr.handler.admin.api.GetNodeCommandStatus;
import org.apache.solr.handler.admin.api.InstallCoreData;
import org.apache.solr.handler.admin.api.MergeIndexes;
import org.apache.solr.handler.admin.api.OverseerOperationAPI;
Expand All @@ -67,7 +68,6 @@
import org.apache.solr.handler.admin.api.RenameCore;
import org.apache.solr.handler.admin.api.RequestApplyCoreUpdatesAPI;
import org.apache.solr.handler.admin.api.RequestBufferUpdatesAPI;
import org.apache.solr.handler.admin.api.RequestCoreCommandStatusAPI;
import org.apache.solr.handler.admin.api.RequestCoreRecoveryAPI;
import org.apache.solr.handler.admin.api.RequestSyncShardAPI;
import org.apache.solr.handler.admin.api.RestoreCore;
Expand Down Expand Up @@ -384,7 +384,6 @@ public Collection<Api> getApis() {
apis.addAll(AnnotatedApi.getApis(new RejoinLeaderElectionAPI(this)));
apis.addAll(AnnotatedApi.getApis(new OverseerOperationAPI(this)));
apis.addAll(AnnotatedApi.getApis(new SplitCoreAPI(this)));
apis.addAll(AnnotatedApi.getApis(new RequestCoreCommandStatusAPI(this)));
// Internal APIs
apis.addAll(AnnotatedApi.getApis(new RequestCoreRecoveryAPI(this)));
apis.addAll(AnnotatedApi.getApis(new PrepareCoreRecoveryAPI(this)));
Expand All @@ -406,7 +405,8 @@ public Collection<Class<? extends JerseyResource>> getJerseyResources() {
UnloadCore.class,
SwapCores.class,
RenameCore.class,
MergeIndexes.class);
MergeIndexes.class,
GetNodeCommandStatus.class);
}

public interface CoreAdminOp {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@
import static org.apache.solr.common.params.CoreAdminParams.REPLICA_TYPE;
import static org.apache.solr.common.params.CoreAdminParams.SHARD;
import static org.apache.solr.handler.admin.CoreAdminHandler.CallInfo;
import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.COMPLETED;
import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.FAILED;
import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.RUNNING;
import static org.apache.solr.handler.admin.CoreAdminHandler.OPERATION_RESPONSE;
import static org.apache.solr.handler.admin.CoreAdminHandler.RESPONSE_MESSAGE;
import static org.apache.solr.handler.admin.CoreAdminHandler.RESPONSE_STATUS;
import static org.apache.solr.handler.admin.CoreAdminHandler.buildCoreParams;
import static org.apache.solr.handler.admin.CoreAdminHandler.normalizePath;

Expand Down Expand Up @@ -79,6 +73,7 @@
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminOp;
import org.apache.solr.handler.admin.api.CoreSnapshot;
import org.apache.solr.handler.admin.api.GetNodeCommandStatus;
import org.apache.solr.handler.admin.api.ReloadCore;
import org.apache.solr.handler.admin.api.RenameCore;
import org.apache.solr.handler.admin.api.SwapCores;
Expand Down Expand Up @@ -242,34 +237,15 @@ public enum CoreAdminOperation implements CoreAdminOp {
REQUESTSTATUS_OP(
REQUESTSTATUS,
it -> {
SolrParams params = it.req.getParams();
String requestId = params.required().get(CoreAdminParams.REQUESTID);
final var params = it.req.getParams();
final String requestId = params.required().get(CoreAdminParams.REQUESTID);
log().info("Checking request status for : " + requestId);

final CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker =
it.handler.getCoreAdminAsyncTracker();
if (coreAdminAsyncTracker.getRequestStatusMap(RUNNING).containsKey(requestId)) {
it.rsp.add(RESPONSE_STATUS, RUNNING);
} else if (coreAdminAsyncTracker.getRequestStatusMap(COMPLETED).containsKey(requestId)) {
it.rsp.add(RESPONSE_STATUS, COMPLETED);
it.rsp.add(
RESPONSE_MESSAGE,
coreAdminAsyncTracker.getRequestStatusMap(COMPLETED).get(requestId).getRspObject());
it.rsp.add(
OPERATION_RESPONSE,
coreAdminAsyncTracker
.getRequestStatusMap(COMPLETED)
.get(requestId)
.getOperationRspObject());
} else if (coreAdminAsyncTracker.getRequestStatusMap(FAILED).containsKey(requestId)) {
it.rsp.add(RESPONSE_STATUS, FAILED);
it.rsp.add(
RESPONSE_MESSAGE,
coreAdminAsyncTracker.getRequestStatusMap(FAILED).get(requestId).getRspObject());
} else {
it.rsp.add(RESPONSE_STATUS, "notfound");
it.rsp.add(RESPONSE_MESSAGE, "No task found in running, completed or failed tasks");
}
final var requestCoreCommandStatusApi =
new GetNodeCommandStatus(
it.handler.coreContainer, it.handler.coreAdminAsyncTracker, it.req, it.rsp);
final SolrJerseyResponse response = requestCoreCommandStatusApi.getCommandStatus(requestId);
V2ApiUtils.squashIntoSolrResponseWithoutHeader(it.rsp, response);
}),

OVERSEEROP_OP(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.COMPLETED;
import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.FAILED;
import static org.apache.solr.handler.admin.CoreAdminHandler.CoreAdminAsyncTracker.RUNNING;

import jakarta.inject.Inject;
import org.apache.solr.client.api.endpoint.GetNodeCommandStatusApi;
import org.apache.solr.client.api.model.GetNodeCommandStatusResponse;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.jersey.PermissionName;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.security.PermissionNameProvider;

/**
* Implementation of V2 API interface {@link GetNodeCommandStatusApi} for checking the status of a
* core-level asynchronous command.
*/
public class GetNodeCommandStatus extends CoreAdminAPIBase implements GetNodeCommandStatusApi {

@Inject
public GetNodeCommandStatus(
CoreContainer coreContainer,
CoreAdminHandler.CoreAdminAsyncTracker coreAdminAsyncTracker,
SolrQueryRequest req,
SolrQueryResponse rsp) {
super(coreContainer, coreAdminAsyncTracker, req, rsp);
}

@Override
@PermissionName(PermissionNameProvider.Name.CORE_READ_PERM)
public GetNodeCommandStatusResponse getCommandStatus(String requestId) {
ensureRequiredParameterProvided(CoreAdminParams.REQUESTID, requestId);
var requestStatusResponse = new GetNodeCommandStatusResponse();
if (coreAdminAsyncTracker.getRequestStatusMap(RUNNING).containsKey(requestId)) {
requestStatusResponse.status = RUNNING;
} else if (coreAdminAsyncTracker.getRequestStatusMap(COMPLETED).containsKey(requestId)) {
requestStatusResponse.status = COMPLETED;
requestStatusResponse.response =
coreAdminAsyncTracker.getRequestStatusMap(COMPLETED).get(requestId).getRspObject();
requestStatusResponse.response =
coreAdminAsyncTracker
.getRequestStatusMap(COMPLETED)
.get(requestId)
.getOperationRspObject();
} else if (coreAdminAsyncTracker.getRequestStatusMap(FAILED).containsKey(requestId)) {
requestStatusResponse.status = FAILED;
requestStatusResponse.response =
coreAdminAsyncTracker.getRequestStatusMap(FAILED).get(requestId).getRspObject();
} else {
requestStatusResponse.status = "notfound";
requestStatusResponse.msg = "No task found in running, completed or failed tasks";
}
return requestStatusResponse;
}
}

This file was deleted.

Loading

0 comments on commit 06a3751

Please sign in to comment.