/* * Licensed to Crate under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial * agreement. */ package io.crate.executor.transport; import io.crate.exceptions.JobKilledException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.TransportActions; import org.elasticsearch.cluster.action.shard.ShardStateAction; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Singleton; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.indices.IndicesService; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import java.util.concurrent.atomic.AtomicBoolean; @Singleton public class TransportShardDeleteAction extends TransportShardAction<ShardDeleteRequest, ShardDeleteRequest.Item> { private final static String ACTION_NAME = "indices:crate/data/write/delete"; @Inject public TransportShardDeleteAction(Settings settings, TransportService transportService, IndexNameExpressionResolver indexNameExpressionResolver, ClusterService clusterService, IndicesService indicesService, ThreadPool threadPool, ShardStateAction shardStateAction, ActionFilters actionFilters) { super(settings, ACTION_NAME, transportService, indexNameExpressionResolver, clusterService, indicesService, threadPool, shardStateAction, actionFilters, ShardDeleteRequest::new); } @Override protected WriteResult<ShardResponse> processRequestItems(ShardId shardId, ShardDeleteRequest request, AtomicBoolean killed) throws InterruptedException { ShardResponse shardResponse = new ShardResponse(); IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); IndexShard indexShard = indexService.getShard(shardId.id()); Translog.Location translogLocation = null; for (int i = 0; i < request.itemIndices().size(); i++) { int location = request.itemIndices().get(i); ShardDeleteRequest.Item item = request.items().get(i); if (killed.get()) { // set failure on response, mark current item and skip all next items. // this way replica operation will be executed, but only items already processed here // will be processed on the replica request.skipFromLocation(location); shardResponse.failure(new InterruptedException(JobKilledException.MESSAGE)); break; } try { DeleteResult deleteResult = shardDeleteOperationOnPrimary(request, item, indexShard); translogLocation = deleteResult.location; if (deleteResult.found) { logger.debug("{} successfully deleted [{}]/[{}]", request.shardId(), request.type(), item.id()); shardResponse.add(location); } else { logger.debug("{} failed to execute delete for [{}]/[{}], doc not found", request.shardId(), request.type(), item.id()); shardResponse.add(location, new ShardResponse.Failure( item.id(), "Document not found while deleting", false)); } } catch (Throwable t) { if (!TransportActions.isShardNotAvailableException(t)) { throw t; } else { logger.debug("{} failed to execute delete for [{}]/[{}]", t, request.shardId(), request.type(), item.id()); shardResponse.add(location, new ShardResponse.Failure( item.id(), ExceptionsHelper.detailedMessage(t), (t instanceof VersionConflictEngineException))); } } } return new WriteResult<>(shardResponse, translogLocation); } @Override protected Translog.Location processRequestItemsOnReplica(ShardId shardId, ShardDeleteRequest request) { IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex()); IndexShard indexShard = indexService.getShard(shardId.id()); Translog.Location translogLocation = null; for (int i = 0; i < request.itemIndices().size(); i++) { int location = request.itemIndices().get(i); if (request.skipFromLocation() == location) { // skipping this and all next items, the primary did not processed them (mostly due to a kill request) break; } ShardDeleteRequest.Item item = request.items().get(i); Engine.Delete delete = indexShard.prepareDeleteOnReplica(request.type(), item.id(), item.version(), item.versionType()); indexShard.delete(delete); logger.trace("{} REPLICA: successfully deleted [{}]/[{}]", request.shardId(), request.type(), item.id()); translogLocation = delete.getTranslogLocation(); } return translogLocation; } private DeleteResult shardDeleteOperationOnPrimary(ShardDeleteRequest request, ShardDeleteRequest.Item item, IndexShard indexShard) { Engine.Delete delete = indexShard.prepareDeleteOnPrimary(request.type(), item.id(), item.version(), item.versionType()); indexShard.delete(delete); // update the request with the version so it will go to the replicas item.versionType(delete.versionType().versionTypeForReplicationAndRecovery()); item.version(delete.version()); assert item.versionType().validateVersionForWrites(item.version()) : "item.version() must be valid"; return new DeleteResult(delete.found(), delete.getTranslogLocation()); } private static class DeleteResult { private final boolean found; private final Translog.Location location; DeleteResult(boolean found, Translog.Location location) { this.found = found; this.location = location; } } }