From c40276b18bb5f20f472883b56da6d2d23c041c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?France=CC=81=20Wilke?= <francewilke@gmail.com> Date: Tue, 12 Jul 2022 12:19:34 +0200 Subject: [PATCH] Add IsRetryableError function --- errors/error.go | 82 +++++++++++++++++++++++++++++------------------- errors/errors.go | 5 ++- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/errors/error.go b/errors/error.go index 658e36e..5765e75 100644 --- a/errors/error.go +++ b/errors/error.go @@ -2,12 +2,13 @@ package errors import ( "fmt" + "net/http" "path" "strconv" "strings" ) -//CustomError implements the following interfaces: +// CustomError implements the following interfaces: // error // github.com/pkg/errors: Cause type CustomError struct { @@ -17,7 +18,7 @@ type CustomError struct { cause error } -//implement interface error: +// implement interface error: func (err CustomError) Error() string { return err.Formatted(FormattingOptions{Causes: false}) } @@ -29,7 +30,7 @@ func Is(e1, e2 error) bool { return e1.Error() == e2.Error() } -//Is() compares the message string of this or any cause to match the specified error message +// Is() compares the message string of this or any cause to match the specified error message func (err CustomError) Is(specificError error) bool { if err.message == specificError.Error() { return true @@ -42,7 +43,7 @@ func (err CustomError) Is(specificError error) bool { return false } -//implement github.com/pkg/errors: Cause +// implement github.com/pkg/errors: Cause func (err CustomError) Cause() error { return err.cause } @@ -55,7 +56,7 @@ func HTTPCode(err error) int { } func (err CustomError) Code() int { - //find http error code - returning the smallest code in the stack of causes (excluding code==0) + // find http error code - returning the smallest code in the stack of causes (excluding code==0) code := err.code if err.cause != nil { if causeWithCode, ok := err.cause.(ErrorWithCause); ok { @@ -77,11 +78,11 @@ func (err CustomError) Description() Description { if err.cause != nil { causeWithStack, ok := err.cause.(*CustomError) if !ok { - //external cause without our stack - //if github.com/pkg/errors, we can still get caller reference + // external cause without our stack + // if github.com/pkg/errors, we can still get caller reference desc.Cause = pkgDescription(0, err.cause) } else { - //cause has our stack + // cause has our stack causeDesription := causeWithStack.Description() desc.Cause = &causeDesription } @@ -93,9 +94,9 @@ func (err CustomError) Format(s fmt.State, v rune) { s.Write([]byte( err.Formatted( FormattingOptions{ - Causes: (v == 'v' || v == 'c'), //include causes for %c and %v, s is only this error - NewLines: v == 'v', //use newlines only on v, c is more compact on single line - Source: s.Flag('+'), //include source references when %+v or %+c + Causes: (v == 'v' || v == 'c'), // include causes for %c and %v, s is only this error + NewLines: v == 'v', // use newlines only on v, c is more compact on single line + Source: s.Flag('+'), // include source references when %+v or %+c }, ), )) @@ -108,7 +109,7 @@ type FormattingOptions struct { } func (err CustomError) Formatted(opts FormattingOptions) string { - //start with this error + // start with this error thisError := "" if opts.Source { thisError += fmt.Sprintf("%s/%s(%d): %s() ", @@ -141,10 +142,10 @@ func (err CustomError) Formatted(opts FormattingOptions) string { return thisError + sep + causeWithStack.Formatted(opts) } - //this level does not have our own stack, but we can detect github.com/pkg/errors stack, then: - //note: do not use fmt.Sprintf("%+v", err.cause) because it will repeat the stack if multiple were captured - //note: do not use err.cause.Error() because it does not include any stack - //instead, get first layer that implements it and log it + // this level does not have our own stack, but we can detect github.com/pkg/errors stack, then: + // note: do not use fmt.Sprintf("%+v", err.cause) because it will repeat the stack if multiple were captured + // note: do not use err.cause.Error() because it does not include any stack + // instead, get first layer that implements it and log it return thisError + pkgStack(err.cause, opts) } @@ -155,13 +156,13 @@ func pkgStack(err error, opts FormattingOptions) string { if errWithStackTracer, ok := e.(stackTracer); ok { st := errWithStackTracer.StackTrace() for _, f := range st { - //source := fmt.Sprintf("%n %s %d", f, f, f) - this shows only package name, not fully qualified package name :-( + // source := fmt.Sprintf("%n %s %d", f, f, f) - this shows only package name, not fully qualified package name :-( sources := strings.SplitN(fmt.Sprintf("%+s(%d)\n%n", f, f, f), "\n", 3) - //package <-- sources[0] - //full filename:line <-- source[1] - //function name <-- source[2] + // package <-- sources[0] + // full filename:line <-- source[1] + // function name <-- source[2] - //skip runtime packages + // skip runtime packages if strings.HasPrefix(sources[0], "runtime") { break } @@ -177,7 +178,7 @@ func pkgStack(err error, opts FormattingOptions) string { } return s } else { - //no stack tracer... + // no stack tracer... if opts.NewLines { s += ", because \n\t" } else { @@ -201,7 +202,7 @@ func pkgDescription(level int, err error) *Description { Cause: nil, } - //recursively fill causes first + // recursively fill causes first if errWithCause, ok := err.(ErrorWithCause); ok { causeErr := errWithCause.Cause() if causeErr != nil { @@ -214,31 +215,48 @@ func pkgDescription(level int, err error) *Description { st := errWithStackTracer.StackTrace() for _, f := range st { if tempDesc == nil { - break //stop if no more causes populated + break // stop if no more causes populated } - //source := fmt.Sprintf("%n %s %d", f, f, f) - this shows only package name, not fully qualified package name :-( + // source := fmt.Sprintf("%n %s %d", f, f, f) - this shows only package name, not fully qualified package name :-( sources := strings.SplitN(fmt.Sprintf("%+s\n%d\n%n", f, f, f), "\n", 4) - //package <-- sources[0] - //full filename <-- source[1] + // package <-- sources[0] + // full filename <-- source[1] //line <-- source[2] - //function name <-- source[3] + // function name <-- source[3] - //stop if entering runtime part of the stack + // stop if entering runtime part of the stack if strings.HasPrefix(sources[0], "runtime") { break } tempDesc.Source = &CallerInfo{ Package: sources[0], File: path.Base(sources[1]), - //Line: sources[2], + // Line: sources[2], Function: sources[3], } if i64, err := strconv.ParseInt(sources[2], 10, 64); err == nil { tempDesc.Source.Line = int(i64) } tempDesc = tempDesc.Cause - } //for each stack level - } //if has stack + } // for each stack level + } // if has stack return desc } + +func IsRetryableError(err error) bool { + + code := HTTPCode(err) + + // 429 should always retry + if code == http.StatusTooManyRequests { + return true + } + + // Retry for all except 200s and 400s + if code < 200 || (code >= 300 && code < 400) || code > 500 { + return true + } + + return false +} diff --git a/errors/errors.go b/errors/errors.go index 10397ab..678c54b 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -2,11 +2,10 @@ package errors import ( "fmt" - pkg_errors "github.com/pkg/errors" ) -//extends default golang error interface +// extends default golang error interface type ErrorWithCause interface { error Cause() error @@ -118,7 +117,7 @@ type Description struct { Cause *Description `json:"cause,omitempty"` } -//from github.com/pkg/errors: +// from github.com/pkg/errors: type stackTracer interface { StackTrace() pkg_errors.StackTrace } -- GitLab