// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package backoff import ( "context" "log" "math/rand" "time" ) const MAX_BACKOFF_MSEC = 30000 type Backoff struct { n int Name string NewTimer func(d time.Duration) *time.Timer } func (b *Backoff) BackOff(ctx context.Context, err error) { if ctx.Err() == nil && err != nil { b.n++ // n^2 backoff timer is a little smoother than the // common choice of 2^n. msec := b.n * b.n * 10 if msec > MAX_BACKOFF_MSEC { msec = MAX_BACKOFF_MSEC } // Randomize the delay between 0.5-1.5 x msec, in order // to prevent accidental "thundering herd" problems. msec = rand.Intn(msec) + msec/2 log.Printf("%s: backoff: %d msec\n", b.Name, msec) newTimer := b.NewTimer if newTimer == nil { newTimer = time.NewTimer } t := newTimer(time.Duration(msec) * time.Millisecond) select { case <-ctx.Done(): t.Stop() case <-t.C: } } else { // not a regular error b.n = 0 } }