github.com/yorinasub17/go-cloud@v0.27.40/internal/retry/retry.go (about) 1 // Copyright 2018 The Go Cloud Development Kit Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package retry provides retry logic. 16 package retry // import "gocloud.dev/internal/retry" 17 18 import ( 19 "context" 20 "fmt" 21 "time" 22 23 "github.com/googleapis/gax-go/v2" 24 ) 25 26 // Call calls the supplied function f repeatedly, using the isRetryable function and 27 // the provided backoff parameters to control the repetition. 28 // 29 // When f returns nil, Call immediately returns nil. 30 // 31 // When f returns an error for which isRetryable returns false, Call immediately 32 // returns that error. 33 // 34 // When f returns an error for which isRetryable returns true, Call sleeps for the 35 // provided backoff value and invokes f again. 36 // 37 // When the provided context is done, Retry returns a ContextError that includes both 38 // ctx.Error() and the last error returned by f, or nil if there isn't one. 39 func Call(ctx context.Context, bo gax.Backoff, isRetryable func(error) bool, f func() error) error { 40 return call(ctx, bo, isRetryable, f, gax.Sleep) 41 } 42 43 // Split out for testing. 44 func call(ctx context.Context, bo gax.Backoff, isRetryable func(error) bool, f func() error, 45 sleep func(context.Context, time.Duration) error) error { 46 // Do nothing if context is done on entry. 47 if err := ctx.Err(); err != nil { 48 return &ContextError{CtxErr: err} 49 } 50 for { 51 err := f() 52 if err == nil { 53 return nil 54 } 55 if !isRetryable(err) { 56 return err 57 } 58 if cerr := sleep(ctx, bo.Pause()); cerr != nil { 59 return &ContextError{CtxErr: cerr, FuncErr: err} 60 } 61 } 62 } 63 64 // A ContextError contains both a context error (either context.Canceled or 65 // context.DeadlineExceeded), and the last error from the function being retried, 66 // or nil if the function was never called. 67 type ContextError struct { 68 CtxErr error // The error obtained from ctx.Err() 69 FuncErr error // The error obtained from the function being retried, or nil 70 } 71 72 func (e *ContextError) Error() string { 73 return fmt.Sprintf("%v; last error: %v", e.CtxErr, e.FuncErr) 74 } 75 76 // Is returns true iff one of the two errors held in e is equal to target. 77 func (e *ContextError) Is(target error) bool { 78 return e.CtxErr == target || e.FuncErr == target 79 }