|
92 | 92 | import io.minio.messages.AccessControlPolicy; |
93 | 93 | import io.minio.messages.CORSConfiguration; |
94 | 94 | import io.minio.messages.DeleteRequest; |
| 95 | +import io.minio.messages.DeleteResult; |
95 | 96 | import io.minio.messages.ErrorResponse; |
96 | 97 | import io.minio.messages.EventType; |
97 | 98 | import io.minio.messages.Filter; |
|
121 | 122 | import java.util.Arrays; |
122 | 123 | import java.util.Collections; |
123 | 124 | import java.util.HashMap; |
| 125 | +import java.util.HashSet; |
124 | 126 | import java.util.List; |
125 | 127 | import java.util.Map; |
| 128 | +import java.util.Set; |
126 | 129 | import java.util.concurrent.TimeUnit; |
127 | 130 | import java.util.stream.Collectors; |
128 | 131 | import okhttp3.Headers; |
|
136 | 139 | @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( |
137 | 140 | value = {"THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION", "REC_CATCH_EXCEPTION"}) |
138 | 141 | public class TestMinioClient extends TestArgs { |
| 142 | + private static final int MAX_DELETE_ATTEMPTS = 5; |
| 143 | + private static final Set<String> TRANSIENT_DELETE_CODES = |
| 144 | + Collections.unmodifiableSet( |
| 145 | + new HashSet<>( |
| 146 | + Arrays.asList("InternalError", "RequestTimeout", "ServiceUnavailable", "SlowDown"))); |
| 147 | + |
139 | 148 | private String bucketName = getRandomName(); |
140 | 149 | private String bucketNameWithLock = getRandomName(); |
141 | 150 | public boolean isQuickTest; |
@@ -1029,15 +1038,46 @@ public List<ObjectWriteResponse> createObjects(String bucketName, int count, int |
1029 | 1038 | public void removeObjects(String bucketName, List<ObjectWriteResponse> results) throws Exception { |
1030 | 1039 | List<DeleteRequest.Object> objects = |
1031 | 1040 | results.stream() |
1032 | | - .map( |
1033 | | - result -> { |
1034 | | - return new DeleteRequest.Object(result.object(), result.versionId()); |
1035 | | - }) |
| 1041 | + .map(r -> new DeleteRequest.Object(r.object(), r.versionId())) |
1036 | 1042 | .collect(Collectors.toList()); |
1037 | | - for (Result<?> r : |
1038 | | - client.removeObjects( |
1039 | | - RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build())) { |
1040 | | - ignore(r.get()); |
| 1043 | + boolean anyTransient = false; |
| 1044 | + for (int attempt = 0; attempt < MAX_DELETE_ATTEMPTS; attempt++) { |
| 1045 | + if (attempt > 0) { |
| 1046 | + try { |
| 1047 | + Thread.sleep(500L << attempt); // 1s / 2s / 4s / 8s |
| 1048 | + } catch (InterruptedException ie) { |
| 1049 | + Thread.currentThread().interrupt(); |
| 1050 | + throw ie; |
| 1051 | + } |
| 1052 | + } |
| 1053 | + anyTransient = false; |
| 1054 | + for (Result<DeleteResult.Error> r : |
| 1055 | + client.removeObjects( |
| 1056 | + RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build())) { |
| 1057 | + DeleteResult.Error err = r.get(); |
| 1058 | + String code = err.code(); |
| 1059 | + if (!TRANSIENT_DELETE_CODES.contains(code)) { |
| 1060 | + throw new Exception( |
| 1061 | + "non-transient delete error '" |
| 1062 | + + code |
| 1063 | + + "': " |
| 1064 | + + err.message() |
| 1065 | + + " on " |
| 1066 | + + err.objectName() |
| 1067 | + + " in bucket " |
| 1068 | + + bucketName); |
| 1069 | + } |
| 1070 | + anyTransient = true; |
| 1071 | + } |
| 1072 | + if (!anyTransient) break; |
| 1073 | + } |
| 1074 | + if (anyTransient) { |
| 1075 | + throw new Exception( |
| 1076 | + results.size() |
| 1077 | + + " object(s) not deleted after " |
| 1078 | + + MAX_DELETE_ATTEMPTS |
| 1079 | + + " attempts in bucket " |
| 1080 | + + bucketName); |
1041 | 1081 | } |
1042 | 1082 | } |
1043 | 1083 |
|
@@ -1190,8 +1230,6 @@ public void testRemoveObjects(String testTags, List<ObjectWriteResponse> results |
1190 | 1230 | mintSuccessLog(methodName, testTags, startTime); |
1191 | 1231 | } catch (Exception e) { |
1192 | 1232 | handleException(methodName, testTags, startTime, e); |
1193 | | - } finally { |
1194 | | - removeObjects(bucketName, results); |
1195 | 1233 | } |
1196 | 1234 | } |
1197 | 1235 |
|
|
0 commit comments