Skip to content

Commit 6fb08ae

Browse files
authored
Flag for specifying a jitter duration (#18)
1 parent 634d140 commit 6fb08ae

File tree

3 files changed

+40
-9
lines changed

3 files changed

+40
-9
lines changed

README.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,23 @@ This tool is an attempt to solve that broader problem. ⏰
5050
$ retry -help
5151
Usage: retry [flags] command|url
5252
-attempts int
53-
maximum number of attempts (default 3)
53+
maximum number of attempts (default 3)
5454
-backoff
55-
use exponential backoff when sleeping
55+
use exponential backoff when sleeping
5656
-consecutive int
57-
required number of back to back successes
57+
required number of back to back successes
58+
-jitter duration
59+
time range randomly added to sleep
5860
-max-time duration
59-
maximum total time (default 1m0s)
61+
maximum total time (default 1m0s)
6062
-quiet
61-
silence all output
63+
silence all output
6264
-sleep duration
63-
time to sleep between attempts (default 5s)
65+
time to sleep between attempts (default 5s)
6466
-task-time duration
65-
maximum time for a single attempt
67+
maximum time for a single attempt
6668
-version
67-
print the version "1.0.0" and exit
69+
print the version "1.0.0" and exit
6870
```
6971

7072
### Running a command
@@ -123,6 +125,16 @@ The `-backoff` flag is used with `-sleep`, and will double the time delay betwee
123125
> $ retry -sleep=15s -backoff wget https://example.com
124126
> ```
125127
128+
### Random jitter
129+
130+
The `-jitter` flag adds a random time range to the sleep duration. Jitter added on top of exponential backoff.
131+
132+
> Run `cat kubeconfig.yml`, sleep for 15 seconds minimum, plus a random 0-10 seconds between each run.
133+
>
134+
> ```bash
135+
> $ retry -sleep=15s -jitter=10s cat kubeconfig.yml
136+
> ```
137+
126138
### Consecutive successes
127139
128140
The `-consecutive` flag requires a number of successful command runs to occur in a row in order to be considered successful. Useful for health checking a service that is inconsistent until if if fully started.

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func main() {
3131
flag.IntVar(&flags.Attempts, "attempts", 3, "maximum number of attempts")
3232
flag.BoolVar(&flags.Backoff, "backoff", false, "use exponential backoff when sleeping")
3333
flag.IntVar(&flags.Consecutive, "consecutive", 0, "required number of back to back successes")
34+
flag.DurationVar(&flags.Jitter, "jitter", 0, "time range randomly added to sleep")
3435
flag.DurationVar(&flags.TotalTime, "max-time", time.Minute, "maximum total time")
3536
flag.BoolVar(&flags.quiet, "quiet", false, "silence all output")
3637
flag.DurationVar(&flags.Sleep, "sleep", 5*time.Second, "time to sleep between attempts")

retry/retry.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package retry
77
import (
88
"context"
99
"errors"
10+
"math/rand"
1011
"time"
1112
)
1213

@@ -35,6 +36,10 @@ type Spec struct {
3536
// row in order for the task to be considered successful overall.
3637
Consecutive int
3738

39+
// Jitter is the duration range to randomly add to the Sleep time.
40+
// Sleep + [0, Jitter)
41+
Jitter time.Duration
42+
3843
// Sleep is the duration to pause between individual task invocations.
3944
Sleep time.Duration
4045

@@ -89,7 +94,7 @@ func Retry(spec Spec, task Task) error {
8994
}
9095

9196
// Sleep for the specified duration.
92-
snooze := spec.Sleep * time.Duration(multiplier)
97+
snooze := spec.Sleep*time.Duration(multiplier) + jitter(spec.Jitter)
9398
if err := contextSleep(ctxMaxTime, snooze); err != nil {
9499
return ErrExceededTime
95100
}
@@ -117,6 +122,19 @@ func maybeTimed(parent context.Context, timeout time.Duration) (context.Context,
117122
return context.WithTimeout(parent, timeout)
118123
}
119124

125+
// jitter returns a random duration in the range of:
126+
// [0, variance)
127+
func jitter(variance time.Duration) time.Duration {
128+
if variance <= 0 {
129+
return 0
130+
}
131+
132+
// rng is a seeded source capable of generating random values.
133+
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
134+
135+
return time.Duration(rng.Int63n(int64(variance)))
136+
}
137+
120138
// contextSleep is a context-aware sleep. It will sleep for the given timeout,
121139
// but will return early if the given context is cancelled. The return value
122140
// will be nil after a full sleep, and non-nil if the given context was

0 commit comments

Comments
 (0)