-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix #138: separate Action and InterruptibleAction handling
- Loading branch information
Showing
11 changed files
with
160 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,2 @@ | ||
# github: [kamilsk, octolab, octopot] | ||
patreon: octolab | ||
custom: ['https://www.buymeacoffee.com/kamilsk', 'https://click.octolab.net/digitalocean'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package retry | ||
|
||
import "context" | ||
|
||
// TryContext takes an interruptable action and performs it, repetitively, until successful. | ||
// It uses the Context as a Breaker to prevent unnecessary action execution. | ||
// | ||
// Optionally, strategies may be passed that assess whether or not an attempt | ||
// should be made. | ||
// | ||
// TODO:v5 not quite honest implementation | ||
func TryContext( | ||
ctx context.Context, | ||
action func(ctx context.Context, attempt uint) error, | ||
strategies ...func(attempt uint, err error) bool, | ||
) (err error) { | ||
|
||
// TODO:v5 will be removed | ||
defer func() { | ||
if r := recover(); r != nil { | ||
err = result{err, r} | ||
} | ||
}() | ||
|
||
for attempt, repeat, should := uint(0), len(strategies), true; should; attempt++ { | ||
for i := 0; should && i < repeat; i++ { | ||
should = should && ctx.Err() == nil && strategies[i](attempt, err) | ||
} | ||
|
||
if !should && ctx.Err() != nil { | ||
return Interrupted | ||
} | ||
|
||
if should { | ||
err = action(ctx, attempt) | ||
should = err != nil | ||
} | ||
} | ||
|
||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package retry | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestTryContext(t *testing.T) { | ||
type Assert struct { | ||
Attempts uint | ||
Error func(error) bool | ||
} | ||
|
||
tests := map[string]struct { | ||
ctx func() context.Context | ||
strategies []func(attempt uint, err error) bool | ||
error error | ||
assert Assert | ||
}{ | ||
"zero iterations": { | ||
func() context.Context { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
cancel() | ||
return ctx | ||
}, | ||
[]func(attempt uint, err error) bool{delay(delta), limit(10000)}, | ||
errors.New("zero iterations"), | ||
Assert{0, func(err error) bool { return IsInterrupted(err) && err.Error() == string(Interrupted) }}, | ||
}, | ||
"one iteration": { | ||
context.Background, | ||
nil, | ||
nil, | ||
Assert{1, func(err error) bool { return err == nil }}, | ||
}, | ||
"two iterations": { | ||
context.Background, | ||
[]func(attempt uint, err error) bool{limit(2)}, | ||
errors.New("two iterations"), | ||
Assert{2, func(err error) bool { return err != nil && err.Error() == "two iterations" }}, | ||
}, | ||
"three iterations": { | ||
context.Background, | ||
[]func(attempt uint, err error) bool{limit(3)}, | ||
errors.New("three iterations"), | ||
Assert{3, func(err error) bool { return err != nil && err.Error() == "three iterations" }}, | ||
}, | ||
} | ||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
var total uint | ||
action := func(ctx context.Context, attempt uint) error { | ||
total = attempt + 1 | ||
return test.error | ||
} | ||
err := TryContext(test.ctx(), action, test.strategies...) | ||
if !test.assert.Error(err) { | ||
t.Error("fail error assertion") | ||
} | ||
if _, is := IsRecovered(err); is { | ||
t.Error("recovered panic is not expected") | ||
} | ||
if test.assert.Attempts != total { | ||
t.Errorf("expected %d attempts, obtained %d", test.assert.Attempts, total) | ||
} | ||
}) | ||
} | ||
// TODO:v5 will be removed | ||
t.Run("unexpected panic", func(t *testing.T) { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
err := TryContext(ctx, func(context.Context, uint) error { panic("Catch Me If You Can") }) | ||
cause, is := IsRecovered(err) | ||
if !is { | ||
t.Fatal("recovered panic is expected") | ||
} | ||
if !reflect.DeepEqual(cause, "Catch Me If You Can") { | ||
t.Fatal("Catch Me If You Can is expected") | ||
} | ||
cancel() | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
// +build draft | ||
|
||
package internal | ||
|
||
import "github.com/kamilsk/retry/v4" | ||
|
||
// Interface defines a behavior of stateful executor of Actions in parallel. | ||
type Interface interface { | ||
Try(retry.Breaker, retry.Action, ...retry.How) Interface | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.