|
@@ -13,13 +13,11 @@ import com.amazonaws.services.s3.AmazonS3;
|
|
|
import com.amazonaws.services.s3.model.AbortMultipartUploadRequest;
|
|
|
import com.amazonaws.services.s3.model.AmazonS3Exception;
|
|
|
import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest;
|
|
|
-import com.amazonaws.services.s3.model.DeleteObjectsRequest;
|
|
|
import com.amazonaws.services.s3.model.GetObjectRequest;
|
|
|
import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
|
|
|
import com.amazonaws.services.s3.model.ListMultipartUploadsRequest;
|
|
|
import com.amazonaws.services.s3.model.ListNextBatchOfObjectsRequest;
|
|
|
import com.amazonaws.services.s3.model.ListObjectsRequest;
|
|
|
-import com.amazonaws.services.s3.model.MultiObjectDeleteException;
|
|
|
import com.amazonaws.services.s3.model.MultipartUpload;
|
|
|
import com.amazonaws.services.s3.model.ObjectListing;
|
|
|
import com.amazonaws.services.s3.model.ObjectMetadata;
|
|
@@ -32,7 +30,6 @@ import com.amazonaws.services.s3.model.UploadPartResult;
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
import org.apache.logging.log4j.Logger;
|
|
|
import org.apache.lucene.util.SetOnce;
|
|
|
-import org.elasticsearch.ExceptionsHelper;
|
|
|
import org.elasticsearch.action.ActionListener;
|
|
|
import org.elasticsearch.action.ActionRunnable;
|
|
|
import org.elasticsearch.action.support.RefCountingListener;
|
|
@@ -70,12 +67,10 @@ import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
-import java.util.concurrent.atomic.AtomicReference;
|
|
|
import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import static org.elasticsearch.common.blobstore.support.BlobContainerUtils.getRegisterUsingConsistentRead;
|
|
|
-import static org.elasticsearch.core.Strings.format;
|
|
|
import static org.elasticsearch.repositories.s3.S3Repository.MAX_FILE_SIZE;
|
|
|
import static org.elasticsearch.repositories.s3.S3Repository.MAX_FILE_SIZE_USING_MULTIPART;
|
|
|
import static org.elasticsearch.repositories.s3.S3Repository.MIN_PART_SIZE_USING_MULTIPART;
|
|
@@ -84,12 +79,6 @@ class S3BlobContainer extends AbstractBlobContainer {
|
|
|
|
|
|
private static final Logger logger = LogManager.getLogger(S3BlobContainer.class);
|
|
|
|
|
|
- /**
|
|
|
- * Maximum number of deletes in a {@link DeleteObjectsRequest}.
|
|
|
- * @see <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html">S3 Documentation</a>.
|
|
|
- */
|
|
|
- private static final int MAX_BULK_DELETES = 1000;
|
|
|
-
|
|
|
private final S3BlobStore blobStore;
|
|
|
private final String keyPath;
|
|
|
|
|
@@ -357,55 +346,7 @@ class S3BlobContainer extends AbstractBlobContainer {
|
|
|
outstanding = blobNames;
|
|
|
}
|
|
|
|
|
|
- final List<String> partition = new ArrayList<>();
|
|
|
- try (AmazonS3Reference clientReference = blobStore.clientReference()) {
|
|
|
- // S3 API only allows 1k blobs per delete so we split up the given blobs into requests of max. 1k deletes
|
|
|
- final AtomicReference<Exception> aex = new AtomicReference<>();
|
|
|
- SocketAccess.doPrivilegedVoid(() -> {
|
|
|
- outstanding.forEachRemaining(key -> {
|
|
|
- partition.add(key);
|
|
|
- if (partition.size() == MAX_BULK_DELETES) {
|
|
|
- deletePartition(clientReference, partition, aex);
|
|
|
- partition.clear();
|
|
|
- }
|
|
|
- });
|
|
|
- if (partition.isEmpty() == false) {
|
|
|
- deletePartition(clientReference, partition, aex);
|
|
|
- }
|
|
|
- });
|
|
|
- if (aex.get() != null) {
|
|
|
- throw aex.get();
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- throw new IOException("Failed to delete blobs " + partition.stream().limit(10).toList(), e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void deletePartition(AmazonS3Reference clientReference, List<String> partition, AtomicReference<Exception> aex) {
|
|
|
- try {
|
|
|
- clientReference.client().deleteObjects(bulkDelete(blobStore, partition));
|
|
|
- } catch (MultiObjectDeleteException e) {
|
|
|
- // We are sending quiet mode requests so we can't use the deleted keys entry on the exception and instead
|
|
|
- // first remove all keys that were sent in the request and then add back those that ran into an exception.
|
|
|
- logger.warn(
|
|
|
- () -> format(
|
|
|
- "Failed to delete some blobs %s",
|
|
|
- e.getErrors().stream().map(err -> "[" + err.getKey() + "][" + err.getCode() + "][" + err.getMessage() + "]").toList()
|
|
|
- ),
|
|
|
- e
|
|
|
- );
|
|
|
- aex.set(ExceptionsHelper.useOrSuppress(aex.get(), e));
|
|
|
- } catch (AmazonClientException e) {
|
|
|
- // The AWS client threw any unexpected exception and did not execute the request at all so we do not
|
|
|
- // remove any keys from the outstanding deletes set.
|
|
|
- aex.set(ExceptionsHelper.useOrSuppress(aex.get(), e));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static DeleteObjectsRequest bulkDelete(S3BlobStore blobStore, List<String> blobs) {
|
|
|
- return new DeleteObjectsRequest(blobStore.bucket()).withKeys(blobs.toArray(Strings.EMPTY_ARRAY))
|
|
|
- .withQuiet(true)
|
|
|
- .withRequestMetricCollector(blobStore.deleteMetricCollector);
|
|
|
+ blobStore.deleteBlobsIgnoringIfNotExists(outstanding);
|
|
|
}
|
|
|
|
|
|
@Override
|