go / jitter
I use a ticker with jitter to avoid thundering herd problems when many clients poll or retry at the same interval.
The problem
When multiple clients use the same interval for polling or retries, they can synchronize and hit a server simultaneously:
// 100 clients all polling every 30 seconds
// will spike load every 30 seconds
ticker := time.NewTicker(30 * time.Second)
for range ticker.C {
poll()
}
The pattern
Add random variation to each interval:
type Jitter struct {
Base time.Duration
Deviation time.Duration
}
func (j Jitter) Duration() time.Duration {
base := j.Base - j.Deviation
jitter := time.Duration(rand.Int64N(int64(2 * j.Deviation)))
return base + jitter
}
A Jitter{Base: 30*time.Second, Deviation: 5*time.Second} produces
durations uniformly distributed between 25 and 35 seconds.
Ticker with jitter
Wrap time.Timer to create a ticker that varies each interval:
type TickerWithJitter struct {
C chan time.Time
jitter Jitter
stop func()
}
func NewTickerWithJitter(base, deviation time.Duration) *TickerWithJitter {
ctx, cancel := context.WithCancel(context.Background())
t := &TickerWithJitter{
C: make(chan time.Time),
jitter: Jitter{Base: base, Deviation: deviation},
stop: cancel,
}
go t.run(ctx)
return t
}
func (t *TickerWithJitter) run(ctx context.Context) {
timer := time.NewTimer(t.jitter.Duration())
defer timer.Stop()
for {
select {
case <-ctx.Done():
return
case now := <-timer.C:
timer.Reset(t.jitter.Duration())
t.C <- now
}
}
}
func (t *TickerWithJitter) Stop() {
t.stop()
}
Usage
// Poll every 30s ± 5s
ticker := NewTickerWithJitter(30*time.Second, 5*time.Second)
defer ticker.Stop()
for range ticker.C {
poll()
}
When to use
- Polling external services from multiple instances
- Periodic background tasks in distributed systems
- Cache refresh intervals
- Health checks from many clients
When not to use
- When precise timing is required
- Single-client scenarios where thundering herd isn't a concern
- Very short intervals where jitter overhead matters
See backoff for retry delay strategies and sleepctx for context-aware sleeping.