gianm commented on code in PR #18891:
URL: https://github.com/apache/druid/pull/18891#discussion_r2912914607


##########
extensions-core/s3-extensions/src/test/java/org/apache/druid/storage/s3/S3DataSegmentKillerTest.java:
##########
@@ -383,22 +392,26 @@ public void 
test_kill_listOfSegments_multiDeleteExceptionIsThrown()
   @Test
   public void 
test_kill_listOfSegments_multiDeleteExceptionIsThrownMultipleTimes()
   {
-    DeleteObjectsRequest deleteObjectsRequest = new 
DeleteObjectsRequest(TEST_BUCKET);

Review Comment:
   Same issue as `test_kill_listOfSegments_multiDeleteExceptionIsThrown`. We 
should use a regular `DeleteObjectsResponse` with errors set.



##########
extensions-core/s3-extensions/src/main/java/org/apache/druid/storage/s3/S3DataSegmentKiller.java:
##########
@@ -147,65 +152,69 @@ public void kill(List<DataSegment> segments) throws 
SegmentLoadingException
   private boolean deleteKeysForBucket(
       ServerSideEncryptingAmazonS3 s3Client,
       String s3Bucket,
-      List<DeleteObjectsRequest.KeyVersion> keysToDelete
+      List<ObjectIdentifier> keysToDelete
   )
   {
     boolean hadException = false;
-    DeleteObjectsRequest deleteObjectsRequest = new 
DeleteObjectsRequest(s3Bucket);
-    deleteObjectsRequest.setQuiet(true);
-    List<List<DeleteObjectsRequest.KeyVersion>> keysChunks = Lists.partition(
+    List<List<ObjectIdentifier>> keysChunks = Lists.partition(
         keysToDelete,
         MAX_MULTI_OBJECT_DELETE_SIZE
     );
-    for (List<DeleteObjectsRequest.KeyVersion> chunkOfKeys : keysChunks) {
+    for (List<ObjectIdentifier> chunkOfKeys : keysChunks) {
       List<String> keysToDeleteStrings = chunkOfKeys.stream().map(
-          
DeleteObjectsRequest.KeyVersion::getKey).collect(Collectors.toList());
+          ObjectIdentifier::key).collect(Collectors.toList());
       try {
-        deleteObjectsRequest.setKeys(chunkOfKeys);
+        DeleteObjectsRequest deleteObjectsRequest = 
DeleteObjectsRequest.builder()
+            .bucket(s3Bucket)
+            .delete(Delete.builder()
+                .objects(chunkOfKeys)
+                .quiet(true)
+                .build())
+            .build();
         log.info(
             "Deleting the following segment files from S3 bucket[%s]: [%s]",
             s3Bucket,
             keysToDeleteStrings
         );
         S3Utils.retryS3Operation(
             () -> {
-              s3Client.deleteObjects(deleteObjectsRequest);
+              DeleteObjectsResponse response = 
s3Client.deleteObjects(deleteObjectsRequest);
+              // Check for errors in the response
+              if (response.hasErrors()) {

Review Comment:
   The old code for `deleteKeysForBucket`, if there was an error in a 
multi-delete, would set `hadException = true` and then return `true` (meaning 
there was an issue deleting keys). That would then cause the caller (`kill`) to 
throw a `SegmentLoadingException`. Now, the exception is suppressed and `kill` 
will treat it as a success. Please restore the old behavior.
   
   There's a retry issue too. The old code would sometimes retry 
`MultiObjectDeleteException` (see my comment in `S3Utils`) due to some logic 
for retrying it in `retryS3Operation`. The new code does not retry, just 
silently treats it as a success (after logging some warnings).
   
   It may be best to focus on fixing `S3Utils.deleteBucketKeys` and then use 
that here. Then we only need to get the error/retry logic right in one place.



##########
extensions-core/s3-extensions/src/main/java/org/apache/druid/storage/s3/S3Utils.java:
##########
@@ -328,18 +418,21 @@ public static void deleteObjectsInPath(
   public static void deleteBucketKeys(
       ServerSideEncryptingAmazonS3 s3Client,
       String bucket,
-      List<DeleteObjectsRequest.KeyVersion> keysToDelete,
+      List<ObjectIdentifier> keysToDelete,
       int retries
   )
       throws Exception
   {
     if (keysToDelete != null && log.isDebugEnabled()) {
       List<String> keys = keysToDelete.stream()
-                                      
.map(DeleteObjectsRequest.KeyVersion::getKey)
+                                      .map(ObjectIdentifier::key)
                                       .collect(Collectors.toList());
       log.debug("Deleting keys from bucket: [%s], keys: [%s]", bucket, keys);
     }
-    DeleteObjectsRequest deleteRequest = new 
DeleteObjectsRequest(bucket).withKeys(keysToDelete);
+    DeleteObjectsRequest deleteRequest = DeleteObjectsRequest.builder()
+        .bucket(bucket)
+        .delete(Delete.builder().objects(keysToDelete).build())
+        .build();
     S3Utils.retryS3Operation(() -> {
       s3Client.deleteObjects(deleteRequest);

Review Comment:
   Need to check `hasErrors` in the response. Related: the old `S3RETRY` 
checked for `MultiObjectDeleteException` and would retry in certain cases. 
That's lost now, please restore it. The best way I can think of to do that is 
to introduce our own version of `MultiObjectDeleteException` and throw it when 
`hasErrors` is true, and then add our version to `S3RETRY`.
   
   Please add a test for this scenario too.
   
   For extra credit only retry the objects that failed to be deleted!



##########
extensions-core/s3-extensions/src/test/java/org/apache/druid/storage/s3/S3DataSegmentKillerTest.java:
##########
@@ -359,16 +365,19 @@ public void test_kill_listOfSegments() throws 
SegmentLoadingException
   @Test
   public void test_kill_listOfSegments_multiDeleteExceptionIsThrown()
   {
-    DeleteObjectsRequest deleteObjectsRequest = new 
DeleteObjectsRequest(TEST_BUCKET);
-    deleteObjectsRequest.withKeys(KEY_1_PATH, KEY_2_PATH);
     // struggled with the idea of making it match on equaling this
     s3Client.deleteObjects(EasyMock.anyObject(DeleteObjectsRequest.class));
-    MultiObjectDeleteException.DeleteError deleteError = new 
MultiObjectDeleteException.DeleteError();
-    deleteError.setKey(KEY_1_PATH);
-    MultiObjectDeleteException multiObjectDeleteException = new 
MultiObjectDeleteException(
-        ImmutableList.of(deleteError),
-        ImmutableList.of());
-    EasyMock.expectLastCall().andThrow(multiObjectDeleteException).once();
+    // In v2 SDK, multi-object delete errors are returned in the response, not 
thrown as exceptions
+    // For testing purposes, we simulate an S3Exception being thrown
+    S3Exception s3Exception = (S3Exception) S3Exception.builder()

Review Comment:
   This isn't right, we should test what the v2 SDK actually does. The test 
should use a regular `DeleteObjectsResponse` with errors set.



##########
extensions-core/s3-extensions/src/test/java/org/apache/druid/storage/s3/S3DataSegmentKillerTest.java:
##########
@@ -438,20 +453,20 @@ public void 
test_kill_listOfSegments_amazonServiceExceptionExceptionIsThrown()
   @Test
   public void test_kill_listOfSegments_retryableExceptionThrown() throws 
SegmentLoadingException

Review Comment:
   This test was formerly testing specifically the partial failure case. Please 
add back a test that focuses on this case. In v2 it will show up as `hasErrors` 
on the response.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to