Skip to content

Commit 6352f10

Browse files
authored
retry more operations when accessing storage + enforcer in tests (#8412)
1 parent b1cecfe commit 6352f10

File tree

7 files changed

+314
-11
lines changed

7 files changed

+314
-11
lines changed

app/lib/package/api_export/exported_api.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ final class ExportedApi {
229229
required String prefix,
230230
required String delimiter,
231231
}) async {
232-
var p = await _pool.withResource(() async => await _bucket.page(
232+
var p = await _pool.withResource(() async => await _bucket.pageWithRetry(
233233
prefix: prefix,
234234
delimiter: delimiter,
235235
pageSize: 1000,
@@ -240,7 +240,8 @@ final class ExportedApi {
240240
}));
241241

242242
if (p.isLast) break;
243-
p = await _pool.withResource(() async => await p.next(pageSize: 1000));
243+
p = await _pool
244+
.withResource(() async => await p.nextWithRetry(pageSize: 1000));
244245
}
245246
}
246247
}
@@ -632,7 +633,7 @@ final class ExportedBlob extends ExportedObject {
632633
final retouchDeadline = clock.agoBy(_updateValidatedAfter);
633634
if (destinationInfo.metadata.validated.isBefore(retouchDeadline)) {
634635
try {
635-
await _owner._bucket.updateMetadata(dst, _metadata());
636+
await _owner._bucket.updateMetadataWithRetry(dst, _metadata());
636637
} catch (e, st) {
637638
// This shouldn't happen, but if a metadata update does fail, it's
638639
// hardly the end of the world.
@@ -646,7 +647,7 @@ final class ExportedBlob extends ExportedObject {
646647

647648
// If dst or source doesn't exist, then we shall attempt to make a copy.
648649
// (if source doesn't exist we'll consistently get an error from here!)
649-
await _owner._storage.copyObject(
650+
await _owner._storage.copyObjectWithRetry(
650651
source.absoluteObjectName,
651652
_owner._bucket.absoluteObjectName(dst),
652653
metadata: _metadata(),
@@ -667,7 +668,7 @@ extension on Bucket {
667668
if (info.metadata.validated
668669
.isBefore(clock.agoBy(_updateValidatedAfter))) {
669670
try {
670-
await updateMetadata(name, metadata);
671+
await updateMetadataWithRetry(name, metadata);
671672
} catch (e, st) {
672673
// This shouldn't happen, but if a metadata update does fail, it's
673674
// hardly the end of the world.

app/lib/package/backend.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,7 @@ class PackageBackend {
996996
_logger.info('Removing temporary object $guid.');
997997

998998
sw.reset();
999-
await _incomingBucket.delete(tmpObjectName(guid));
999+
await _incomingBucket.deleteWithRetry(tmpObjectName(guid));
10001000
_logger.info('Temporary object removed in ${sw.elapsed}.');
10011001
return version;
10021002
});

app/lib/service/download_counts/backend.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:pub_dev/shared/cached_value.dart';
1515
import 'package:pub_dev/shared/configuration.dart';
1616
import 'package:pub_dev/shared/datastore.dart';
1717
import 'package:pub_dev/shared/redis_cache.dart';
18+
import 'package:pub_dev/shared/storage.dart';
1819

1920
/// Sets the download counts backend service.
2021
void registerDownloadCountsBackend(DownloadCountsBackend backend) =>
@@ -42,7 +43,7 @@ class DownloadCountsBackend {
4243
try {
4344
final info = await storageService
4445
.bucket(activeConfiguration.reportsBucketName!)
45-
.info(downloadCounts30DaysTotalsFileName);
46+
.infoWithRetry(downloadCounts30DaysTotalsFileName);
4647

4748
if (_lastData.etag == info.etag) {
4849
return _lastData.data;

app/lib/service/services.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:appengine/appengine.dart';
99
import 'package:clock/clock.dart';
1010
import 'package:fake_gcloud/mem_datastore.dart';
1111
import 'package:fake_gcloud/mem_storage.dart';
12+
import 'package:fake_gcloud/retry_enforcer_storage.dart';
1213
import 'package:gcloud/service_scope.dart';
1314
import 'package:gcloud/storage.dart';
1415
import 'package:googleapis_auth/auth_io.dart' as auth;
@@ -148,7 +149,7 @@ Future<R> withFakeServices<R>({
148149
return await fork(() async {
149150
register(#appengine.context, FakeClientContext());
150151
registerDbService(DatastoreDB(datastore!));
151-
registerStorageService(storage!);
152+
registerStorageService(RetryEnforcerStorage(storage!));
152153
IOServer? frontendServer;
153154
IOServer? searchServer;
154155
if (configuration == null) {

app/lib/shared/storage.dart

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extension StorageExt on Storage {
4444
/// local environment.
4545
Future<void> verifyBucketExistenceAndAccess(String bucketName) async {
4646
// check bucket existence
47-
if (!await bucketExists(bucketName)) {
47+
if (!await bucketExistsWithRetry(bucketName)) {
4848
final message = 'Bucket "$bucketName" does not exists!';
4949
_logger.shout(message);
5050
if (envConfig.isRunningLocally) {
@@ -69,10 +69,29 @@ extension StorageExt on Storage {
6969
return;
7070
}
7171
}
72+
73+
/// Check whether a cloud storage bucket exists with the default retry.
74+
Future<bool> bucketExistsWithRetry(String bucketName) async {
75+
return await _retry(() => bucketExists(bucketName));
76+
}
77+
78+
/// Copy an object with the default retry.
79+
Future<void> copyObjectWithRetry(
80+
String src,
81+
String dest, {
82+
ObjectMetadata? metadata,
83+
}) async {
84+
return await _retry(() async => await copyObject(src, dest));
85+
}
7286
}
7387

7488
/// Additional methods on buckets.
7589
extension BucketExt on Bucket {
90+
/// Lookup object metadata with default retry.
91+
Future<ObjectInfo> infoWithRetry(String name) async {
92+
return await _retry(() => info(name));
93+
}
94+
7695
/// Returns an [ObjectInfo] if [name] exists, `null` otherwise.
7796
Future<ObjectInfo?> tryInfo(String name) async {
7897
return await retry(
@@ -89,6 +108,11 @@ extension BucketExt on Bucket {
89108
);
90109
}
91110

111+
/// Delete an object with default retry.
112+
Future<void> deleteWithRetry(String name) async {
113+
return await _retry(() => delete(name));
114+
}
115+
92116
/// Deletes [name] if it exists, ignores 404 otherwise.
93117
Future<void> tryDelete(String name) async {
94118
return await retry(
@@ -159,6 +183,35 @@ extension BucketExt on Bucket {
159183
String objectUrl(String objectName) {
160184
return '${activeConfiguration.storageBaseUrl}/$bucketName/$objectName';
161185
}
186+
187+
/// Update object metadata with default retry rules.
188+
Future<void> updateMetadataWithRetry(
189+
String objectName, ObjectMetadata metadata) async {
190+
return await _retry(() async => await updateMetadata(objectName, metadata));
191+
}
192+
193+
/// Start paging through objects in the bucket with the default retry.
194+
Future<Page<BucketEntry>> pageWithRetry(
195+
{String? prefix, String? delimiter, int pageSize = 50}) async {
196+
return await _retry(
197+
() async => await page(
198+
prefix: prefix,
199+
delimiter: delimiter,
200+
pageSize: pageSize,
201+
),
202+
);
203+
}
204+
}
205+
206+
extension PageExt<T> on Page<T> {
207+
/// Move to the next page with default retry.
208+
Future<Page<T>> nextWithRetry({int pageSize = 50}) async {
209+
return await _retry(() => next(pageSize: pageSize));
210+
}
211+
}
212+
213+
Future<R> _retry<R>(Future<R> Function() fn) async {
214+
return await retry(fn, maxAttempts: 3, retryIf: _retryIf);
162215
}
163216

164217
bool _retryIf(Exception e) {
@@ -212,7 +265,7 @@ Future<void> updateContentDispositionToAttachment(
212265
ObjectInfo info, Bucket bucket) async {
213266
if (info.metadata.contentDisposition != 'attachment') {
214267
try {
215-
await bucket.updateMetadata(
268+
await bucket.updateMetadataWithRetry(
216269
info.name, info.metadata.replace(contentDisposition: 'attachment'));
217270
} on Exception catch (e, st) {
218271
_logger.warning(

app/test/admin/exported_api_sync_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ void main() {
181181
final oldData = oldRoot[path] as Map;
182182
final bucket =
183183
storageService.bucket(activeConfiguration.exportedApiBucketName!);
184-
await bucket.updateMetadata(
184+
await bucket.updateMetadataWithRetry(
185185
path,
186186
ObjectMetadata(
187187
contentType: 'text/plain',

0 commit comments

Comments
 (0)