74 lines
1.8 KiB
Go
74 lines
1.8 KiB
Go
|
// Package failure implements the Phi Accrual Failure Detector
|
||
|
/*
|
||
|
|
||
|
This package implements the paper "The φ Accrual Failure Detector" (2004)
|
||
|
(available at http://hdl.handle.net/10119/4784).
|
||
|
|
||
|
To use the failure detection algorithm, you need a heartbeat loop that will
|
||
|
call Ping() at regular intervals. At any point, you can call Phi() which will
|
||
|
report how suspicious it is that a heartbeat has not been heard since the last
|
||
|
time Ping() was called.
|
||
|
|
||
|
*/
|
||
|
package failure
|
||
|
|
||
|
import (
|
||
|
"math"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/dgryski/go-onlinestats"
|
||
|
)
|
||
|
|
||
|
// Detector is a failure detector
|
||
|
type Detector struct {
|
||
|
w *onlinestats.Windowed
|
||
|
last time.Time
|
||
|
minSamples int
|
||
|
mu sync.Mutex
|
||
|
}
|
||
|
|
||
|
// New returns a new failure detector that considers the last windowSize
|
||
|
// samples, and ensures there are at least minSamples in the window before
|
||
|
// returning an answer
|
||
|
func New(windowSize, minSamples int) *Detector {
|
||
|
|
||
|
d := &Detector{
|
||
|
w: onlinestats.NewWindowed(windowSize),
|
||
|
minSamples: minSamples,
|
||
|
}
|
||
|
|
||
|
return d
|
||
|
}
|
||
|
|
||
|
// Ping registers a heart-beat at time now
|
||
|
func (d *Detector) Ping(now time.Time) {
|
||
|
d.mu.Lock()
|
||
|
defer d.mu.Unlock()
|
||
|
if !d.last.IsZero() {
|
||
|
d.w.Push(now.Sub(d.last).Seconds())
|
||
|
}
|
||
|
d.last = now
|
||
|
}
|
||
|
|
||
|
// Phi calculates the suspicion level at time 'now' that the remote end has failed
|
||
|
func (d *Detector) Phi(now time.Time) float64 {
|
||
|
d.mu.Lock()
|
||
|
defer d.mu.Unlock()
|
||
|
if d.w.Len() < d.minSamples {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
t := now.Sub(d.last).Seconds()
|
||
|
pLater := 1 - cdf(d.w.Mean(), d.w.Stddev(), t)
|
||
|
phi := -math.Log10(pLater)
|
||
|
|
||
|
return phi
|
||
|
}
|
||
|
|
||
|
// cdf is the cumulative distribution function of a normally distributed random
|
||
|
// variable with the given mean and standard deviation
|
||
|
func cdf(mean, stddev, x float64) float64 {
|
||
|
return 0.5 + 0.5*math.Erf((x-mean)/(stddev*math.Sqrt2))
|
||
|
}
|