From 0955a270fefb15ed046efea3c8f92e3ae704b519 Mon Sep 17 00:00:00 2001 From: Chris Cotter Date: Wed, 1 Dec 2021 01:27:34 -0500 Subject: [PATCH 1/8] feat(internal/gensupport): add configurable retry Extend SendAndRetryRequest to allow retry options (backoff and ErrorFunc) to be passed in. Add a surface to the generated code for storage to allow the manual layer to choose whether the request is retried. I tested this out via the manual layer by verifying that the ObjectsInsertCall only retries now when WithRetry() is applied. I still need to update the generator to ensure that the changes in the generated file will persist and probably add some tests as well. Thought I'd try and get an initial review first. --- internal/gensupport/send.go | 44 ++++++++++++++++++++++++++++---- internal/gensupport/send_test.go | 2 +- storage/v1/storage-gen.go | 31 +++++++++++++++++++++- 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/internal/gensupport/send.go b/internal/gensupport/send.go index 276d6f6963a..679e0bca267 100644 --- a/internal/gensupport/send.go +++ b/internal/gensupport/send.go @@ -10,6 +10,9 @@ import ( "errors" "net/http" "time" + + "github.com/googleapis/gax-go/v2" + "google.golang.org/api/googleapi" ) // SendRequest sends a single HTTP request using the given client. @@ -50,7 +53,7 @@ func send(ctx context.Context, client *http.Client, req *http.Request) (*http.Re // If ctx is non-nil, it calls all hooks, then sends the request with // req.WithContext, then calls any functions returned by the hooks in // reverse order. -func SendRequestWithRetry(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { +func SendRequestWithRetry(ctx context.Context, client *http.Client, req *http.Request, retry *RetryConfig) (*http.Response, error) { // Disallow Accept-Encoding because it interferes with the automatic gzip handling // done by the default http.Transport. See https://github.com/google/google-api-go-client/issues/219. if _, ok := req.Header["Accept-Encoding"]; ok { @@ -59,10 +62,10 @@ func SendRequestWithRetry(ctx context.Context, client *http.Client, req *http.Re if ctx == nil { return client.Do(req) } - return sendAndRetry(ctx, client, req) + return sendAndRetry(ctx, client, req, retry) } -func sendAndRetry(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { +func sendAndRetry(ctx context.Context, client *http.Client, req *http.Request, retry *RetryConfig) (*http.Response, error) { if client == nil { client = http.DefaultClient } @@ -72,7 +75,33 @@ func sendAndRetry(ctx context.Context, client *http.Client, req *http.Request) ( // Loop to retry the request, up to the context deadline. var pause time.Duration - bo := backoff() + var bo Backoff + if retry != nil && retry.Backoff != nil { + bo = &gax.Backoff{ + Initial: retry.Backoff.Initial, + Max: retry.Backoff.Max, + Multiplier: retry.Backoff.Multiplier, + } + } else { + bo = backoff() + } + + var errorFunc = shouldRetry + if retry != nil && retry.ShouldRetry != nil { + errorFunc = func(status int, err error) bool { + // This is kind of hacky; it is necessary because ShouldRetry expects to + // handle HTTP errors via googleapi.Error, but the error has not yet been + // wrapped with a googleapi.Error at this layer, and the ErrorFunc type + // in the manual layer does not pass in a status explicitly as it does + // here. So, we must wrap error status codes in a googleapi.Error so that + // ShouldRetry can parse this correctly. + if status >= 400 { + return retry.ShouldRetry(&googleapi.Error{Code: status}) + } else { + return retry.ShouldRetry(err) + } + } + } for { select { @@ -96,7 +125,7 @@ func sendAndRetry(ctx context.Context, client *http.Client, req *http.Request) ( // Check if we can retry the request. A retry can only be done if the error // is retryable and the request body can be re-created using GetBody (this // will not be possible if the body was unbuffered). - if req.GetBody == nil || !shouldRetry(status, err) { + if req.GetBody == nil || !errorFunc(status, err) { break } var errBody error @@ -121,3 +150,8 @@ func DecodeResponse(target interface{}, res *http.Response) error { } return json.NewDecoder(res.Body).Decode(target) } + +type RetryConfig struct { + Backoff *gax.Backoff + ShouldRetry func(err error) bool +} diff --git a/internal/gensupport/send_test.go b/internal/gensupport/send_test.go index 289a7bc2b5b..d6af483e66e 100644 --- a/internal/gensupport/send_test.go +++ b/internal/gensupport/send_test.go @@ -24,7 +24,7 @@ func TestSendRequestWithRetry(t *testing.T) { // Setting Accept-Encoding should give an error immediately. req, _ := http.NewRequest("GET", "url", nil) req.Header.Set("Accept-Encoding", "") - _, err := SendRequestWithRetry(context.Background(), nil, req) + _, err := SendRequestWithRetry(context.Background(), nil, req, nil) if err == nil { t.Error("got nil, want error") } diff --git a/storage/v1/storage-gen.go b/storage/v1/storage-gen.go index def1bd35996..23f168ef42d 100644 --- a/storage/v1/storage-gen.go +++ b/storage/v1/storage-gen.go @@ -55,6 +55,7 @@ import ( "strconv" "strings" + "github.com/googleapis/gax-go/v2" googleapi "google.golang.org/api/googleapi" gensupport "google.golang.org/api/internal/gensupport" option "google.golang.org/api/option" @@ -10099,6 +10100,7 @@ type ObjectsInsertCall struct { mediaInfo_ *gensupport.MediaInfo ctx_ context.Context header_ http.Header + retry *gensupport.RetryConfig } // Insert: Stores a new object and metadata. @@ -10265,6 +10267,29 @@ func (c *ObjectsInsertCall) ProgressUpdater(pu googleapi.ProgressUpdater) *Objec return c } +// WithRetry causes the library to retry the initial request of the upload +// (for resumable uploads) or the entire upload (for multipart uploads) if +// a transient error occurs. This is contingent on ChunkSize being > 0 (so +// that the input data may be buffered). The backoff argument will be used to +// determine exponential backoff timing, and the errorFunc is used to determine +// which errors are considered retryable. By default, exponetial backoff will be +// applied using gax defaults, and the following errors are retried: +// +// - HTTP responses with codes 429, 502, 503, and 504. +// +// - Transient network errors such as connection reset and io.ErrUnexpectedEOF. +// +// - Errors which are considered transient using the Temporary() interface. +// +// - Wrapped versions of these errors. +func (c *ObjectsInsertCall) WithRetry(bo *gax.Backoff, errorFunc func(err error) bool) *ObjectsInsertCall { + c.retry = &gensupport.RetryConfig{ + Backoff: bo, + ShouldRetry: errorFunc, + } + return c +} + // Fields allows partial responses to be retrieved. See // https://developers.google.com/gdata/docs/2.0/basics#PartialResponse // for more information. @@ -10328,7 +10353,11 @@ func (c *ObjectsInsertCall) doRequest(alt string) (*http.Response, error) { googleapi.Expand(req.URL, map[string]string{ "bucket": c.bucket, }) - return gensupport.SendRequestWithRetry(c.ctx_, c.s.client, req) + if c.retry != nil { + return gensupport.SendRequestWithRetry(c.ctx_, c.s.client, req, c.retry) + } else { + return gensupport.SendRequest(c.ctx_, c.s.client, req) + } } // Do executes the "storage.objects.insert" call. From 05e09f498b3428b93657bb82d6303c9cfb94f1da Mon Sep 17 00:00:00 2001 From: Chris Cotter Date: Thu, 9 Dec 2021 15:18:12 -0500 Subject: [PATCH 2/8] use configs for chunk uploads & refactor --- internal/gensupport/resumable.go | 36 ++++------------ internal/gensupport/retry.go | 72 ++++++++++++++++++++++++++++++++ internal/gensupport/send.go | 23 +--------- storage/v1/storage-gen.go | 1 + 4 files changed, 82 insertions(+), 50 deletions(-) create mode 100644 internal/gensupport/retry.go diff --git a/internal/gensupport/resumable.go b/internal/gensupport/resumable.go index edc87ec24f6..5126642f027 100644 --- a/internal/gensupport/resumable.go +++ b/internal/gensupport/resumable.go @@ -12,33 +12,8 @@ import ( "net/http" "sync" "time" - - gax "github.com/googleapis/gax-go/v2" -) - -// Backoff is an interface around gax.Backoff's Pause method, allowing tests to provide their -// own implementation. -type Backoff interface { - Pause() time.Duration -} - -// These are declared as global variables so that tests can overwrite them. -var ( - retryDeadline = 32 * time.Second - backoff = func() Backoff { - return &gax.Backoff{Initial: 100 * time.Millisecond} - } - // isRetryable is a platform-specific hook, specified in retryable_linux.go - syscallRetryable func(error) bool = func(err error) bool { return false } ) -const ( - // statusTooManyRequests is returned by the storage API if the - // per-project limits have been temporarily exceeded. The request - // should be retried. - // https://cloud.google.com/storage/docs/json_api/v1/status-codes#standardcodes - statusTooManyRequests = 429 -) // ResumableUpload is used by the generated APIs to provide resumable uploads. // It is not used by developers directly. @@ -57,6 +32,9 @@ type ResumableUpload struct { // Callback is an optional function that will be periodically called with the cumulative number of bytes uploaded. Callback func(int64) + + // Retry optionally configures retries for requests made against the upload. + Retry *RetryConfig } // Progress returns the number of bytes uploaded at this point. @@ -176,13 +154,15 @@ func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err } return resp, nil } + // Configure retryable error criteria. + errorFunc := rx.Retry.errorFunc() // Send all chunks. for { var pause time.Duration - // Each chunk gets its own initialized-at-zero retry. - bo := backoff() + // Each chunk gets its own initialized-at-zero backoff. + bo := rx.Retry.backoff() quitAfter := time.After(retryDeadline) // Retry loop for a single chunk. @@ -206,7 +186,7 @@ func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err } // Check if we should retry the request. - if !shouldRetry(status, err) { + if !errorFunc(status, err) { break } diff --git a/internal/gensupport/retry.go b/internal/gensupport/retry.go new file mode 100644 index 00000000000..0def05ecc53 --- /dev/null +++ b/internal/gensupport/retry.go @@ -0,0 +1,72 @@ +// Copyright 2021 Google LLC. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gensupport + +import ( + "time" + + "github.com/googleapis/gax-go/v2" + "google.golang.org/api/googleapi" +) + +// Backoff is an interface around gax.Backoff's Pause method, allowing tests to provide their +// own implementation. +type Backoff interface { + Pause() time.Duration +} + +// These are declared as global variables so that tests can overwrite them. +var ( + retryDeadline = 32 * time.Second // Per-chunk deadline for resumable uploads. + backoff = func() Backoff { + return &gax.Backoff{Initial: 100 * time.Millisecond} + } + // isRetryable is a platform-specific hook, specified in retryable_linux.go + syscallRetryable func(error) bool = func(err error) bool { return false } +) + +const ( + // statusTooManyRequests is returned by the storage API if the + // per-project limits have been temporarily exceeded. The request + // should be retried. + // https://cloud.google.com/storage/docs/json_api/v1/status-codes#standardcodes + statusTooManyRequests = 429 +) + +// RetryConfig allows configuration of backoff timing and retryable errors. +type RetryConfig struct { + Backoff *gax.Backoff + ShouldRetry func(err error) bool +} + +// Get a new backoff object based on the configured values. +func (r *RetryConfig) backoff() Backoff { + if r == nil || r.Backoff == nil { + return backoff() + } + return &gax.Backoff{ + Initial: r.Backoff.Initial, + Max: r.Backoff.Max, + Multiplier: r.Backoff.Multiplier, + } +} + +// This is kind of hacky; it is necessary because ShouldRetry expects to +// handle HTTP errors via googleapi.Error, but the error has not yet been +// wrapped with a googleapi.Error at this layer, and the ErrorFunc type +// in the manual layer does not pass in a status explicitly as it does +// here. So, we must wrap error status codes in a googleapi.Error so that +// ShouldRetry can parse this correctly. +func (r *RetryConfig) errorFunc() func(status int, err error) bool { + if r == nil || r.ShouldRetry == nil { + return shouldRetry + } + return func(status int, err error) bool { + if status >= 400 { + return r.ShouldRetry(&googleapi.Error{Code: status}) + } + return r.ShouldRetry(err) + } +} diff --git a/internal/gensupport/send.go b/internal/gensupport/send.go index 679e0bca267..dab64aef367 100644 --- a/internal/gensupport/send.go +++ b/internal/gensupport/send.go @@ -12,7 +12,6 @@ import ( "time" "github.com/googleapis/gax-go/v2" - "google.golang.org/api/googleapi" ) // SendRequest sends a single HTTP request using the given client. @@ -86,22 +85,7 @@ func sendAndRetry(ctx context.Context, client *http.Client, req *http.Request, r bo = backoff() } - var errorFunc = shouldRetry - if retry != nil && retry.ShouldRetry != nil { - errorFunc = func(status int, err error) bool { - // This is kind of hacky; it is necessary because ShouldRetry expects to - // handle HTTP errors via googleapi.Error, but the error has not yet been - // wrapped with a googleapi.Error at this layer, and the ErrorFunc type - // in the manual layer does not pass in a status explicitly as it does - // here. So, we must wrap error status codes in a googleapi.Error so that - // ShouldRetry can parse this correctly. - if status >= 400 { - return retry.ShouldRetry(&googleapi.Error{Code: status}) - } else { - return retry.ShouldRetry(err) - } - } - } + var errorFunc = retry.errorFunc() for { select { @@ -150,8 +134,3 @@ func DecodeResponse(target interface{}, res *http.Response) error { } return json.NewDecoder(res.Body).Decode(target) } - -type RetryConfig struct { - Backoff *gax.Backoff - ShouldRetry func(err error) bool -} diff --git a/storage/v1/storage-gen.go b/storage/v1/storage-gen.go index 23f168ef42d..c6ee94b5bca 100644 --- a/storage/v1/storage-gen.go +++ b/storage/v1/storage-gen.go @@ -10390,6 +10390,7 @@ func (c *ObjectsInsertCall) Do(opts ...googleapi.CallOption) (*Object, error) { if rx != nil { rx.Client = c.s.client rx.UserAgent = c.s.userAgent() + rx.Retry = c.retry ctx := c.ctx_ if ctx == nil { ctx = context.TODO() From 67f78e01db4862a0fb4f43e9cb38ddbd29cad075 Mon Sep 17 00:00:00 2001 From: Chris Cotter Date: Thu, 9 Dec 2021 15:21:02 -0500 Subject: [PATCH 3/8] gofmt --- internal/gensupport/resumable.go | 1 - internal/gensupport/retry.go | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/gensupport/resumable.go b/internal/gensupport/resumable.go index 5126642f027..38db4b90b2e 100644 --- a/internal/gensupport/resumable.go +++ b/internal/gensupport/resumable.go @@ -14,7 +14,6 @@ import ( "time" ) - // ResumableUpload is used by the generated APIs to provide resumable uploads. // It is not used by developers directly. type ResumableUpload struct { diff --git a/internal/gensupport/retry.go b/internal/gensupport/retry.go index 0def05ecc53..fc281296ed1 100644 --- a/internal/gensupport/retry.go +++ b/internal/gensupport/retry.go @@ -19,7 +19,7 @@ type Backoff interface { // These are declared as global variables so that tests can overwrite them. var ( - retryDeadline = 32 * time.Second // Per-chunk deadline for resumable uploads. + retryDeadline = 32 * time.Second // Per-chunk deadline for resumable uploads. backoff = func() Backoff { return &gax.Backoff{Initial: 100 * time.Millisecond} } @@ -47,8 +47,8 @@ func (r *RetryConfig) backoff() Backoff { return backoff() } return &gax.Backoff{ - Initial: r.Backoff.Initial, - Max: r.Backoff.Max, + Initial: r.Backoff.Initial, + Max: r.Backoff.Max, Multiplier: r.Backoff.Multiplier, } } From 2629a72706e87ad7f2dfab6ffd00d6d450854684 Mon Sep 17 00:00:00 2001 From: Chris Cotter Date: Thu, 9 Dec 2021 15:33:09 -0500 Subject: [PATCH 4/8] more refactoring --- internal/gensupport/resumable.go | 30 ------------------------ internal/gensupport/retry.go | 40 +++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/internal/gensupport/resumable.go b/internal/gensupport/resumable.go index 38db4b90b2e..0fb74606c3a 100644 --- a/internal/gensupport/resumable.go +++ b/internal/gensupport/resumable.go @@ -205,33 +205,3 @@ func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err return prepareReturn(resp, err) } } - -// shouldRetry indicates whether an error is retryable for the purposes of this -// package, following guidance from -// https://cloud.google.com/storage/docs/exponential-backoff . -func shouldRetry(status int, err error) bool { - if 500 <= status && status <= 599 { - return true - } - if status == statusTooManyRequests { - return true - } - if err == io.ErrUnexpectedEOF { - return true - } - // Transient network errors should be retried. - if syscallRetryable(err) { - return true - } - if err, ok := err.(interface{ Temporary() bool }); ok { - if err.Temporary() { - return true - } - } - // If Go 1.13 error unwrapping is available, use this to examine wrapped - // errors. - if err, ok := err.(interface{ Unwrap() error }); ok { - return shouldRetry(status, err.Unwrap()) - } - return false -} diff --git a/internal/gensupport/retry.go b/internal/gensupport/retry.go index fc281296ed1..4a4861b1b1a 100644 --- a/internal/gensupport/retry.go +++ b/internal/gensupport/retry.go @@ -5,6 +5,7 @@ package gensupport import ( + "io" "time" "github.com/googleapis/gax-go/v2" @@ -19,11 +20,13 @@ type Backoff interface { // These are declared as global variables so that tests can overwrite them. var ( - retryDeadline = 32 * time.Second // Per-chunk deadline for resumable uploads. - backoff = func() Backoff { + // Per-chunk deadline for resumable uploads. + retryDeadline = 32 * time.Second + // Default backoff timer. + backoff = func() Backoff { return &gax.Backoff{Initial: 100 * time.Millisecond} } - // isRetryable is a platform-specific hook, specified in retryable_linux.go + // syscallRetryable is a platform-specific hook, specified in retryable_linux.go syscallRetryable func(error) bool = func(err error) bool { return false } ) @@ -35,6 +38,37 @@ const ( statusTooManyRequests = 429 ) +// shouldRetry indicates whether an error is retryable for the purposes of this +// package, unless a ShouldRetry func is specified by the RetryConfig instead. +// It follows guidance from +// https://cloud.google.com/storage/docs/exponential-backoff . +func shouldRetry(status int, err error) bool { + if 500 <= status && status <= 599 { + return true + } + if status == statusTooManyRequests { + return true + } + if err == io.ErrUnexpectedEOF { + return true + } + // Transient network errors should be retried. + if syscallRetryable(err) { + return true + } + if err, ok := err.(interface{ Temporary() bool }); ok { + if err.Temporary() { + return true + } + } + // If Go 1.13 error unwrapping is available, use this to examine wrapped + // errors. + if err, ok := err.(interface{ Unwrap() error }); ok { + return shouldRetry(status, err.Unwrap()) + } + return false +} + // RetryConfig allows configuration of backoff timing and retryable errors. type RetryConfig struct { Backoff *gax.Backoff From f233fabdcc27267e9d74d9234a63faae43ec537f Mon Sep 17 00:00:00 2001 From: Chris Cotter Date: Thu, 9 Dec 2021 17:54:06 -0500 Subject: [PATCH 5/8] add generator changes --- google-api-go-generator/gen.go | 41 +++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/google-api-go-generator/gen.go b/google-api-go-generator/gen.go index 10eb88ed0e4..dc51bfc7b2d 100644 --- a/google-api-go-generator/gen.go +++ b/google-api-go-generator/gen.go @@ -699,6 +699,9 @@ func (a *API) GenerateCode() ([]byte, error) { pn(" %q", imp) } pn("") + if a.Name == "storage" { + pn(" %q", "github.com/googleapis/gax-go/v2") + } for _, imp := range []struct { pkg string lname string @@ -1838,6 +1841,9 @@ func (meth *Method) generateCode() { if meth.supportsMediaUpload() { pn(" mediaInfo_ *gensupport.MediaInfo") + if meth.api.Name == "storage" { + pn(" retry *gensupport.RetryConfig") + } } pn(" ctx_ context.Context") pn(" header_ http.Header") @@ -1986,6 +1992,32 @@ func (meth *Method) generateCode() { pn("}") } + if meth.supportsMediaUpload() && meth.api.Name == "storage" { + comment := "WithRetry causes the library to retry the initial request of the upload" + + "(for resumable uploads) or the entire upload (for multipart uploads) if" + + "a transient error occurs. This is contingent on ChunkSize being > 0 (so" + + "that the input data may be buffered). The backoff argument will be used to" + + "determine exponential backoff timing, and the errorFunc is used to determine" + + "which errors are considered retryable. By default, exponetial backoff will be" + + "applied using gax defaults, and the following errors are retried:" + + "\n\n" + + "- HTTP responses with codes 429, 502, 503, and 504." + + "\n\n" + + "- Transient network errors such as connection reset and io.ErrUnexpectedEOF." + + "\n\n" + + "- Errors which are considered transient using the Temporary() interface." + + "\n\n" + + "- Wrapped versions of these errors." + p("\n%s", asComment("", comment)) + pn("func (c *%s) WithRetry(bo *gax.Backoff, errorFunc func(err error) bool) *%s {", callName, callName) + pn(" c.retry = &gensupport.RetryConfig{") + pn(" Backoff: bo,") + pn(" ShouldRetry: errorFunc,") + pn(" }") + pn(" return c") + pn("}") + } + comment := "Fields allows partial responses to be retrieved. " + "See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse " + "for more information." @@ -2106,7 +2138,11 @@ func (meth *Method) generateCode() { pn(`})`) } if meth.supportsMediaUpload() && meth.api.Name == "storage" { - pn("return gensupport.SendRequestWithRetry(c.ctx_, c.s.client, req)") + pn("if c.retry != nil {") + pn(" return gensupport.SendRequestWithRetry(c.ctx_, c.s.client, req, c.retry)") + pn("} else {") + pn(" return gensupport.SendRequest(c.ctx_, c.s.client, req)") + pn("}") } else { pn("return gensupport.SendRequest(c.ctx_, c.s.client, req)") } @@ -2172,6 +2208,9 @@ func (meth *Method) generateCode() { pn("if rx != nil {") pn(" rx.Client = c.s.client") pn(" rx.UserAgent = c.s.userAgent()") + if meth.api.Name == "storage" { + pn(" rx.Retry = c.retry") + } pn(" ctx := c.ctx_") pn(" if ctx == nil {") // TODO(mcgreevy): Require context when calling Media, or Do. From 056879998fc4dac8101300ca4bf959f6392d62d5 Mon Sep 17 00:00:00 2001 From: Chris Cotter Date: Thu, 9 Dec 2021 18:06:29 -0500 Subject: [PATCH 6/8] gofmt --- google-api-go-generator/gen.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/google-api-go-generator/gen.go b/google-api-go-generator/gen.go index dc51bfc7b2d..a573a854aa2 100644 --- a/google-api-go-generator/gen.go +++ b/google-api-go-generator/gen.go @@ -1994,20 +1994,20 @@ func (meth *Method) generateCode() { if meth.supportsMediaUpload() && meth.api.Name == "storage" { comment := "WithRetry causes the library to retry the initial request of the upload" + - "(for resumable uploads) or the entire upload (for multipart uploads) if" + - "a transient error occurs. This is contingent on ChunkSize being > 0 (so" + - "that the input data may be buffered). The backoff argument will be used to" + - "determine exponential backoff timing, and the errorFunc is used to determine" + - "which errors are considered retryable. By default, exponetial backoff will be" + - "applied using gax defaults, and the following errors are retried:" + - "\n\n" + - "- HTTP responses with codes 429, 502, 503, and 504." + - "\n\n" + - "- Transient network errors such as connection reset and io.ErrUnexpectedEOF." + - "\n\n" + - "- Errors which are considered transient using the Temporary() interface." + - "\n\n" + - "- Wrapped versions of these errors." + "(for resumable uploads) or the entire upload (for multipart uploads) if" + + "a transient error occurs. This is contingent on ChunkSize being > 0 (so" + + "that the input data may be buffered). The backoff argument will be used to" + + "determine exponential backoff timing, and the errorFunc is used to determine" + + "which errors are considered retryable. By default, exponetial backoff will be" + + "applied using gax defaults, and the following errors are retried:" + + "\n\n" + + "- HTTP responses with codes 429, 502, 503, and 504." + + "\n\n" + + "- Transient network errors such as connection reset and io.ErrUnexpectedEOF." + + "\n\n" + + "- Errors which are considered transient using the Temporary() interface." + + "\n\n" + + "- Wrapped versions of these errors." p("\n%s", asComment("", comment)) pn("func (c *%s) WithRetry(bo *gax.Backoff, errorFunc func(err error) bool) *%s {", callName, callName) pn(" c.retry = &gensupport.RetryConfig{") From 0bd85a281492d55809dca7f522eb9c646cec1c22 Mon Sep 17 00:00:00 2001 From: Chris Cotter Date: Fri, 10 Dec 2021 15:01:29 -0500 Subject: [PATCH 7/8] fix v1beta2 file --- storage/v1beta2/storage-gen.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/v1beta2/storage-gen.go b/storage/v1beta2/storage-gen.go index 090c2e0d321..b077f128a9b 100644 --- a/storage/v1beta2/storage-gen.go +++ b/storage/v1beta2/storage-gen.go @@ -6285,7 +6285,7 @@ func (c *ObjectsInsertCall) doRequest(alt string) (*http.Response, error) { googleapi.Expand(req.URL, map[string]string{ "bucket": c.bucket, }) - return gensupport.SendRequestWithRetry(c.ctx_, c.s.client, req) + return gensupport.SendRequest(c.ctx_, c.s.client, req) } // Do executes the "storage.objects.insert" call. From fb2d23998c60e3273c4e3a1a6f41ccd4feaf5882 Mon Sep 17 00:00:00 2001 From: Chris Cotter Date: Fri, 10 Dec 2021 15:36:44 -0500 Subject: [PATCH 8/8] remove superfluous else --- google-api-go-generator/gen.go | 3 +-- storage/v1/storage-gen.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/google-api-go-generator/gen.go b/google-api-go-generator/gen.go index a573a854aa2..2e337c9e1d8 100644 --- a/google-api-go-generator/gen.go +++ b/google-api-go-generator/gen.go @@ -2140,9 +2140,8 @@ func (meth *Method) generateCode() { if meth.supportsMediaUpload() && meth.api.Name == "storage" { pn("if c.retry != nil {") pn(" return gensupport.SendRequestWithRetry(c.ctx_, c.s.client, req, c.retry)") - pn("} else {") - pn(" return gensupport.SendRequest(c.ctx_, c.s.client, req)") pn("}") + pn("return gensupport.SendRequest(c.ctx_, c.s.client, req)") } else { pn("return gensupport.SendRequest(c.ctx_, c.s.client, req)") } diff --git a/storage/v1/storage-gen.go b/storage/v1/storage-gen.go index c6ee94b5bca..7ce93d87ac3 100644 --- a/storage/v1/storage-gen.go +++ b/storage/v1/storage-gen.go @@ -10355,9 +10355,8 @@ func (c *ObjectsInsertCall) doRequest(alt string) (*http.Response, error) { }) if c.retry != nil { return gensupport.SendRequestWithRetry(c.ctx_, c.s.client, req, c.retry) - } else { - return gensupport.SendRequest(c.ctx_, c.s.client, req) } + return gensupport.SendRequest(c.ctx_, c.s.client, req) } // Do executes the "storage.objects.insert" call.