Files
VictoriaMetrics/lib/timeutil/backoff_timer.go
Vadim Alekseev bc708c8568 lib/timeutil: introduce backoff timer struct (#10714)
### Describe Your Changes

I noticed that the backoff timer logic is repeated across multiple
packages. I've implemented a universal wrapper to avoid duplicating this
logic. This structure is already [actively
used](2aa0ea10bb/app/vlagent/kubernetescollector/backoff_timer.go (L11))
for the Kubernetes Collector in vlagent and can be reused in vlagent's
remotewrite. I've also included a usage example in this PR so you can
evaluate its utility.

### Checklist

The following checks are **mandatory**:

- [X] My change adheres to [VictoriaMetrics contributing
guidelines](https://docs.victoriametrics.com/victoriametrics/contributing/#pull-request-checklist).
- [X] My change adheres to [VictoriaMetrics development
goals](https://docs.victoriametrics.com/victoriametrics/goals/).
2026-04-02 12:31:28 +02:00

69 lines
1.5 KiB
Go

package timeutil
import (
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/timerpool"
)
// BackoffTimer implements an exponential backoff timer with jitter.
type BackoffTimer struct {
min time.Duration
max time.Duration
current time.Duration
}
// NewBackoffTimer returns a new BackoffTimer initialized with the given minDelay and maxDelay.
func NewBackoffTimer(minDelay, maxDelay time.Duration) *BackoffTimer {
if maxDelay < minDelay {
minDelay = maxDelay
}
return &BackoffTimer{
min: minDelay,
max: maxDelay,
current: minDelay,
}
}
// Wait sleeps for the current delay with jitter, doubling the delay for the next Wait.
// Use CurrentDelay to get the current backoff duration.
//
// Wait returns false if stopCh is closed.
func (bt *BackoffTimer) Wait(stopCh <-chan struct{}) bool {
v := AddJitterToDuration(bt.current)
bt.current *= 2
if bt.current > bt.max {
bt.current = bt.max
}
timer := timerpool.Get(v)
defer timerpool.Put(timer)
select {
case <-stopCh:
return false
case <-timer.C:
return true
}
}
// CurrentDelay returns the current backoff duration.
func (bt *BackoffTimer) CurrentDelay() time.Duration {
return bt.current
}
// SetDelay overrides the current delay. Useful for respecting Retry-After headers.
func (bt *BackoffTimer) SetDelay(d time.Duration) {
if d < bt.min {
d = bt.min
}
if d > bt.max {
d = bt.max
}
bt.current = d
}
// Reset sets the backoff delay to its minimum.
func (bt *BackoffTimer) Reset() {
bt.current = bt.min
}