Skip to content

Commit cad26ac

Browse files
committed
chore: fetcher will change duration to achieve fast retry when the update failed with a 2x factor step from 1s to interval
1 parent f328203 commit cad26ac

File tree

2 files changed

+31
-3
lines changed

2 files changed

+31
-3
lines changed

component/resource/fetcher.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/metacubex/mihomo/common/utils"
10+
"github.com/metacubex/mihomo/component/slowdown"
1011
types "github.com/metacubex/mihomo/constant/provider"
1112
"github.com/metacubex/mihomo/log"
1213

@@ -29,6 +30,7 @@ type Fetcher[V any] struct {
2930
onUpdate func(V)
3031
watcher *fswatch.Watcher
3132
loadBufMutex sync.Mutex
33+
backoff slowdown.Backoff
3234
}
3335

3436
func (f *Fetcher[V]) Name() string {
@@ -83,6 +85,7 @@ func (f *Fetcher[V]) Initial() (V, error) {
8385
func (f *Fetcher[V]) Update() (V, bool, error) {
8486
buf, hash, err := f.vehicle.Read(f.ctx, f.hash)
8587
if err != nil {
88+
f.backoff.AddAttempt() // add a failed attempt to backoff
8689
return lo.Empty[V](), false, err
8790
}
8891
return f.loadBuf(buf, hash, f.vehicle.Type() != types.File)
@@ -111,8 +114,10 @@ func (f *Fetcher[V]) loadBuf(buf []byte, hash utils.HashType, updateFile bool) (
111114

112115
contents, err := f.parser(buf)
113116
if err != nil {
117+
f.backoff.AddAttempt() // add a failed attempt to backoff
114118
return lo.Empty[V](), false, err
115119
}
120+
f.backoff.Reset() // no error, reset backoff
116121

117122
if updateFile {
118123
if err = f.vehicle.Write(buf); err != nil {
@@ -147,14 +152,25 @@ func (f *Fetcher[V]) pullLoop(forceUpdate bool) {
147152
log.Warnln("[Provider] %s not updated for a long time, force refresh", f.Name())
148153
f.updateWithLog()
149154
}
155+
if attempt := f.backoff.Attempt(); attempt > 0 { // f.Update() was failed, decrease the interval from backoff to achieve fast retry
156+
if duration := f.backoff.ForAttempt(attempt); duration < initialInterval {
157+
initialInterval = duration
158+
}
159+
}
150160

151161
timer := time.NewTimer(initialInterval)
152162
defer timer.Stop()
153163
for {
154164
select {
155165
case <-timer.C:
156-
timer.Reset(f.interval)
157166
f.updateWithLog()
167+
interval := f.interval
168+
if attempt := f.backoff.Attempt(); attempt > 0 { // f.Update() was failed, decrease the interval from backoff to achieve fast retry
169+
if duration := f.backoff.ForAttempt(attempt); duration < interval {
170+
interval = duration
171+
}
172+
}
173+
timer.Reset(interval)
158174
case <-f.ctx.Done():
159175
return
160176
}
@@ -212,5 +228,11 @@ func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicl
212228
parser: parser,
213229
onUpdate: onUpdate,
214230
interval: interval,
231+
backoff: slowdown.Backoff{
232+
Factor: 2,
233+
Jitter: false,
234+
Min: time.Second,
235+
Max: interval,
236+
},
215237
}
216238
}

component/slowdown/backoff.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ package slowdown
44

55
import (
66
"math"
7-
"math/rand"
87
"sync/atomic"
98
"time"
9+
10+
"github.com/metacubex/randv2"
1011
)
1112

1213
// Backoff is a time.Duration counter, starting at Min. After every call to
@@ -63,7 +64,7 @@ func (b *Backoff) ForAttempt(attempt float64) time.Duration {
6364
minf := float64(min)
6465
durf := minf * math.Pow(factor, attempt)
6566
if b.Jitter {
66-
durf = rand.Float64()*(durf-minf) + minf
67+
durf = randv2.Float64()*(durf-minf) + minf
6768
}
6869
//ensure float64 wont overflow int64
6970
if durf > maxInt64 {
@@ -90,6 +91,11 @@ func (b *Backoff) Attempt() float64 {
9091
return float64(b.attempt.Load())
9192
}
9293

94+
// AddAttempt adds one to the attempt counter.
95+
func (b *Backoff) AddAttempt() {
96+
b.attempt.Add(1)
97+
}
98+
9399
// Copy returns a backoff with equals constraints as the original
94100
func (b *Backoff) Copy() *Backoff {
95101
return &Backoff{

0 commit comments

Comments
 (0)