Skip to content

Commit 4e551ec

Browse files
fix: jitter backoff strategy reset (#9342) (#9459)
* fix: jitter backoff strategy reset * add PR to changelog fragment (cherry picked from commit 23236ef) Co-authored-by: Ruben Ruiz de Gauna <[email protected]>
1 parent a2149d4 commit 4e551ec

File tree

3 files changed

+93
-8
lines changed

3 files changed

+93
-8
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: bug-fix
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: Fixed jitter backoff strategy reset
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
#description:
20+
21+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
22+
component: elastic-agent
23+
24+
# PR URL; optional; the PR number that added the changeset.
25+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
26+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
27+
# Please provide it if you are adding a fragment for a different PR.
28+
pr: https://github.com/elastic/elastic-agent/pull/9342
29+
30+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
31+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
32+
issue: https://github.com/elastic/elastic-agent/issues/8864

internal/pkg/core/backoff/backoff_test.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func TestCloseChannel(t *testing.T) {
3232
c := make(chan struct{})
3333
b := f(c)
3434
close(c)
35-
assert.False(t, b.Wait(), "should return false because the channel shuld get closed faster than the next wait duration")
35+
assert.False(t, b.Wait(), "should return false because the channel should get closed faster than the next wait duration")
3636
})
3737
}
3838
}
@@ -96,3 +96,46 @@ func TestNextWait(t *testing.T) {
9696
})
9797
}
9898
}
99+
100+
func TestReset(t *testing.T) {
101+
init := time.Millisecond
102+
max := 5 * time.Second
103+
104+
tests := map[string]factory{
105+
"ExpBackoff": func(done <-chan struct{}) Backoff {
106+
return NewExpBackoff(done, init, max)
107+
},
108+
"EqualJitterBackoff": func(done <-chan struct{}) Backoff {
109+
// Use a custom randFn to ensure the jitter value is always the same for testing
110+
// purposes. Constructor uses rand.N which is not deterministic.
111+
bo := &EqualJitterBackoff{
112+
done: done,
113+
init: init,
114+
max: max,
115+
// force the random function to return the max value for deterministic behavior
116+
randFn: func(t time.Duration) time.Duration { return t },
117+
}
118+
bo.Reset()
119+
return bo
120+
},
121+
}
122+
123+
for name, f := range tests {
124+
t.Run(name, func(t *testing.T) {
125+
c := make(chan struct{})
126+
b := f(c)
127+
128+
startWait := b.NextWait()
129+
130+
b.Wait()
131+
nextWait := b.NextWait()
132+
t.Logf("startWait: %s nextWait: %s", startWait, nextWait)
133+
assert.Greater(t, nextWait, startWait, "nextWait value did not increase after Wait")
134+
135+
b.Reset()
136+
restartWait := b.NextWait()
137+
t.Logf("startWait: %s restartWait: %s", startWait, restartWait)
138+
assert.Equal(t, startWait, restartWait, "wait value did not reset to initial value after reset")
139+
})
140+
}
141+
}

internal/pkg/core/backoff/equal_jitter.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import (
99
"time"
1010
)
1111

12+
// randFn defines a signature for functions that return a random duration.
13+
// It's used to allow injecting a deterministic random function for testing purposes.
14+
type randFn func(n time.Duration) time.Duration
15+
1216
// EqualJitterBackoff implements an equal jitter strategy, meaning the wait time will consist of two parts,
1317
// the first will be exponential and the other half will be random and will provide the jitter
1418
// necessary to distribute the wait on remote endpoint.
@@ -21,23 +25,29 @@ type EqualJitterBackoff struct {
2125
nextRand time.Duration
2226

2327
last time.Time
28+
29+
// Expose randFn to ensure the jitter is always the same for testing
30+
// purposes. Constructor uses rand.N which is not deterministic.
31+
randFn randFn
2432
}
2533

2634
// NewEqualJitterBackoff returns a new EqualJitter object.
2735
func NewEqualJitterBackoff(done <-chan struct{}, init, max time.Duration) Backoff {
28-
return &EqualJitterBackoff{
29-
duration: init * 2, // Allow to sleep at least the init period on the first wait.
30-
done: done,
31-
init: init,
32-
max: max,
33-
nextRand: rand.N(init),
36+
bo := &EqualJitterBackoff{
37+
done: done,
38+
init: init,
39+
max: max,
40+
randFn: rand.N[time.Duration],
3441
}
42+
bo.Reset()
43+
return bo
3544
}
3645

3746
// Reset resets the duration of the backoff.
3847
func (b *EqualJitterBackoff) Reset() {
3948
// Allow to sleep at least the init period on the first wait.
4049
b.duration = b.init * 2
50+
b.nextRand = b.randFn(b.init)
4151
}
4252

4353
func (b *EqualJitterBackoff) NextWait() time.Duration {
@@ -51,7 +61,7 @@ func (b *EqualJitterBackoff) Wait() bool {
5161
backoff := b.NextWait()
5262

5363
// increase duration for next wait.
54-
b.nextRand = rand.N(b.duration)
64+
b.nextRand = b.randFn(b.duration)
5565
b.duration *= 2
5666
if b.duration > b.max {
5767
b.duration = b.max

0 commit comments

Comments
 (0)