Skip to content

Commit 4b9f2e9

Browse files
authored
Increase Azure client timeout on tests (#67210)
Additionally, this commit improves the error messages provided as previously we weren't including the blob name on deletion failures. Closes #67119
1 parent 085a288 commit 4b9f2e9

File tree

3 files changed

+71
-59
lines changed

3 files changed

+71
-59
lines changed

plugins/repository-azure/src/internalClusterTest/java/org/elasticsearch/repositories/azure/AzureBlobStoreRepositoryTests.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
import java.util.regex.Pattern;
5151

5252
import static org.hamcrest.Matchers.anEmptyMap;
53-
import static org.hamcrest.Matchers.equalTo;
5453
import static org.hamcrest.Matchers.is;
5554

5655
@SuppressForbidden(reason = "this test uses a HttpServer to emulate an Azure endpoint")
@@ -124,8 +123,8 @@ AzureStorageService createAzureStorageService(Settings settings, AzureClientProv
124123
@Override
125124
RequestRetryOptions getRetryOptions(LocationMode locationMode, AzureStorageSettings azureStorageSettings) {
126125
return new RequestRetryOptions(RetryPolicyType.EXPONENTIAL,
127-
azureStorageSettings.getMaxRetries() + 1, 5,
128-
1L, 15L, null);
126+
azureStorageSettings.getMaxRetries() + 1, 60,
127+
50L, 100L, null);
129128
}
130129

131130
@Override
@@ -222,7 +221,6 @@ private boolean isPutBlockList(String request) {
222221
}
223222
}
224223

225-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/67119")
226224
public void testLargeBlobCountDeletion() throws Exception {
227225
int numberOfBlobs = randomIntBetween(257, 2000);
228226
try (BlobStore store = newBlobStore()) {
@@ -234,7 +232,7 @@ public void testLargeBlobCountDeletion() throws Exception {
234232
}
235233

236234
container.delete();
237-
assertThat(container.listBlobs().size(), equalTo(0));
235+
assertThat(container.listBlobs(), is(anEmptyMap()));
238236
}
239237
}
240238

plugins/repository-azure/src/main/java/org/elasticsearch/repositories/azure/AzureBlobStore.java

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import java.util.concurrent.atomic.AtomicLong;
8282
import java.util.function.BiConsumer;
8383
import java.util.function.BiPredicate;
84+
import java.util.function.Supplier;
8485

