Skip to content

Commit ce6e7a0

Browse files
authored
feat(storage): add retry config to the Client and HmacKey operations (#5193)
1 parent fcf0cd0 commit ce6e7a0

6 files changed

+237
-24
lines changed

storage/bucket.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,22 @@ type BucketHandle struct {
5555
// found at:
5656
// https://cloud.google.com/storage/docs/bucket-naming
5757
func (c *Client) Bucket(name string) *BucketHandle {
58+
retry := c.retry.clone()
5859
return &BucketHandle{
5960
c: c,
6061
name: name,
6162
acl: ACLHandle{
6263
c: c,
6364
bucket: name,
65+
retry: retry,
6466
},
6567
defaultObjectACL: ACLHandle{
6668
c: c,
6769
bucket: name,
6870
isDefault: true,
71+
retry: retry,
6972
},
73+
retry: retry,
7074
}
7175
}
7276

@@ -1431,9 +1435,19 @@ func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator {
14311435
// on the new handle will use the customized retry configuration.
14321436
// Retry options set on a object handle will take precedence over options set on
14331437
// the bucket handle.
1438+
// These retry options will merge with the client's retry configuration (if set)
1439+
// for the returned handle. Options passed into this method will take precedence
1440+
// over retry options on the client. Note that you must explicitly pass in each
1441+
// option you want to override.
14341442
func (b *BucketHandle) Retryer(opts ...RetryOption) *BucketHandle {
14351443
b2 := *b
1436-
retry := &retryConfig{}
1444+
var retry *retryConfig
1445+
if b.retry != nil {
1446+
// merge the options with the existing retry
1447+
retry = b.retry
1448+
} else {
1449+
retry = &retryConfig{}
1450+
}
14371451
for _, opt := range opts {
14381452
opt.apply(retry)
14391453
}

storage/emulator_test.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ trap cleanup EXIT
5858
# TODO: move to passing once fixed
5959
FAILING=( "buckets.setIamPolicy"
6060
"objects.insert"
61-
"hmacKey.update"
6261
)
6362
# TODO: remove regex once all tests are passing
6463
# Unfortunately, there is no simple way to skip specific tests (see https://github.com/golang/go/issues/41583)
@@ -85,6 +84,7 @@ PASSING=( "buckets.list"
8584
"hmacKey.list"
8685
"hmacKey.create"
8786
"hmacKey.delete"
87+
"hmacKey.update"
8888
"notifications.list"
8989
"notifications.create"
9090
"notifications.get"

storage/hmac.go

+21-12
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ type HMACKey struct {
8989
type HMACKeyHandle struct {
9090
projectID string
9191
accessID string
92-
93-
raw *raw.ProjectsHmacKeysService
92+
retry *retryConfig
93+
raw *raw.ProjectsHmacKeysService
9494
}
9595

9696
// HMACKeyHandle creates a handle that will be used for HMACKey operations.
@@ -100,6 +100,7 @@ func (c *Client) HMACKeyHandle(projectID, accessID string) *HMACKeyHandle {
100100
return &HMACKeyHandle{
101101
projectID: projectID,
102102
accessID: accessID,
103+
retry: c.retry,
103104
raw: raw.NewProjectsHmacKeysService(c.raw),
104105
}
105106
}
@@ -126,10 +127,10 @@ func (hkh *HMACKeyHandle) Get(ctx context.Context, opts ...HMACKeyOption) (*HMAC
126127

127128
var metadata *raw.HmacKeyMetadata
128129
var err error
129-
err = runWithRetry(ctx, func() error {
130+
err = run(ctx, func() error {
130131
metadata, err = call.Context(ctx).Do()
131132
return err
132-
})
133+
}, hkh.retry, true)
133134
if err != nil {
134135
return nil, err
135136
}
@@ -156,9 +157,9 @@ func (hkh *HMACKeyHandle) Delete(ctx context.Context, opts ...HMACKeyOption) err
156157
}
157158
setClientHeader(delCall.Header())
158159

159-
return runWithRetry(ctx, func() error {
160+
return run(ctx, func() error {
160161
return delCall.Context(ctx).Do()
161-
})
162+
}, hkh.retry, true)
162163
}
163164

164165
func pbHmacKeyToHMACKey(pb *raw.HmacKey, updatedTimeCanBeNil bool) (*HMACKey, error) {
@@ -214,8 +215,13 @@ func (c *Client) CreateHMACKey(ctx context.Context, projectID, serviceAccountEma
214215

215216
setClientHeader(call.Header())
216217

217-
hkPb, err := call.Context(ctx).Do()
218-
if err != nil {
218+
var hkPb *raw.HmacKey
219+
220+
if err := run(ctx, func() error {
221+
h, err := call.Context(ctx).Do()
222+
hkPb = h
223+
return err
224+
}, c.retry, false); err != nil {
219225
return nil, err
220226
}
221227

@@ -257,10 +263,11 @@ func (h *HMACKeyHandle) Update(ctx context.Context, au HMACKeyAttrsToUpdate, opt
257263

258264
var metadata *raw.HmacKeyMetadata
259265
var err error
260-
err = runWithRetry(ctx, func() error {
266+
isIdempotent := len(au.Etag) > 0
267+
err = run(ctx, func() error {
261268
metadata, err = call.Context(ctx).Do()
262269
return err
263-
})
270+
}, h.retry, isIdempotent)
264271

265272
if err != nil {
266273
return nil, err
@@ -285,6 +292,7 @@ type HMACKeysIterator struct {
285292
nextFunc func() error
286293
index int
287294
desc hmacKeyDesc
295+
retry *retryConfig
288296
}
289297

290298
// ListHMACKeys returns an iterator for listing HMACKeys.
@@ -297,6 +305,7 @@ func (c *Client) ListHMACKeys(ctx context.Context, projectID string, opts ...HMA
297305
ctx: ctx,
298306
raw: raw.NewProjectsHmacKeysService(c.raw),
299307
projectID: projectID,
308+
retry: c.retry,
300309
}
301310

302311
for _, opt := range opts {
@@ -361,10 +370,10 @@ func (it *HMACKeysIterator) fetch(pageSize int, pageToken string) (token string,
361370

362371
ctx := it.ctx
363372
var resp *raw.HmacKeysMetadata
364-
err = runWithRetry(it.ctx, func() error {
373+
err = run(it.ctx, func() error {
365374
resp, err = call.Context(ctx).Do()
366375
return err
367-
})
376+
}, it.retry, true)
368377
if err != nil {
369378
return "", err
370379
}

storage/retry_conformance_test.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,14 @@ var methods = map[string][]retryFunc{
252252
"storage.hmacKey.update": {
253253
func(ctx context.Context, c *Client, fs *resources, preconditions bool) error {
254254
key := c.HMACKeyHandle(projectID, fs.hmacKey.AccessID)
255+
uattrs := HMACKeyAttrsToUpdate{State: "INACTIVE"}
255256

256-
_, err := key.Update(ctx, HMACKeyAttrsToUpdate{State: "INACTIVE"})
257-
if err != nil {
258-
return err
257+
if preconditions {
258+
uattrs.Etag = fs.hmacKey.Etag
259259
}
260-
return fmt.Errorf("Etag preconditions not supported")
260+
261+
_, err := key.Update(ctx, uattrs)
262+
return err
261263
},
262264
},
263265
"storage.objects.compose": {

storage/storage.go

+26-3
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ type Client struct {
109109
readHost string
110110
// May be nil.
111111
creds *google.Credentials
112+
retry *retryConfig
112113

113114
// gc is an optional gRPC-based, GAPIC client.
114115
//
@@ -1792,7 +1793,8 @@ func setConditionField(call reflect.Value, name string, value interface{}) bool
17921793
// on the new handle will use the customized retry configuration.
17931794
// These retry options will merge with the bucket's retryer (if set) for the
17941795
// returned handle. Options passed into this method will take precedence over
1795-
// options on the bucket's retryer.
1796+
// retry options on the bucket and client. Note that you must explicitly pass in
1797+
// each option you want to override.
17961798
func (o *ObjectHandle) Retryer(opts ...RetryOption) *ObjectHandle {
17971799
o2 := *o
17981800
var retry *retryConfig
@@ -1810,6 +1812,27 @@ func (o *ObjectHandle) Retryer(opts ...RetryOption) *ObjectHandle {
18101812
return &o2
18111813
}
18121814

1815+
// SetRetry configures the client with custom retry behavior as specified by the
1816+
// options that are passed to it. All operations using this client will use the
1817+
// customized retry configuration.
1818+
// This should be called once before using the client for network operations, as
1819+
// there could be indeterminate behaviour with operations in progress.
1820+
// Retry options set on a bucket or object handle will take precedence over
1821+
// these options.
1822+
func (c *Client) SetRetry(opts ...RetryOption) {
1823+
var retry *retryConfig
1824+
if c.retry != nil {
1825+
// merge the options with the existing retry
1826+
retry = c.retry
1827+
} else {
1828+
retry = &retryConfig{}
1829+
}
1830+
for _, opt := range opts {
1831+
opt.apply(retry)
1832+
}
1833+
c.retry = retry
1834+
}
1835+
18131836
// RetryOption allows users to configure non-default retry behavior for API
18141837
// calls made to GCS.
18151838
type RetryOption interface {
@@ -1971,10 +1994,10 @@ func (c *Client) ServiceAccount(ctx context.Context, projectID string) (string,
19711994
r := c.raw.Projects.ServiceAccount.Get(projectID)
19721995
var res *raw.ServiceAccount
19731996
var err error
1974-
err = runWithRetry(ctx, func() error {
1997+
err = run(ctx, func() error {
19751998
res, err = r.Context(ctx).Do()
19761999
return err
1977-
})
2000+
}, c.retry, true)
19782001
if err != nil {
19792002
return "", err
19802003
}

0 commit comments

Comments
 (0)