@@ -109,8 +109,9 @@ type GCSStorage struct {
109
109
clientCnt int64
110
110
clientOps []option.ClientOption
111
111
112
- handles []* storage.BucketHandle
113
- clients []* storage.Client
112
+ handles []* storage.BucketHandle
113
+ clients []* storage.Client
114
+ clientCancel context.CancelFunc
114
115
}
115
116
116
117
// GetBucketHandle gets the handle to the GCS API on the bucket.
@@ -334,6 +335,7 @@ func (s *GCSStorage) Rename(ctx context.Context, oldFileName, newFileName string
334
335
335
336
// Close implements ExternalStorage interface.
336
337
func (s * GCSStorage ) Close () {
338
+ s .clientCancel ()
337
339
for _ , client := range s .clients {
338
340
if err := client .Close (); err != nil {
339
341
log .Warn ("failed to close gcs client" , zap .Error (err ))
@@ -415,39 +417,73 @@ skipHandleCred:
415
417
return ret , nil
416
418
}
417
419
418
- // Reset resets the GCS storage.
420
+ // Reset resets the GCS storage. Reset should not be used concurrently with
421
+ // Close.
419
422
func (s * GCSStorage ) Reset (ctx context.Context ) error {
420
423
logutil .Logger (ctx ).Info ("resetting gcs storage" )
421
424
422
- for _ , client := range s .clients {
423
- _ = client .Close ()
424
- }
425
-
426
- s .clients = make ([]* storage.Client , gcsClientCnt )
427
- eg , egCtx := util .NewErrorGroupWithRecoverWithCtx (ctx )
428
- for i := range s .clients {
429
- eg .Go (func () error {
430
- client , err := storage .NewClient (egCtx , s .clientOps ... )
425
+ s .cancelAndCloseGCSClients ()
426
+
427
+ clientCtx , clientCancel := context .WithCancel (context .Background ())
428
+ s .clients = make ([]* storage.Client , 0 , gcsClientCnt )
429
+ wg := util.WaitGroupWrapper {}
430
+ cliCh := make (chan * storage.Client )
431
+ wg .RunWithLog (func () {
432
+ for range gcsClientCnt {
433
+ select {
434
+ case cli := <- cliCh :
435
+ s .clients = append (s .clients , cli )
436
+ case <- ctx .Done ():
437
+ clientCancel ()
438
+ return
439
+ case <- clientCtx .Done ():
440
+ return
441
+ }
442
+ }
443
+ })
444
+ firstErr := atomic .NewError (nil )
445
+ for range gcsClientCnt {
446
+ wg .RunWithLog (func () {
447
+ client , err := storage .NewClient (clientCtx , s .clientOps ... )
431
448
if err != nil {
432
- return errors .Trace (err )
449
+ firstErr .CompareAndSwap (nil , err )
450
+ clientCancel ()
451
+ return
433
452
}
434
453
client .SetRetry (storage .WithErrorFunc (shouldRetry ), storage .WithPolicy (storage .RetryAlways ))
435
- s .clients [i ] = client
436
- return nil
454
+ select {
455
+ case cliCh <- client :
456
+ case <- clientCtx .Done ():
457
+ }
437
458
})
438
459
}
439
- err := eg .Wait ()
440
- if err != nil {
460
+ wg .Wait ()
461
+ if err := firstErr .Load (); err != nil {
462
+ s .cancelAndCloseGCSClients ()
441
463
return errors .Trace (err )
442
464
}
443
465
466
+ s .clientCancel = clientCancel
444
467
s .handles = make ([]* storage.BucketHandle , gcsClientCnt )
445
468
for i := range s .handles {
446
469
s .handles [i ] = s .clients [i ].Bucket (s .gcs .Bucket )
447
470
}
448
471
return nil
449
472
}
450
473
474
+ func (s * GCSStorage ) cancelAndCloseGCSClients () {
475
+ if s .clientCancel != nil {
476
+ s .clientCancel ()
477
+ s .clientCancel = nil
478
+ }
479
+
480
+ for _ , client := range s .clients {
481
+ if client != nil {
482
+ _ = client .Close ()
483
+ }
484
+ }
485
+ }
486
+
451
487
func shouldRetry (err error ) bool {
452
488
if storage .ShouldRetry (err ) {
453
489
return true
0 commit comments