8586
public class AzureBlobStore implements BlobStore {
8687
private static final Logger logger = LogManager.getLogger(AzureBlobStore.class);
@@ -227,40 +228,41 @@ public DeleteResult deleteBlobDirectory(String path) throws IOException {
227228
final AtomicInteger blobsDeleted = new AtomicInteger(0);
228229
final AtomicLong bytesDeleted = new AtomicLong(0);
229230

230-
try {
231-
final BlobServiceClient client = client();
232-
SocketAccess.doPrivilegedVoidException(() -> {
233-
final BlobContainerClient blobContainerClient = client.getBlobContainerClient(container);
234-
final BlobContainerAsyncClient blobContainerAsyncClient = asyncClient().getBlobContainerAsyncClient(container);
235-
final Queue<String> directories = new ArrayDeque<>();
236-
directories.offer(path);
237-
String directoryName;
238-
List<Mono<Void>> deleteTasks = new ArrayList<>();
239-
while ((directoryName = directories.poll()) != null) {
240-
final BlobListDetails blobListDetails = new BlobListDetails()
241-
.setRetrieveMetadata(true);
242-
243-
final ListBlobsOptions options = new ListBlobsOptions()
244-
.setPrefix(directoryName)
245-
.setDetails(blobListDetails);
246-
247-
for (BlobItem blobItem : blobContainerClient.listBlobsByHierarchy("/", options, null)) {
248-
if (blobItem.isPrefix() != null && blobItem.isPrefix()) {
249-
directories.offer(blobItem.getName());
250-
} else {
251-
BlobAsyncClient blobAsyncClient = blobContainerAsyncClient.getBlobAsyncClient(blobItem.getName());
252-
deleteTasks.add(blobAsyncClient.delete());
253-
bytesDeleted.addAndGet(blobItem.getProperties().getContentLength());
254-
blobsDeleted.incrementAndGet();
255-
}
231+
final BlobServiceClient client = client();
232+
SocketAccess.doPrivilegedVoidException(() -> {
233+
final BlobContainerClient blobContainerClient = client.getBlobContainerClient(container);
234+
final BlobContainerAsyncClient blobContainerAsyncClient = asyncClient().getBlobContainerAsyncClient(container);
235+
final Queue<String> directories = new ArrayDeque<>();
236+
directories.offer(path);
237+
String directoryName;
238+
List<Mono<Void>> deleteTasks = new ArrayList<>();
239+
while ((directoryName = directories.poll()) != null) {
240+
final BlobListDetails blobListDetails = new BlobListDetails()
241+
.setRetrieveMetadata(true);
242+
243+
final ListBlobsOptions options = new ListBlobsOptions()
244+
.setPrefix(directoryName)
245+
.setDetails(blobListDetails);
246+
247+
for (BlobItem blobItem : blobContainerClient.listBlobsByHierarchy("/", options, null)) {
248+
if (blobItem.isPrefix() != null && blobItem.isPrefix()) {
249+
directories.offer(blobItem.getName());
250+
} else {
251+
BlobAsyncClient blobAsyncClient = blobContainerAsyncClient.getBlobAsyncClient(blobItem.getName());
252+
final Mono<Void> deleteTask = blobAsyncClient.delete()
253+
// Ignore not found blobs, as it's possible that due to network errors a request
254+
// for an already deleted blob is retried, causing an error.
255+
.onErrorResume(this::isNotFoundError, throwable -> Mono.empty())
256+
.onErrorMap(throwable -> new IOException("Error deleting blob " + blobItem.getName(), throwable));
257+
deleteTasks.add(deleteTask);
258+
bytesDeleted.addAndGet(blobItem.getProperties().getContentLength());
259+
blobsDeleted.incrementAndGet();
256260
}
257261
}
262+
}
258263

259-
executeDeleteTasks(deleteTasks);
260-
});
261-
} catch (Exception e) {
262-
throw new IOException("Deleting directory [" + path + "] failed", e);
263-
}
264+
executeDeleteTasks(deleteTasks, () -> "Deleting directory [" + path + "] failed");
265+
});
264266

265267
return new DeleteResult(blobsDeleted.get(), bytesDeleted.get());
266268
}
@@ -270,31 +272,43 @@ void deleteBlobList(List<String> blobs) throws IOException {
270272
return;
271273
}
272274

273-
try {
274-
BlobServiceAsyncClient asyncClient = asyncClient();
275-
SocketAccess.doPrivilegedVoidException(() -> {
276-
List<Mono<Void>> deleteTasks = new ArrayList<>(blobs.size());
277-
final BlobContainerAsyncClient blobContainerClient = asyncClient.getBlobContainerAsyncClient(container);
278-
for (String blob : blobs) {
279-
final Mono<Void> deleteTask = blobContainerClient.getBlobAsyncClient(blob)
280-
.delete()
281-
// Ignore not found blobs
282-
.onErrorResume(e -> (e instanceof BlobStorageException) && ((BlobStorageException) e).getStatusCode() == 404,
283-
throwable -> Mono.empty());
284-
deleteTasks.add(deleteTask);
285-
}
275+
BlobServiceAsyncClient asyncClient = asyncClient();
276+
SocketAccess.doPrivilegedVoidException(() -> {
277+
List<Mono<Void>> deleteTasks = new ArrayList<>(blobs.size());
278+
final BlobContainerAsyncClient blobContainerClient = asyncClient.getBlobContainerAsyncClient(container);
279+
for (String blob : blobs) {
280+
final Mono<Void> deleteTask = blobContainerClient.getBlobAsyncClient(blob)
281+
.delete()
282+
// Ignore not found blobs
283+
.onErrorResume(this::isNotFoundError, throwable -> Mono.empty())
284+
.onErrorMap(throwable -> new IOException("Error deleting blob " + blob, throwable));
285+
286+
deleteTasks.add(deleteTask);
287+
}
286288

287-
executeDeleteTasks(deleteTasks);
288-
});
289-
} catch (Exception e) {
290-
throw new IOException("Unable to delete blobs " + blobs, e);
291-
}
289+
executeDeleteTasks(deleteTasks, () -> "Unable to delete blobs " + blobs);
290+
});
292291
}
293292

294-
private void executeDeleteTasks(List<Mono<Void>> deleteTasks) {
295-
// zipDelayError executes all tasks in parallel and delays
296-
// error propagation until all tasks have finished.
297-
Mono.zipDelayError(deleteTasks, results -> null).block();
293+
private boolean isNotFoundError(Throwable e) {
294+
return e instanceof BlobStorageException && ((BlobStorageException) e).getStatusCode() == 404;
295+
}
296+
297+
private void executeDeleteTasks(List<Mono<Void>> deleteTasks, Supplier<String> errorMessageSupplier) throws IOException {
298+
try {
299+
// zipDelayError executes all tasks in parallel and delays
300+
// error propagation until all tasks have finished.
301+
Mono.zipDelayError(deleteTasks, results -> null).block();
302+
} catch (Exception e) {
303+
final IOException exception = new IOException(errorMessageSupplier.get());
304+
for (Throwable suppressed : e.getSuppressed()) {
305+
// We're only interested about the blob deletion exceptions and not in the reactor internals exceptions
306+
if (suppressed instanceof IOException) {
307+
exception.addSuppressed(suppressed);
308+
}
309+
}
310+
throw exception;
311+
}
298312
}
299313

300314
public InputStream getInputStream(String blob, long position, final @Nullable Long length) throws IOException {

plugins/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureBlobContainerRetriesTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ private BlobContainer createBlobContainer(final int maxRetries, String secondary
153153
RequestRetryOptions getRetryOptions(LocationMode locationMode, AzureStorageSettings azureStorageSettings) {
154154
return new RequestRetryOptions(RetryPolicyType.EXPONENTIAL,
155155
maxRetries + 1,
156-
1,
156+
60,
157157
50L,
158158
100L,
159159
// The SDK doesn't work well with ip endponts. Secondary host endpoints that contain

0 commit comments

Comments
 (0)