Skip to content

Commit 66231aa

Browse files
authored
Merge pull request #211 from flant/feat_exponential_backoff
feat: exponential backoff for hook retries
2 parents 90e20b0 + 72274b7 commit 66231aa

File tree

9 files changed

+83
-3
lines changed

9 files changed

+83
-3
lines changed

.github/workflows/build.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ jobs:
77
build_binary:
88
name: Build shell-operator binary
99
runs-on: ubuntu-latest
10+
env:
11+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
1012
steps:
1113
- name: Set up Go 1.15
1214
uses: actions/setup-go@v2

.github/workflows/dev-build.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ jobs:
5454
- ubuntu
5555
- alpine
5656
runs-on: ubuntu-latest
57+
env:
58+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
5759
if: github.event_name == 'pull_request' && github.event.label.id == 1838515600 # ':robot: build dev images' label
5860
steps:
5961
- uses: actions/checkout@v2

.github/workflows/lint.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ jobs:
88
run_linter:
99
name: Run linter
1010
runs-on: ubuntu-latest
11+
env:
12+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
1113
steps:
1214
- name: Set up Go 1.15
1315
uses: actions/setup-go@v2

.github/workflows/release-build.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ jobs:
2020
- alpine3.11
2121
- alpine3.12
2222
runs-on: [ubuntu-latest]
23+
env:
24+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
2325
steps:
2426
- uses: actions/checkout@v2
2527

.github/workflows/tests.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ jobs:
4646
name: Build and unit tests
4747
if: github.event_name == 'push' && github.event.ref != 'refs/heads/master'
4848
runs-on: ubuntu-latest
49+
env:
50+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
4951
steps:
5052
- name: Set up Go 1.15
5153
uses: actions/setup-go@v2
@@ -132,6 +134,8 @@ jobs:
132134
name: Download modules and build libjq
133135
if: (github.event_name == 'push' && github.event.ref == 'refs/heads/master') || (github.event_name == 'pull_request' && github.event.label.id == 1838578615)
134136
runs-on: ubuntu-latest
137+
env:
138+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
135139
steps:
136140
- name: Set up Go 1.15
137141
uses: actions/setup-go@v2
@@ -208,6 +212,8 @@ jobs:
208212
if: (github.event_name == 'push' && github.event.ref == 'refs/heads/master') || (github.event_name == 'pull_request' && github.event.label.id == 1838578615)
209213
needs: prepare_build_dependencies
210214
runs-on: ubuntu-latest
215+
env:
216+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
211217
steps:
212218
- name: Checkout code
213219
uses: actions/checkout@v2
@@ -277,6 +283,8 @@ jobs:
277283
os: [ubuntu-latest]
278284
k8s_version: [1.16, 1.17]
279285
runs-on: ${{ matrix.os }}
286+
env:
287+
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
280288
steps:
281289
- name: Checkout code
282290
uses: actions/checkout@v2

cmd/shell-operator/main.go

+5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package main
22

33
import (
44
"fmt"
5+
"math/rand"
56
"os"
7+
"time"
68

79
"github.com/flant/shell-operator/pkg/debug"
810
log "github.com/sirupsen/logrus"
@@ -29,8 +31,11 @@ func main() {
2931
startCmd := kpApp.Command("start", "Start shell-operator.").
3032
Default().
3133
Action(func(c *kingpin.ParseContext) error {
34+
// Init logging subsystem.
3235
app.SetupLogging()
3336
log.Infof("%s %s", app.AppName, app.Version)
37+
// Init rand generator.
38+
rand.Seed(time.Now().UnixNano())
3439

3540
defaultOperator := shell_operator.DefaultOperator()
3641
err := shell_operator.InitAndStart(defaultOperator)

pkg/task/queue/task_queue.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package queue
33
import (
44
"context"
55
"fmt"
6-
"github.com/flant/shell-operator/pkg/utils/measure"
76
"os"
87
"strings"
98
"sync"
@@ -13,6 +12,8 @@ import (
1312

1413
"github.com/flant/shell-operator/pkg/metric_storage"
1514
"github.com/flant/shell-operator/pkg/task"
15+
"github.com/flant/shell-operator/pkg/utils/exponential_backoff"
16+
"github.com/flant/shell-operator/pkg/utils/measure"
1617
)
1718

1819
/*
@@ -351,9 +352,9 @@ func (q *TaskQueue) Start() {
351352

352353
switch taskRes.Status {
353354
case "Fail":
355+
// Exponential backoff delay before retry.
356+
nextSleepDelay = exponential_backoff.CalculateDelay(DelayOnFailedTask, t.GetFailureCount())
354357
t.IncrementFailureCount()
355-
// delay before retry
356-
nextSleepDelay = DelayOnFailedTask
357358
q.Status = fmt.Sprintf("sleep after fail for %s", nextSleepDelay.String())
358359
case "Success":
359360
// add tasks after current task in reverse order
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package exponential_backoff
2+
3+
import (
4+
"math"
5+
"math/rand"
6+
"time"
7+
)
8+
9+
const MaxExponentialBackoffDelay = time.Duration(32 * time.Second)
10+
const ExponentialDelayFactor float64 = 2.0 // Each delay is twice longer.
11+
const ExponentialDelayRandomMs = 1000 // Each delay has random additional milliseconds.
12+
13+
// CalculateDelay returns delay distributed from initialDelay to default maxDelay (32s)
14+
//
15+
// Example:
16+
// Retry 0: 5s
17+
// Retry 1: 6s
18+
// Retry 2: 7.8s
19+
// Retry 3: 9.8s
20+
// Retry 4: 13s
21+
// Retry 5: 21s
22+
// Retry 6: 32s
23+
// Retry 7: 32s
24+
func CalculateDelay(initialDelay time.Duration, retryCount int) time.Duration {
25+
return CalculateDelayWithMax(initialDelay, MaxExponentialBackoffDelay, retryCount)
26+
}
27+
28+
func CalculateDelayWithMax(initialDelay time.Duration, maxDelay time.Duration, retryCount int) time.Duration {
29+
if retryCount == 0 {
30+
return initialDelay
31+
}
32+
delayNs := int64(float64(time.Second) * math.Pow(ExponentialDelayFactor, float64(retryCount-1)))
33+
rndDelayMs := rand.Intn(ExponentialDelayRandomMs)
34+
delay := initialDelay + time.Duration(delayNs) + time.Duration(rndDelayMs)*time.Millisecond
35+
delay = delay.Truncate(100 * time.Millisecond)
36+
if delay.Nanoseconds() > maxDelay.Nanoseconds() {
37+
return maxDelay
38+
}
39+
return delay
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package exponential_backoff
2+
3+
import (
4+
"math/rand"
5+
"testing"
6+
"time"
7+
)
8+
9+
func Test_Delay(t *testing.T) {
10+
t.SkipNow()
11+
rand.Seed(time.Now().UnixNano())
12+
for i := 0; i < 16; i++ {
13+
t.Logf("Delay for %02d retry: %s\n",
14+
i,
15+
CalculateDelay(5*time.Second, i),
16+
)
17+
}
18+
}

0 commit comments

Comments
 (0)