Skip to content

Commit 116b050

Browse files
Cleanup Bulk Delete Exception Logging (#41693) (#42606)
* Cleanup Bulk Delete Exception Logging * Follow up to #41368 * Collect all failed blob deletes and add them to the exception message * Remove logging of blob name list from caller exception logging
1 parent 44bf784 commit 116b050

File tree

3 files changed

+30
-15
lines changed

3 files changed

+30
-15
lines changed

plugins/repository-gcs/src/main/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageBlobStore.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ public void error(StorageException exception) {
329329
if (e != null) {
330330
throw new IOException("Exception when deleting blobs [" + failedBlobs + "]", e);
331331
}
332+
assert failedBlobs.isEmpty();
332333
}
333334

334335
private static String buildKey(String keyPath, String s) {

plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest;
2626
import com.amazonaws.services.s3.model.DeleteObjectsRequest;
2727
import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
28+
import com.amazonaws.services.s3.model.MultiObjectDeleteException;
2829
import com.amazonaws.services.s3.model.ObjectListing;
2930
import com.amazonaws.services.s3.model.ObjectMetadata;
3031
import com.amazonaws.services.s3.model.PartETag;
@@ -34,6 +35,7 @@
3435
import com.amazonaws.services.s3.model.UploadPartRequest;
3536
import com.amazonaws.services.s3.model.UploadPartResult;
3637
import org.apache.lucene.util.SetOnce;
38+
import org.elasticsearch.ExceptionsHelper;
3739
import org.elasticsearch.common.Nullable;
3840
import org.elasticsearch.common.Strings;
3941
import org.elasticsearch.common.blobstore.BlobMetaData;
@@ -50,6 +52,8 @@
5052
import java.util.ArrayList;
5153
import java.util.List;
5254
import java.util.Map;
55+
import java.util.Set;
56+
import java.util.stream.Collectors;
5357

5458
import static org.elasticsearch.repositories.s3.S3Repository.MAX_FILE_SIZE;
5559
import static org.elasticsearch.repositories.s3.S3Repository.MAX_FILE_SIZE_USING_MULTIPART;
@@ -127,12 +131,13 @@ public void deleteBlobsIgnoringIfNotExists(List<String> blobNames) throws IOExce
127131
if (blobNames.isEmpty()) {
128132
return;
129133
}
134+
final Set<String> outstanding = blobNames.stream().map(this::buildKey).collect(Collectors.toSet());
130135
try (AmazonS3Reference clientReference = blobStore.clientReference()) {
131136
// S3 API only allows 1k blobs per delete so we split up the given blobs into requests of max. 1k deletes
132137
final List<DeleteObjectsRequest> deleteRequests = new ArrayList<>();
133138
final List<String> partition = new ArrayList<>();
134-
for (String blob : blobNames) {
135-
partition.add(buildKey(blob));
139+
for (String key : outstanding) {
140+
partition.add(key);
136141
if (partition.size() == MAX_BULK_DELETES ) {
137142
deleteRequests.add(bulkDelete(blobStore.bucket(), partition));
138143
partition.clear();
@@ -144,23 +149,32 @@ public void deleteBlobsIgnoringIfNotExists(List<String> blobNames) throws IOExce
144149
SocketAccess.doPrivilegedVoid(() -> {
145150
AmazonClientException aex = null;
146151
for (DeleteObjectsRequest deleteRequest : deleteRequests) {
152+
List<String> keysInRequest =
153+
deleteRequest.getKeys().stream().map(DeleteObjectsRequest.KeyVersion::getKey).collect(Collectors.toList());
147154
try {
148155
clientReference.client().deleteObjects(deleteRequest);
156+
outstanding.removeAll(keysInRequest);
157+
} catch (MultiObjectDeleteException e) {
158+
// We are sending quiet mode requests so we can't use the deleted keys entry on the exception and instead
159+
// first remove all keys that were sent in the request and then add back those that ran into an exception.
160+
outstanding.removeAll(keysInRequest);
161+
outstanding.addAll(
162+
e.getErrors().stream().map(MultiObjectDeleteException.DeleteError::getKey).collect(Collectors.toSet()));
163+
aex = ExceptionsHelper.useOrSuppress(aex, e);
149164
} catch (AmazonClientException e) {
150-
if (aex == null) {
151-
aex = e;
152-
} else {
153-
aex.addSuppressed(e);
154-
}
165+
// The AWS client threw any unexpected exception and did not execute the request at all so we do not
166+
// remove any keys from the outstanding deletes set.
167+
aex = ExceptionsHelper.useOrSuppress(aex, e);
155168
}
156169
}
157170
if (aex != null) {
158171
throw aex;
159172
}
160173
});
161-
} catch (final AmazonClientException e) {
162-
throw new IOException("Exception when deleting blobs [" + blobNames + "]", e);
174+
} catch (Exception e) {
175+
throw new IOException("Failed to delete blobs [" + outstanding + "]", e);
163176
}
177+
assert outstanding.isEmpty();
164178
}
165179

166180
private static DeleteObjectsRequest bulkDelete(String bucket, List<String> blobs) {

server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -998,8 +998,8 @@ protected void finalize(final List<SnapshotFiles> snapshots,
998998
try {
999999
blobContainer.deleteBlobsIgnoringIfNotExists(blobNames);
10001000
} catch (IOException e) {
1001-
logger.warn(() -> new ParameterizedMessage("[{}][{}] failed to delete index blobs {} during finalization",
1002-
snapshotId, shardId, blobNames), e);
1001+
logger.warn(() -> new ParameterizedMessage("[{}][{}] failed to delete index blobs during finalization",
1002+
snapshotId, shardId), e);
10031003
throw e;
10041004
}
10051005

@@ -1014,8 +1014,8 @@ protected void finalize(final List<SnapshotFiles> snapshots,
10141014
try {
10151015
blobContainer.deleteBlobsIgnoringIfNotExists(indexBlobs);
10161016
} catch (IOException e) {
1017-
logger.warn(() -> new ParameterizedMessage("[{}][{}] failed to delete index blobs {} during finalization",
1018-
snapshotId, shardId, indexBlobs), e);
1017+
logger.warn(() -> new ParameterizedMessage("[{}][{}] failed to delete index blobs during finalization",
1018+
snapshotId, shardId), e);
10191019
throw e;
10201020
}
10211021

@@ -1027,8 +1027,8 @@ protected void finalize(final List<SnapshotFiles> snapshots,
10271027
try {
10281028
blobContainer.deleteBlobsIgnoringIfNotExists(orphanedBlobs);
10291029
} catch (IOException e) {
1030-
logger.warn(() -> new ParameterizedMessage("[{}][{}] failed to delete data blobs {} during finalization",
1031-
snapshotId, shardId, orphanedBlobs), e);
1030+
logger.warn(() -> new ParameterizedMessage("[{}][{}] failed to delete data blobs during finalization",
1031+
snapshotId, shardId), e);
10321032
}
10331033
} catch (IOException e) {
10341034
String message =

0 commit comments

Comments
 (0)