2018-02-23 15:02:05 +00:00
|
|
|
// Package up is used to run a function for some duration. If a new function is added while a previous run is
|
|
|
|
|
// still ongoing, nothing new will be executed.
|
2018-02-05 22:00:29 +00:00
|
|
|
package up
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
2020-02-04 05:19:48 -08:00
|
|
|
|
|
|
|
|
"github.com/cenkalti/backoff/v4"
|
2018-02-05 22:00:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Probe is used to run a single Func until it returns true (indicating a target is healthy). If an Func
|
|
|
|
|
// is already in progress no new one will be added, i.e. there is always a maximum of 1 checks in flight.
|
2018-12-01 09:07:03 +00:00
|
|
|
// When failures start to happen we will back off every second failure up to maximum of 4 intervals.
|
2018-02-05 22:00:29 +00:00
|
|
|
type Probe struct {
|
|
|
|
|
sync.Mutex
|
2018-04-13 16:01:12 +01:00
|
|
|
inprogress int
|
2020-02-04 05:19:48 -08:00
|
|
|
expBackoff backoff.ExponentialBackOff
|
2018-02-05 22:00:29 +00:00
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:21:57 +01:00
|
|
|
// Func is used to determine if a target is alive. If so this function must return nil.
|
|
|
|
|
type Func func() error
|
2018-02-05 22:00:29 +00:00
|
|
|
|
2018-08-14 17:55:55 +02:00
|
|
|
// New returns a pointer to an initialized Probe.
|
2018-05-16 22:38:14 +03:00
|
|
|
func New() *Probe { return &Probe{} }
|
2018-02-05 22:00:29 +00:00
|
|
|
|
|
|
|
|
// Do will probe target, if a probe is already in progress this is a noop.
|
2018-05-16 22:38:14 +03:00
|
|
|
func (p *Probe) Do(f Func) {
|
|
|
|
|
p.Lock()
|
|
|
|
|
if p.inprogress != idle {
|
|
|
|
|
p.Unlock()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
p.inprogress = active
|
2020-02-04 05:19:48 -08:00
|
|
|
interval := p.expBackoff.NextBackOff()
|
|
|
|
|
// If exponential backoff has reached the maximum elapsed time (15 minutes),
|
|
|
|
|
// reset it and try again
|
|
|
|
|
if interval == -1 {
|
|
|
|
|
p.expBackoff.Reset()
|
|
|
|
|
interval = p.expBackoff.NextBackOff()
|
|
|
|
|
}
|
2018-05-16 22:38:14 +03:00
|
|
|
p.Unlock()
|
|
|
|
|
// Passed the lock. Now run f for as long it returns false. If a true is returned
|
|
|
|
|
// we return from the goroutine and we can accept another Func to run.
|
|
|
|
|
go func() {
|
2018-12-01 09:07:03 +00:00
|
|
|
i := 1
|
2018-05-16 22:38:14 +03:00
|
|
|
for {
|
|
|
|
|
if err := f(); err == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(interval)
|
2018-02-05 22:00:29 +00:00
|
|
|
p.Lock()
|
2018-05-16 22:38:14 +03:00
|
|
|
if p.inprogress == stop {
|
2018-02-05 22:00:29 +00:00
|
|
|
p.Unlock()
|
2018-05-16 22:38:14 +03:00
|
|
|
return
|
2018-02-05 22:00:29 +00:00
|
|
|
}
|
|
|
|
|
p.Unlock()
|
2018-12-01 09:07:03 +00:00
|
|
|
i++
|
2018-05-16 22:38:14 +03:00
|
|
|
}
|
2018-02-05 22:00:29 +00:00
|
|
|
|
2018-05-16 22:38:14 +03:00
|
|
|
p.Lock()
|
|
|
|
|
p.inprogress = idle
|
|
|
|
|
p.Unlock()
|
|
|
|
|
}()
|
|
|
|
|
}
|
2018-04-13 16:01:12 +01:00
|
|
|
|
2018-05-16 22:38:14 +03:00
|
|
|
// Stop stops the probing.
|
|
|
|
|
func (p *Probe) Stop() {
|
|
|
|
|
p.Lock()
|
|
|
|
|
p.inprogress = stop
|
|
|
|
|
p.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start will initialize the probe manager, after which probes can be initiated with Do.
|
2020-02-04 05:19:48 -08:00
|
|
|
// Initializes exponential backoff using the given interval duration
|
2018-12-01 09:07:03 +00:00
|
|
|
func (p *Probe) Start(interval time.Duration) {
|
2018-05-16 22:38:14 +03:00
|
|
|
p.Lock()
|
2020-02-04 05:19:48 -08:00
|
|
|
eB := &backoff.ExponentialBackOff{
|
|
|
|
|
InitialInterval: interval,
|
|
|
|
|
RandomizationFactor: backoff.DefaultRandomizationFactor,
|
|
|
|
|
Multiplier: backoff.DefaultMultiplier,
|
2020-02-06 19:28:53 +01:00
|
|
|
MaxInterval: 15 * time.Second,
|
|
|
|
|
MaxElapsedTime: 2 * time.Minute,
|
2020-02-04 05:19:48 -08:00
|
|
|
Stop: backoff.Stop,
|
|
|
|
|
Clock: backoff.SystemClock,
|
|
|
|
|
}
|
|
|
|
|
p.expBackoff = *eB
|
|
|
|
|
p.expBackoff.Reset()
|
2018-05-16 22:38:14 +03:00
|
|
|
p.Unlock()
|
2018-02-05 22:00:29 +00:00
|
|
|
}
|
2018-04-13 16:01:12 +01:00
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
idle = iota
|
|
|
|
|
active
|
|
|
|
|
stop
|
|
|
|
|
)
|