Skip to content

Commit 2572447

Browse files
authored
Engine: refactor task retry backoff logic (#123)
* Engine: refactor task retry backoff logic * Backoff README
1 parent 6070417 commit 2572447

File tree

5 files changed

+53
-17
lines changed

5 files changed

+53
-17
lines changed

Diff for: README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,11 @@ steps:
298298
- `name`: a unique identifier
299299
- `description`: a human readable sentence to convey the step's intent
300300
- `dependencies`: a list of step names on which this step waits before running
301-
- `retry_pattern`: (seconds|minutes|hours) define on what temporal order of magnitude the re-runs of this step should be spread
301+
- `retry_pattern`: (`seconds`, `minutes`, `hours`) define on what temporal order of magnitude the re-runs of this step should be spread (default = `seconds`)
302+
303+
<p align="center">
304+
<img src="./assets/img/utask_backoff.png" width="70%">
305+
</p>
302306

303307
#### Action
304308

Diff for: assets/img/utask_backoff.png

219 KB
Loading

Diff for: engine/engine.go

+44-15
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8-
"math"
98
"strings"
109
"time"
1110

1211
"github.com/cenkalti/backoff"
1312
"github.com/ghodss/yaml"
13+
expbk "github.com/jpillora/backoff"
1414
"github.com/juju/errors"
1515
"github.com/loopfz/gadgeto/zesty"
1616
"github.com/ovh/configstore"
@@ -820,25 +820,54 @@ func minDuration(a, b time.Duration) time.Duration {
820820
return b
821821
}
822822

823-
// for retryCount: 1, 2, 3, 4, 5, 6......
824-
// seconds: 10, 17, 21, 24, 26, 28.....
825-
// minutes and hours: 1, 1, 7, 11, 14, 16.....
826-
func computeDelay(d time.Duration, rc int) time.Duration {
827-
// shorter two first retries for "minutes" and "hours" retry patterns
828-
if d >= time.Minute {
829-
if rc <= 2 {
830-
return d
831-
}
832-
rc--
823+
const maxMinutes = 30 * time.Minute
824+
825+
// 1m36s, 3m12s, 4m48s, 6m24s, 8m0s, 9m36s, 11m12s, 12m48s, 14m24s, 16m0s
826+
// 17m36s, 19m12s, 20m48s, 22m24s, 24m0s, 25m36s, 27m12s, 28m48s, 30m0s
827+
// 30m0s, 30m0s, ...
828+
// cap 30m0s is reached at 20th retry
829+
func computeDelayMinutesLinear(d time.Duration, rc int) time.Duration {
830+
ret := time.Duration((float64(1.6) * float64(rc)) * float64(d))
831+
if ret >= maxMinutes {
832+
ret = maxMinutes
833833
}
834-
ret := time.Duration(float64(d) * math.Log(float64(rc)) * 10)
835-
// baseline 10s for "seconds" retry pattern
836-
if d < time.Minute {
837-
ret += 10 * d
834+
return ret
835+
}
836+
837+
const maxHours = 3 * time.Hour
838+
839+
// 1h, 1h10, 1h20, 1h30, ... 3h, 3h, 3h, ...
840+
// cap 3h is reached at 13th retry
841+
func computeDelayHoursLinear(d time.Duration, rc int) time.Duration {
842+
rc--
843+
ret := d + (time.Duration(rc) * 10 * time.Minute)
844+
if ret >= maxHours {
845+
ret = maxHours
838846
}
839847
return ret
840848
}
841849

850+
var expBackoff = expbk.Backoff{Min: 8 * time.Second, Max: 10 * time.Minute, Factor: 1.25}
851+
852+
// 10s, 12.5s, 15.625s, 19.53125s, 24.4140625s, 30.517578125s, 38.146972656s, 47.68371582s
853+
// 59.604644775s, 1m14.505805969s, 1m33.132257461s, 1m56.415321826s, 2m25.519152283s
854+
// 3m1.898940354s, 3m47.373675443s, 4m44.217094304s, 5m55.27136788s, 7m24.08920985s, 9m15.111512312s
855+
// 10m0s, 10m0s, 10m0s, ...
856+
// the 10m0s cap is reached at the 20th retry
857+
func computeDelaySecondsExponential(d time.Duration, rc int) time.Duration {
858+
return expBackoff.ForAttempt(float64(rc))
859+
}
860+
861+
func computeDelay(d time.Duration, rc int) time.Duration {
862+
switch d {
863+
case time.Minute:
864+
return computeDelayMinutesLinear(d, rc)
865+
case time.Hour:
866+
return computeDelayHoursLinear(d, rc)
867+
}
868+
return computeDelaySecondsExponential(time.Second, rc)
869+
}
870+
842871
func resolutionStateSetter(res *resolution.Resolution, modifiedSteps map[string]bool) step.StateSetter {
843872
return func(step, state, message string) {
844873
if _, ok := res.Steps[step]; ok {

Diff for: go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
github.com/huandu/xstrings v1.2.0 // indirect
2323
github.com/imdario/mergo v0.3.7 // indirect
2424
github.com/jinzhu/now v1.0.1 // indirect
25+
github.com/jpillora/backoff v1.0.0
2526
github.com/juju/errors v0.0.0-20190207033735-e65537c515d7
2627
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
2728
github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect
@@ -57,7 +58,7 @@ require (
5758
github.com/spf13/viper v1.4.0
5859
github.com/stretchr/testify v1.4.0
5960
github.com/tjarratt/babble v0.0.0-20191126185718-ccc47f626248 // indirect
60-
github.com/ugorji/go/codec v1.1.7 // indirect
61+
github.com/ugorji/go v1.1.7 // indirect
6162
github.com/wI2L/fizz v0.0.0-20190425144348-6274bc96d962
6263
github.com/ziutek/mymysql v1.5.4 // indirect
6364
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392

Diff for: go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
108108
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
109109
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
110110
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
111+
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
112+
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
111113
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
112114
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
113115
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=

0 commit comments

Comments
 (0)