diff --git a/thirdparty/delay/delay.go b/thirdparty/delay/delay.go index 8116b300e4f..9d22f591b64 100644 --- a/thirdparty/delay/delay.go +++ b/thirdparty/delay/delay.go @@ -1,10 +1,13 @@ package delay import ( + "math/rand" "sync" "time" ) +var sharedRNG = rand.New(rand.NewSource(time.Now().UnixNano())) + // Delay makes it easy to add (threadsafe) configurable delays to other // objects. type D interface { @@ -23,8 +26,6 @@ type delay struct { t time.Duration } -// TODO func Variable(time.Duration) D returns a delay with probablistic latency - func (d *delay) Set(t time.Duration) time.Duration { d.l.Lock() defer d.l.Unlock() @@ -44,3 +45,61 @@ func (d *delay) Get() time.Duration { defer d.l.Unlock() return d.t } + +// VariableNormal is a delay following a normal distribution +// Notice that to implement the D interface Set can only change the mean delay +// the standard deviation is set only at initialization +func VariableNormal(t, std time.Duration, rng *rand.Rand) D { + if rng == nil { + rng = sharedRNG + } + + v := &variableNormal{ + std: std, + rng: rng, + } + v.t = t + return v +} + +type variableNormal struct { + delay + std time.Duration + rng *rand.Rand +} + +func (d *variableNormal) Wait() { + d.l.RLock() + defer d.l.RUnlock() + randomDelay := time.Duration(d.rng.NormFloat64() * float64(d.std)) + time.Sleep(randomDelay + d.t) +} + +// VariableUniform is a delay following a uniform distribution +// Notice that to implement the D interface Set can only change the minimum delay +// the delta is set only at initialization +func VariableUniform(t, d time.Duration, rng *rand.Rand) D { + if rng == nil { + rng = sharedRNG + } + + v := &variableUniform{ + d: d, + rng: rng, + } + v.t = t + return v +} + +type variableUniform struct { + delay + d time.Duration // max delta + rng *rand.Rand +} + +func (d *variableUniform) Wait() { + d.l.RLock() + defer d.l.RUnlock() + randomDelay := time.Duration(d.rng.Float64() * float64(d.d)) + time.Sleep(randomDelay + d.t) +}