Skip to content

Commit

Permalink
Retry the tryDelete and tryInfo operations on storage buckets. (dart-…
Browse files Browse the repository at this point in the history
  • Loading branch information
isoos authored Dec 6, 2024
1 parent 769c959 commit 90a8fe9
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 42 deletions.
83 changes: 43 additions & 40 deletions app/lib/shared/storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,34 @@ extension StorageExt on Storage {
extension BucketExt on Bucket {
/// Returns an [ObjectInfo] if [name] exists, `null` otherwise.
Future<ObjectInfo?> tryInfo(String name) async {
try {
return await info(name);
} on DetailedApiRequestError catch (e) {
if (e.status == 404) return null;
rethrow;
}
return await retry(
() async {
try {
return await info(name);
} on DetailedApiRequestError catch (e) {
if (e.status == 404) return null;
rethrow;
}
},
maxAttempts: 3,
retryIf: _retryIf,
);
}

/// Deletes [name] if it exists, ignores 404 otherwise.
Future<void> tryDelete(String name) async {
try {
return await delete(name);
} on DetailedApiRequestError catch (e) {
if (e.status == 404) return null;
rethrow;
}
return await retry(
() async {
try {
return await delete(name);
} on DetailedApiRequestError catch (e) {
if (e.status == 404) return null;
rethrow;
}
},
maxAttempts: 3,
retryIf: _retryIf,
);
}

Future uploadPublic(String objectName, int length,
Expand Down Expand Up @@ -139,22 +151,7 @@ extension BucketExt on Bucket {
return builder.toBytes();
},
maxAttempts: 3,
retryIf: (e) {
if (e is TimeoutException) {
return true; // Timeouts we can retry
}
if (e is IOException) {
return true; // I/O issues are worth retrying
}
if (e is http.ClientException) {
return true; // HTTP issues are worth retrying
}
if (e is DetailedApiRequestError) {
final status = e.status;
return status == null || status >= 500; // 5xx errors are retried
}
return e is ApiRequestError; // Unknown API errors are retried
},
retryIf: _retryIf,
);
}

Expand All @@ -164,6 +161,23 @@ extension BucketExt on Bucket {
}
}

bool _retryIf(Exception e) {
if (e is TimeoutException) {
return true; // Timeouts we can retry
}
if (e is IOException) {
return true; // I/O issues are worth retrying
}
if (e is http.ClientException) {
return true; // HTTP issues are worth retrying
}
if (e is DetailedApiRequestError) {
final status = e.status;
return status == null || status >= 500; // 5xx errors are retried
}
return e is ApiRequestError; // Unknown API errors are retried
}

/// Returns a valid `gs://` URI for a given [bucket] + [path] combination.
String bucketUri(Bucket bucket, String path) =>
'gs://${bucket.bucketName}/$path';
Expand Down Expand Up @@ -264,18 +278,7 @@ Future uploadWithRetry(Bucket bucket, String objectName, int length,
await sink.close();
},
description: 'Upload to $objectName',
shouldRetryOnError: (e) {
// upstream proxy or rate limit issue
if (e is DetailedApiRequestError) {
return _retryStatusCodes.contains(e.status);
}
// network connection failures
if (e is http.ClientException || e is SocketException) {
return true;
}
// otherwise no retry
return false;
},
shouldRetryOnError: _retryIf,
sleep: Duration(seconds: 10),
);
}
Expand Down
4 changes: 2 additions & 2 deletions app/lib/shared/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,14 @@ List<T> boundedList<T>(List<T> list, {int? offset, int? limit}) {
Future<R> retryAsync<R>(
Future<R> Function() body, {
int maxAttempt = 3,
bool Function(Object)? shouldRetryOnError,
bool Function(Exception)? shouldRetryOnError,
String description = 'Async operation',
Duration sleep = const Duration(seconds: 1),
}) async {
for (int i = 1;; i++) {
try {
return await body();
} catch (e, st) {
} on Exception catch (e, st) {
_logger.info('$description failed (attempt: $i of $maxAttempt).', e, st);
if (i < maxAttempt &&
(shouldRetryOnError == null || shouldRetryOnError(e))) {
Expand Down

0 comments on commit 90a8fe9

Please sign in to comment.