-
Notifications
You must be signed in to change notification settings - Fork 1
/
singleflight.go
67 lines (59 loc) · 1.29 KB
/
singleflight.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package helpers
import (
"context"
"fmt"
"time"
"github.com/pkg/errors"
"golang.org/x/sync/singleflight"
)
var (
singleFlightGroup = new(singleflight.Group)
waitTime = time.Duration(100) * time.Millisecond
maxWaitTime = time.Duration(2000) * time.Millisecond
)
func SingleDoChan[T any](ctx context.Context, key string, call func() (T, error), retry int, ttl ...time.Duration) (data T, err error) {
result := singleFlightGroup.DoChan(key, func() (result interface{}, err error) {
defer func() {
if e := recover(); e != nil {
err = errors.WithStack(fmt.Errorf("%v", e))
}
}()
if len(ttl) > 0 {
forgetTimer := time.AfterFunc(ttl[0], func() {
singleFlightGroup.Forget(key)
})
defer forgetTimer.Stop()
}
for i := 0; i <= retry; i++ {
result, err = call()
if err == nil {
return result, nil
}
if i == retry {
return nil, err
}
waitTime := JitterBackoff(waitTime, maxWaitTime, i)
select {
case <-time.After(waitTime):
case <-ctx.Done():
return nil, errors.WithStack(ctx.Err())
}
}
return nil, err
})
select {
case r := <-result:
if r.Err != nil {
err = r.Err
return
}
val, ok := r.Val.(T)
if !ok {
return
}
return val, nil
case <-ctx.Done():
err = errors.WithStack(ctx.Err())
return
}
}