Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions changelog/unreleased/solr-18011-locking-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
title: Allow locked Admin APIs to call other locked AdminAPIs without deadlocking
type: changed # added, changed, fixed, deprecated, removed, dependency_update, security, other
authors:
- name: Houston Putman
nick: HoustonPutman
url: https://home.apache.org/phonebook.html?uid=houston
links:
- name: SOLR-18011
url: https://issues.apache.org/jira/browse/SOLR-18011
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.apache.solr.client.api.model.AddReplicaPropertyRequestBody;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;

@Path("/collections/{collName}/shards/{shardName}/replicas/{replicaName}/properties/{propName}")
public interface AddReplicaPropertyApi {
Expand All @@ -33,7 +33,7 @@ public interface AddReplicaPropertyApi {
@Operation(
summary = "Adds a property to the specified replica",
tags = {"replica-properties"})
public SolrJerseyResponse addReplicaProperty(
public SubResponseAccumulatingJerseyResponse addReplicaProperty(
@Parameter(
description = "The name of the collection the replica belongs to.",
required = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import jakarta.ws.rs.PathParam;
import org.apache.solr.client.api.model.GetAliasPropertyResponse;
import org.apache.solr.client.api.model.GetAllAliasPropertiesResponse;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;
import org.apache.solr.client.api.model.UpdateAliasPropertiesRequestBody;
import org.apache.solr.client.api.model.UpdateAliasPropertyRequestBody;

Expand Down Expand Up @@ -56,7 +56,7 @@ GetAliasPropertyResponse getAliasProperty(
@Operation(
summary = "Update properties for a collection alias.",
tags = {"alias-properties"})
SolrJerseyResponse updateAliasProperties(
SubResponseAccumulatingJerseyResponse updateAliasProperties(
@Parameter(description = "Alias Name") @PathParam("aliasName") String aliasName,
@RequestBody(description = "Properties that need to be updated", required = true)
UpdateAliasPropertiesRequestBody requestBody)
Expand All @@ -67,7 +67,7 @@ SolrJerseyResponse updateAliasProperties(
@Operation(
summary = "Update a specific property for a collection alias.",
tags = {"alias-properties"})
SolrJerseyResponse createOrUpdateAliasProperty(
SubResponseAccumulatingJerseyResponse createOrUpdateAliasProperty(
@Parameter(description = "Alias Name") @PathParam("aliasName") String aliasName,
@Parameter(description = "Property Name") @PathParam("propName") String propName,
@RequestBody(description = "Property value that needs to be updated", required = true)
Expand All @@ -79,7 +79,7 @@ SolrJerseyResponse createOrUpdateAliasProperty(
@Operation(
summary = "Delete a specific property for a collection alias.",
tags = {"alias-properties"})
SolrJerseyResponse deleteAliasProperty(
SubResponseAccumulatingJerseyResponse deleteAliasProperty(
@Parameter(description = "Alias Name") @PathParam("aliasName") String aliasName,
@Parameter(description = "Property Name") @PathParam("propName") String propName)
throws Exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import org.apache.solr.client.api.model.BalanceReplicasRequestBody;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;

@Path("cluster/replicas/balance")
public interface BalanceReplicasApi {
@POST
@Operation(
summary = "Balance Replicas across the given set of Nodes.",
tags = {"cluster"})
SolrJerseyResponse balanceReplicas(
SubResponseAccumulatingJerseyResponse balanceReplicas(
@RequestBody(description = "Contains user provided parameters")
BalanceReplicasRequestBody requestBody)
throws Exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import org.apache.solr.client.api.model.CreateAliasRequestBody;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;

@Path("/aliases")
public interface CreateAliasApi {
@POST
@Operation(
summary = "Create a traditional or 'routed' alias",
tags = {"aliases"})
SolrJerseyResponse createAlias(CreateAliasRequestBody requestBody) throws Exception;
SubResponseAccumulatingJerseyResponse createAlias(CreateAliasRequestBody requestBody)
throws Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;

@Path("/aliases/{aliasName}")
public interface DeleteAliasApi {
Expand All @@ -32,7 +32,7 @@ public interface DeleteAliasApi {
@Operation(
summary = "Deletes an alias by its name",
tags = {"aliases"})
SolrJerseyResponse deleteAlias(
SubResponseAccumulatingJerseyResponse deleteAlias(
@Parameter(description = "The name of the alias to delete", required = true)
@PathParam("aliasName")
String aliasName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.apache.solr.client.api.model.DeleteNodeRequestBody;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;

@Path("cluster/nodes/{nodeName}/clear/")
public interface DeleteNodeApi {
Expand All @@ -33,7 +33,7 @@ public interface DeleteNodeApi {
@Operation(
summary = "Delete all replicas off of the specified SolrCloud node",
tags = {"node"})
SolrJerseyResponse deleteNode(
SubResponseAccumulatingJerseyResponse deleteNode(
@Parameter(
description =
"The name of the node to be cleared. Usually of the form 'host:1234_solr'.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.apache.solr.client.api.model.InstallShardDataRequestBody;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;

/** V2 API definition allowing users to import offline-constructed index into a shard. */
@Path("/collections/{collName}/shards/{shardName}/install")
Expand All @@ -30,7 +30,7 @@ public interface InstallShardDataApi {
@Operation(
summary = "Install offline index into an existing shard",
tags = {"shards"})
SolrJerseyResponse installShardData(
SubResponseAccumulatingJerseyResponse installShardData(
@PathParam("collName") String collName,
@PathParam("shardName") String shardName,
InstallShardDataRequestBody requestBody)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import org.apache.solr.client.api.model.MigrateReplicasRequestBody;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;

/** V2 API definition for migrating replicas from a set of nodes to another set of nodes. */
@Path("cluster/replicas/migrate")
Expand All @@ -30,7 +30,7 @@ public interface MigrateReplicasApi {
@Operation(
summary = "Migrate Replicas from a given set of nodes.",
tags = {"cluster"})
SolrJerseyResponse migrateReplicas(
SubResponseAccumulatingJerseyResponse migrateReplicas(
@RequestBody(description = "Contains user provided parameters", required = true)
MigrateReplicasRequestBody requestBody)
throws Exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.apache.solr.client.api.model.ReplaceNodeRequestBody;
import org.apache.solr.client.api.model.SolrJerseyResponse;
import org.apache.solr.client.api.model.SubResponseAccumulatingJerseyResponse;

/**
* V2 API definition for recreating replicas in one node (the source) on another node(s) (the
Expand All @@ -35,7 +35,7 @@ public interface ReplaceNodeApi {
@Operation(
summary = "'Replace' a specified node by moving all replicas elsewhere",
tags = {"node"})
SolrJerseyResponse replaceNode(
SubResponseAccumulatingJerseyResponse replaceNode(
@Parameter(description = "The name of the node to be replaced.", required = true)
@PathParam("sourceNodeName")
String sourceNodeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;

public class CreateCollectionSnapshotResponse extends AsyncJerseyResponse {
public class CreateCollectionSnapshotResponse extends SubResponseAccumulatingJerseyResponse {
@Schema(description = "The name of the collection.")
public String collection;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import io.swagger.v3.oas.annotations.media.Schema;

/** The Response for {@link org.apache.solr.client.api.endpoint.CollectionSnapshotApis.Delete} */
public class DeleteCollectionSnapshotResponse extends AsyncJerseyResponse {
public class DeleteCollectionSnapshotResponse extends SubResponseAccumulatingJerseyResponse {
@Schema(description = "The name of the collection.")
@JsonProperty(COLLECTION)
public String collection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,9 @@ public class InstallShardDataRequestBody {

@JsonProperty public String repository;

@JsonProperty public String name;

@JsonProperty public String shardBackupId;

@JsonProperty public String async;
}
6 changes: 6 additions & 0 deletions solr/core/src/java/org/apache/solr/api/V2HttpCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.apache.solr.api;

import static org.apache.solr.common.cloud.ZkStateReader.COLLECTION_PROP;
import static org.apache.solr.common.params.CollectionAdminParams.CALLING_LOCK_IDS_HEADER;
import static org.apache.solr.servlet.SolrDispatchFilter.Action.ADMIN;
import static org.apache.solr.servlet.SolrDispatchFilter.Action.ADMIN_OR_REMOTEPROXY;
import static org.apache.solr.servlet.SolrDispatchFilter.Action.PROCESS;
Expand Down Expand Up @@ -216,6 +217,11 @@ private void initAdminRequest(String path) throws Exception {
solrReq.getContext().put(CoreContainer.class.getName(), cores);
requestType = AuthorizationContext.RequestType.ADMIN;
action = ADMIN;

String callingLockIds = req.getHeader(CALLING_LOCK_IDS_HEADER);
if (callingLockIds != null && !callingLockIds.isBlank()) {
solrReq.getContext().put(CALLING_LOCK_IDS_HEADER, callingLockIds);
}
}

protected void parseRequest() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.apache.solr.cloud;

import java.util.List;
import org.apache.solr.cloud.api.collections.CollectionApiLockFactory;
import org.apache.solr.common.params.CollectionParams;

Expand Down Expand Up @@ -62,5 +63,6 @@ DistributedLock createLock(
CollectionParams.LockLevel level,
String collName,
String shardId,
String replicaName);
String replicaName,
List<String> callingLockIds);
}
4 changes: 4 additions & 0 deletions solr/core/src/java/org/apache/solr/cloud/DistributedLock.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ public interface DistributedLock {
void release();

boolean isAcquired();

String getLockId();

boolean isMirroringLock();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.common.annotations.VisibleForTesting;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.solr.common.SolrException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -46,7 +47,12 @@ public void waitUntilAcquired() {
for (DistributedLock lock : locks) {
log.debug("DistributedMultiLock.waitUntilAcquired. About to wait on lock {}", lock);
lock.waitUntilAcquired();
log.debug("DistributedMultiLock.waitUntilAcquired. Acquired lock {}", lock);
if (lock.isMirroringLock()) {
log.debug(
"DistributedMultiLock.waitUntilAcquired. Mirroring already-acquired lock {}", lock);
} else {
log.debug("DistributedMultiLock.waitUntilAcquired. Acquired lock {}", lock);
}
}
}

Expand All @@ -70,6 +76,10 @@ public boolean isAcquired() {
return true;
}

public String getLockId() {
return locks.stream().map(DistributedLock::getLockId).collect(Collectors.joining(","));
}

@VisibleForTesting
public int getCountInternalLocks() {
return locks.size();
Expand Down
Loading
Loading