From 5732a11f764fd442765bce60dc048e1cf9f592c8 Mon Sep 17 00:00:00 2001 From: Stuart Date: Thu, 2 May 2024 16:23:47 +0100 Subject: [PATCH 1/3] Added convenience functions for setting the retryable flag --- errors.go | 22 ++++++++++++++++++++++ errors_test.go | 15 +++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/errors.go b/errors.go index 737daf7..1a1c48b 100644 --- a/errors.go +++ b/errors.go @@ -384,6 +384,28 @@ func IsRetryable(err error) bool { return false } +// Retryable converts the error to a terror if necessary, and marks it as retryable. +func Retryable(err error) error { + if err == nil { + return nil + } + // Using propagate ensures the error can be converted to a terror + terr := Propagate(err).(*Error) + terr.SetIsRetryable(true) + return terr +} + +// Retryable converts the error to a terror if necessary, and marks it as not retryable. +func NotRetryable(err error) error { + if err == nil { + return nil + } + // Using propagate ensures the error can be converted to a terror + terr := Propagate(err).(*Error) + terr.SetIsRetryable(false) + return terr +} + // Augment adds context to an existing error. // If the error given is not already a terror, a new terror is created. func Augment(err error, context string, params map[string]string) error { diff --git a/errors_test.go b/errors_test.go index 4aa9cd2..2cc9ebb 100644 --- a/errors_test.go +++ b/errors_test.go @@ -214,6 +214,21 @@ func TestIsRetryable(t *testing.T) { assert.False(t, IsRetryable(&testRetryableError{false})) assert.True(t, IsRetryable(&testRetryableError{true})) assert.True(t, IsRetryable(&testRetryableError{true})) + + // Setting using convenience functions + assert.True(t, IsRetryable(Retryable(errors.New("")))) + assert.False(t, IsRetryable(NotRetryable(errors.New("")))) + assert.True(t, IsRetryable(Retryable(Augment(errors.New(""), "", nil)))) + assert.False(t, IsRetryable(NotRetryable(Augment(errors.New(""), "", nil)))) + + // Overriding the default for error types using convenience functions + assert.True(t, IsRetryable(Retryable(BadRequest("", "", nil)))) + assert.True(t, IsRetryable(Retryable(BadResponse("", "", nil)))) + assert.True(t, IsRetryable(Retryable(NotFound("", "", nil)))) + assert.True(t, IsRetryable(Retryable(PreconditionFailed("", "", nil)))) + assert.True(t, IsRetryable(Retryable(NonRetryableInternalService("", "", nil)))) + assert.False(t, IsRetryable(NotRetryable(InternalService("", "", nil)))) + assert.False(t, IsRetryable(NotRetryable(RateLimited("", "", nil)))) } type testRetryableError struct { From d2853e29ca95a02f49c2c446cc11942fb6c7050a Mon Sep 17 00:00:00 2001 From: Stuart Date: Thu, 2 May 2024 16:35:00 +0100 Subject: [PATCH 2/3] Fixed a comment typo --- errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors.go b/errors.go index 1a1c48b..e4575b6 100644 --- a/errors.go +++ b/errors.go @@ -395,7 +395,7 @@ func Retryable(err error) error { return terr } -// Retryable converts the error to a terror if necessary, and marks it as not retryable. +// NotRetryable converts the error to a terror if necessary, and marks it as not retryable. func NotRetryable(err error) error { if err == nil { return nil From 1869f0e4097f76856461db309837cf5bcfe9d086 Mon Sep 17 00:00:00 2001 From: Stuart Date: Thu, 2 May 2024 18:26:55 +0100 Subject: [PATCH 3/3] Updated readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 1eab3a3..1f2a372 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,16 @@ When using the the wrapping functionality (e.g. `Wrap`, `Augment`, `Propagate`), of an error is preserved as expected. Importantly, it is also preserved when constructing a new error from a causal error with `NewInternalWithCause`. +Wrap an error with `Retryable` or `NotRetryable` to set the retryability explicitly. This will +override the retryability derived from the error code. + +```go +retryableErr := terrors.Retryable(terrors.Augment(err, "didn't work, let's try again", nil)) +if terrors.IsRetryable(retryableErr) { + // retry the operation +} +``` + ## API Full API documentation can be found on