diff --git a/app/lib/package/api_export/exported_api.dart b/app/lib/package/api_export/exported_api.dart index f88ad7dcd5..eedf540452 100644 --- a/app/lib/package/api_export/exported_api.dart +++ b/app/lib/package/api_export/exported_api.dart @@ -121,7 +121,7 @@ final class ExportedApi { // Only delete the item if it's older than _minGarbageAge // This avoids any races where we delete files we've just created // TODO: Conditionally deletion API from package:gcloud would be better! - await _bucket.tryDeleteWithRetry(item.name); + await _bucket.deleteWithRetry(item.name); } }); @@ -137,7 +137,7 @@ final class ExportedApi { item.updated.isBefore(gcFilesBefore)) { // Only delete the item if it's older than _minGarbageAge // This avoids any races where we delete files we've just created - await _bucket.tryDeleteWithRetry(item.name); + await _bucket.deleteWithRetry(item.name); } }); } @@ -184,7 +184,7 @@ final class ExportedApi { await _listBucket( prefix: entry.name, delimiter: '', - (entry) async => await _bucket.tryDeleteWithRetry(entry.name), + (entry) async => await _bucket.deleteWithRetry(entry.name), ); } })); @@ -336,7 +336,7 @@ final class ExportedPackage { item.updated.isBefore(clock.agoBy(_minGarbageAge))) { // Only delete if the item if it's older than _minGarbageAge // This avoids any races where we delete files we've just created - await _owner._bucket.tryDeleteWithRetry(item.name); + await _owner._bucket.deleteWithRetry(item.name); } }); @@ -380,7 +380,7 @@ final class ExportedPackage { if (info.updated.isBefore(clock.agoBy(_minGarbageAge))) { // Only delete if the item if it's older than _minGarbageAge // This avoids any races where we delete files we've just created - await _owner._bucket.tryDeleteWithRetry(item.name); + await _owner._bucket.deleteWithRetry(item.name); } } // Ignore cases where tryInfo fails, assuming the object has been @@ -399,7 +399,7 @@ final class ExportedPackage { await _owner._listBucket( prefix: prefix + '/api/archives/$_package-', delimiter: '', - (item) async => await _owner._bucket.tryDeleteWithRetry(item.name), + (item) async => await _owner._bucket.deleteWithRetry(item.name), ); }), ]); @@ -442,7 +442,7 @@ sealed class ExportedObject { Future delete() async { await Future.wait(_owner._prefixes.map((prefix) async { await _owner._pool.withResource(() async { - await _owner._bucket.tryDeleteWithRetry(prefix + _objectName); + await _owner._bucket.deleteWithRetry(prefix + _objectName); }); })); } diff --git a/app/lib/shared/storage.dart b/app/lib/shared/storage.dart index 8ceb20e22d..1b26cb19ec 100644 --- a/app/lib/shared/storage.dart +++ b/app/lib/shared/storage.dart @@ -103,20 +103,17 @@ extension BucketExt on Bucket { } /// Delete an object with default retry. + /// + /// Ignores 404 responses, as it may happen if the first request completes + /// on the server while unsuccessful on the client. Future deleteWithRetry(String name) async { - return await _retry(() => delete(name)); - } - - /// Deletes [name] if it exists, ignores 404 otherwise. - Future tryDeleteWithRetry(String name) async { return await _retry( () async { try { await delete(name); - return true; } on DetailedApiRequestError catch (e) { if (e.status == 404) { - return false; + return; } rethrow; } @@ -338,8 +335,8 @@ Future deleteBucketFolderRecursively( final pool = Pool(concurrency ?? 1); for (final entry in page!.items) { final f = pool.withResource(() async { - final deleted = await bucket.tryDeleteWithRetry(entry.name); - if (deleted) count++; + await bucket.deleteWithRetry(entry.name); + count++; }); futures.add(f); } @@ -477,7 +474,7 @@ class VersionedJsonStorage { final age = clock.now().difference(info.updated); if (minAgeThreshold == null || age > minAgeThreshold) { deleted++; - await _bucket.tryDeleteWithRetry(entry.name); + await _bucket.deleteWithRetry(entry.name); } } });