Skip to content

Commit 5760de7

Browse files
authored
Merge pull request moby#5010 from tonistiigi/flightcontrol-timeout-fix
flightcontrol: protect contention timeouts
2 parents 37ab8e8 + dce25cc commit 5760de7

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

util/flightcontrol/flightcontrol.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package flightcontrol
33
import (
44
"context"
55
"io"
6-
"runtime"
6+
"math/rand"
77
"sort"
88
"sync"
99
"time"
@@ -43,13 +43,14 @@ func (g *Group[T]) Do(ctx context.Context, key string, fn func(ctx context.Conte
4343
err = errors.Wrapf(errRetryTimeout, "flightcontrol")
4444
return v, err
4545
}
46-
runtime.Gosched()
4746
if backoff > 0 {
48-
time.Sleep(backoff)
49-
backoff *= 2
47+
backoff = time.Duration(float64(backoff) * 1.2)
5048
} else {
51-
backoff = time.Millisecond
49+
// randomize initial backoff to avoid all goroutines retrying at once
50+
//nolint:gosec // using math/rand pseudo-randomness is acceptable here
51+
backoff = time.Millisecond + time.Duration(rand.Intn(1e7))*time.Nanosecond
5252
}
53+
time.Sleep(backoff)
5354
}
5455
}
5556

util/flightcontrol/flightcontrol_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,28 @@ func TestContention(t *testing.T) {
223223
wg.Wait()
224224
}
225225

226+
func TestMassiveParallel(t *testing.T) {
227+
var retryCount int64
228+
g := &Group[string]{}
229+
eg, ctx := errgroup.WithContext(context.Background())
230+
for i := 0; i < 1000; i++ {
231+
eg.Go(func() error {
232+
_, err := g.Do(ctx, "key", func(ctx context.Context) (string, error) {
233+
return "", errors.Errorf("always fail")
234+
})
235+
if errors.Is(err, errRetryTimeout) {
236+
atomic.AddInt64(&retryCount, 1)
237+
}
238+
return err
239+
})
240+
// magic numbers to increase contention
241+
time.Sleep(5 * time.Microsecond)
242+
}
243+
err := eg.Wait()
244+
assert.Error(t, err)
245+
assert.Equal(t, int64(0), retryCount)
246+
}
247+
226248
func testFunc(wait time.Duration, ret string, counter *int64) func(ctx context.Context) (string, error) {
227249
return func(ctx context.Context) (string, error) {
228250
atomic.AddInt64(counter, 1)

0 commit comments

Comments
 (0)