Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(storage): export ShouldRetry #6370

Merged
merged 2 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions storage/grpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,7 @@ func (r *gRPCReader) Close() error {
// an attempt to reopen the stream.
func (r *gRPCReader) recv() (*storagepb.ReadObjectResponse, error) {
msg, err := r.stream.Recv()
if err != nil && shouldRetry(err) {
if err != nil && ShouldRetry(err) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: ShouldRetry should not be referenced directly here; instead we should be using the configured value from the retry configuration passed through the interface. Will file a separate bug to fix this up. FYI @noahdietz

// This will "close" the existing stream and immediately attempt to
// reopen the stream, but will backoff if further attempts are necessary.
// Reopening the stream Recvs the first message, so if retrying is
Expand Down Expand Up @@ -1559,7 +1559,7 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
// resend the entire buffer via a new stream.
// If not retriable, falling through will return the error received
// from closing the stream.
if shouldRetry(err) {
if ShouldRetry(err) {
sent = 0
finishWrite = false
// TODO: Add test case for failure modes of querying progress.
Expand Down Expand Up @@ -1590,7 +1590,7 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
// resend the entire buffer via a new stream.
// If not retriable, falling through will return the error received
// from closing the stream.
if shouldRetry(err) {
if ShouldRetry(err) {
sent = 0
finishWrite = false
offset, err = w.determineOffset(start)
Expand Down
15 changes: 12 additions & 3 deletions storage/invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func run(ctx context.Context, call func() error, retry *retryConfig, isIdempoten
bo.Initial = retry.backoff.Initial
bo.Max = retry.backoff.Max
}
var errorFunc func(err error) bool = shouldRetry
var errorFunc func(err error) bool = ShouldRetry
if retry.shouldRetry != nil {
errorFunc = retry.shouldRetry
}
Expand Down Expand Up @@ -89,7 +89,16 @@ func setRetryHeaderGRPC(_ context.Context) func(string, int) {
}
}

func shouldRetry(err error) bool {
// ShouldRetry returns true if an error is retryable, based on best practice
// guidance from GCS. See
// https://cloud.google.com/storage/docs/retry-strategy#go for more information
// on what errors are considered retryable.
//
// If you would like to customize retryable errors, use the WithErrorFunc to
// supply a RetryOption to your library calls. For example, to retry additional
// errors, you can write a custom func that wraps ShouldRetry and also specifies
// additional errors that should return true.
func ShouldRetry(err error) bool {
if err == nil {
return false
}
Expand Down Expand Up @@ -131,7 +140,7 @@ func shouldRetry(err error) bool {
}
// Unwrap is only supported in go1.13.x+
if e, ok := err.(interface{ Unwrap() error }); ok {
return shouldRetry(e.Unwrap())
return ShouldRetry(e.Unwrap())
}
return false
}
2 changes: 1 addition & 1 deletion storage/invoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func TestShouldRetry(t *testing.T) {
},
} {
t.Run(test.desc, func(s *testing.T) {
got := shouldRetry(test.inputErr)
got := ShouldRetry(test.inputErr)

if got != test.shouldRetry {
s.Errorf("got %v, want %v", got, test.shouldRetry)
Expand Down
7 changes: 4 additions & 3 deletions storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -1875,8 +1875,8 @@ func (ws *withPolicy) apply(config *retryConfig) {

// WithErrorFunc allows users to pass a custom function to the retryer. Errors
// will be retried if and only if `shouldRetry(err)` returns true.
// By default, the following errors are retried (see invoke.go for the default
// shouldRetry function):
// By default, the following errors are retried (see ShouldRetry for the default
// function):
//
// - HTTP responses with codes 408, 429, 502, 503, and 504.
//
Expand All @@ -1887,7 +1887,8 @@ func (ws *withPolicy) apply(config *retryConfig) {
// - Wrapped versions of these errors.
//
// This option can be used to retry on a different set of errors than the
// default.
// default. Users can use the default ShouldRetry function inside their custom
// function if they only want to make minor modifications to default behavior.
func WithErrorFunc(shouldRetry func(err error) bool) RetryOption {
return &withErrorFunc{
shouldRetry: shouldRetry,
Expand Down
28 changes: 14 additions & 14 deletions storage/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1139,10 +1139,10 @@ func TestRetryer(t *testing.T) {
name: "object retryer configures retry",
objectOptions: []RetryOption{
WithPolicy(RetryAlways),
WithErrorFunc(shouldRetry),
WithErrorFunc(ShouldRetry),
},
want: &retryConfig{
shouldRetry: shouldRetry,
shouldRetry: ShouldRetry,
policy: RetryAlways,
},
},
Expand All @@ -1155,15 +1155,15 @@ func TestRetryer(t *testing.T) {
Multiplier: 6,
}),
WithPolicy(RetryAlways),
WithErrorFunc(shouldRetry),
WithErrorFunc(ShouldRetry),
},
want: &retryConfig{
backoff: &gax.Backoff{
Initial: time.Minute,
Max: time.Hour,
Multiplier: 6,
},
shouldRetry: shouldRetry,
shouldRetry: ShouldRetry,
policy: RetryAlways,
},
},
Expand All @@ -1176,15 +1176,15 @@ func TestRetryer(t *testing.T) {
Multiplier: 6,
}),
WithPolicy(RetryAlways),
WithErrorFunc(shouldRetry),
WithErrorFunc(ShouldRetry),
},
want: &retryConfig{
backoff: &gax.Backoff{
Initial: time.Minute,
Max: time.Hour,
Multiplier: 6,
},
shouldRetry: shouldRetry,
shouldRetry: ShouldRetry,
policy: RetryAlways,
},
},
Expand All @@ -1195,11 +1195,11 @@ func TestRetryer(t *testing.T) {
},
objectOptions: []RetryOption{
WithPolicy(RetryNever),
WithErrorFunc(shouldRetry),
WithErrorFunc(ShouldRetry),
},
want: &retryConfig{
policy: RetryNever,
shouldRetry: shouldRetry,
shouldRetry: ShouldRetry,
},
},
{
Expand All @@ -1209,11 +1209,11 @@ func TestRetryer(t *testing.T) {
},
objectOptions: []RetryOption{
WithPolicy(RetryNever),
WithErrorFunc(shouldRetry),
WithErrorFunc(ShouldRetry),
},
want: &retryConfig{
policy: RetryNever,
shouldRetry: shouldRetry,
shouldRetry: ShouldRetry,
},
},
{
Expand All @@ -1231,11 +1231,11 @@ func TestRetryer(t *testing.T) {
Initial: time.Nanosecond,
Max: time.Microsecond,
}),
WithErrorFunc(shouldRetry),
WithErrorFunc(ShouldRetry),
},
want: &retryConfig{
policy: RetryAlways,
shouldRetry: shouldRetry,
shouldRetry: ShouldRetry,
backoff: &gax.Backoff{
Initial: time.Nanosecond,
Max: time.Microsecond,
Expand Down Expand Up @@ -1268,7 +1268,7 @@ func TestRetryer(t *testing.T) {
name: "object retryer does not override bucket retryer if option is not set",
bucketOptions: []RetryOption{
WithPolicy(RetryNever),
WithErrorFunc(shouldRetry),
WithErrorFunc(ShouldRetry),
},
objectOptions: []RetryOption{
WithBackoff(gax.Backoff{
Expand All @@ -1278,7 +1278,7 @@ func TestRetryer(t *testing.T) {
},
want: &retryConfig{
policy: RetryNever,
shouldRetry: shouldRetry,
shouldRetry: ShouldRetry,
backoff: &gax.Backoff{
Initial: time.Nanosecond,
Max: time.Second,
Expand Down