Unverified Commit 4e669686 authored by Hannah Howard's avatar Hannah Howard Committed by GitHub

Merge pull request #1 from ipfs/feat/refactor-wait-time

Feat/refactor wait time
parents d0bfb593 d494d3fd
...@@ -2,7 +2,7 @@ sudo: false ...@@ -2,7 +2,7 @@ sudo: false
language: go language: go
go: go:
- 1.9 - 1.11
install: install:
- go get github.com/whyrusleeping/gx - go get github.com/whyrusleeping/gx
......
...@@ -6,24 +6,19 @@ import ( ...@@ -6,24 +6,19 @@ import (
"time" "time"
) )
var sharedRNG = rand.New(rand.NewSource(time.Now().UnixNano()))
// D (Delay) makes it easy to add (threadsafe) configurable delays to other // D (Delay) makes it easy to add (threadsafe) configurable delays to other
// objects. // objects.
type D interface { type D interface {
Set(time.Duration) time.Duration Set(time.Duration) time.Duration
Wait() Wait()
NextWaitTime() time.Duration
Get() time.Duration Get() time.Duration
} }
// Fixed returns a delay with fixed latency
func Fixed(t time.Duration) D {
return &delay{t: t}
}
type delay struct { type delay struct {
l sync.RWMutex l sync.RWMutex
t time.Duration t time.Duration
generator Generator
} }
func (d *delay) Set(t time.Duration) time.Duration { func (d *delay) Set(t time.Duration) time.Duration {
...@@ -37,7 +32,13 @@ func (d *delay) Set(t time.Duration) time.Duration { ...@@ -37,7 +32,13 @@ func (d *delay) Set(t time.Duration) time.Duration {
func (d *delay) Wait() { func (d *delay) Wait() {
d.l.RLock() d.l.RLock()
defer d.l.RUnlock() defer d.l.RUnlock()
time.Sleep(d.t) time.Sleep(d.generator.NextWaitTime(d.t))
}
func (d *delay) NextWaitTime() time.Duration {
d.l.Lock()
defer d.l.Unlock()
return d.generator.NextWaitTime(d.t)
} }
func (d *delay) Get() time.Duration { func (d *delay) Get() time.Duration {
...@@ -46,60 +47,29 @@ func (d *delay) Get() time.Duration { ...@@ -46,60 +47,29 @@ func (d *delay) Get() time.Duration {
return d.t return d.t
} }
// VariableNormal is a delay following a normal distribution // Delay generates a generic delay form a t, a sleeper, and a generator
// Notice that to implement the D interface Set can only change the mean delay func Delay(t time.Duration, generator Generator) D {
// the standard deviation is set only at initialization return &delay{
func VariableNormal(t, std time.Duration, rng *rand.Rand) D { t: t,
if rng == nil { generator: generator,
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() { // Fixed returns a delay with fixed latency
d.l.RLock() func Fixed(t time.Duration) D {
defer d.l.RUnlock() return Delay(t, FixedGenerator())
randomDelay := time.Duration(d.rng.NormFloat64() * float64(d.std))
time.Sleep(randomDelay + d.t)
} }
// VariableUniform is a delay following a uniform distribution // VariableUniform is a delay following a uniform distribution
// Notice that to implement the D interface Set can only change the minimum delay // Notice that to implement the D interface Set can only change the minimum delay
// the delta is set only at initialization // the delta is set only at initialization
func VariableUniform(t, d time.Duration, rng *rand.Rand) D { func VariableUniform(t, d time.Duration, rng *rand.Rand) D {
if rng == nil { return Delay(t, VariableUniformGenerator(d, rng))
rng = sharedRNG
}
v := &variableUniform{
d: d,
rng: rng,
}
v.t = t
return v
} }
type variableUniform struct { // VariableNormal is a delay following a normal distribution
delay // Notice that to implement the D interface Set can only change the mean delay
d time.Duration // max delta // the standard deviation is set only at initialization
rng *rand.Rand func VariableNormal(t, std time.Duration, rng *rand.Rand) D {
} return Delay(t, VariableNormalGenerator(std, rng))
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)
} }
package delay
import (
"testing"
"time"
)
func TestDelaySetAndGet(t *testing.T) {
initialValue := 1000 * time.Millisecond
modifiedValue := 2000 * time.Millisecond
deviation := 1000 * time.Millisecond
fixed := Fixed(initialValue)
variableNormal := VariableNormal(initialValue, deviation, nil)
variableUniform := VariableUniform(initialValue, deviation, nil)
if fixed.Get().Seconds() != 1 {
t.Fatal("Fixed delay not initialized correctly")
}
if variableNormal.Get().Seconds() != 1 {
t.Fatal("Normalized variable delay not initialized correctly")
}
if variableUniform.Get().Seconds() != 1 {
t.Fatal("Uniform variable delay not initialized correctly")
}
fixed.Set(modifiedValue)
if fixed.Get().Seconds() != 2 {
t.Fatal("Fixed delay not set correctly")
}
variableNormal.Set(modifiedValue)
if variableNormal.Get().Seconds() != 2 {
t.Fatal("Normalized variable delay not set correctly")
}
variableUniform.Set(modifiedValue)
if variableUniform.Get().Seconds() != 2 {
t.Fatal("Uniform variable delay not initialized correctly")
}
}
type fixedAdd struct {
toAdd time.Duration
}
func (fa *fixedAdd) NextWaitTime(t time.Duration) time.Duration {
return t + fa.toAdd
}
func TestDelaySleep(t *testing.T) {
initialValue := 1000 * time.Millisecond
toAdd := 500 * time.Millisecond
generator := &fixedAdd{toAdd: toAdd}
delay := Delay(initialValue, generator)
if delay.NextWaitTime() != initialValue+toAdd {
t.Fatal("NextWaitTime should call the generator")
}
}
package delay
import (
"math/rand"
"time"
)
// Generator provides an interface for generating wait times
type Generator interface {
NextWaitTime(time.Duration) time.Duration
}
var sharedRNG = rand.New(rand.NewSource(time.Now().UnixNano()))
// VariableNormalGenerator makes delays that following a normal distribution
func VariableNormalGenerator(std time.Duration, rng *rand.Rand) Generator {
if rng == nil {
rng = sharedRNG
}
return &variableNormal{
std: std,
rng: rng,
}
}
type variableNormal struct {
std time.Duration
rng *rand.Rand
}
func (d *variableNormal) NextWaitTime(t time.Duration) time.Duration {
return time.Duration(d.rng.NormFloat64()*float64(d.std)) + t
}
// VariableUniformGenerator generates delays following a uniform distribution
func VariableUniformGenerator(d time.Duration, rng *rand.Rand) Generator {
if rng == nil {
rng = sharedRNG
}
return &variableUniform{
d: d,
rng: rng,
}
}
type variableUniform struct {
d time.Duration // max delta
rng *rand.Rand
}
func (d *variableUniform) NextWaitTime(t time.Duration) time.Duration {
return time.Duration(d.rng.Float64()*float64(d.d)) + t
}
type fixed struct{}
// FixedGenerator returns a delay with fixed latency
func FixedGenerator() Generator {
return &fixed{}
}
func (d *fixed) NextWaitTime(t time.Duration) time.Duration {
return t
}
package delay
import (
"math"
"math/rand"
"testing"
"time"
)
const testSeed = 99
func TestGeneratorNextWaitTime(t *testing.T) {
initialValue := 1000 * time.Millisecond
deviation := 1000 * time.Millisecond
firstRandomNormal := rand.New(rand.NewSource(testSeed)).NormFloat64()
firstRandom := rand.New(rand.NewSource(testSeed)).Float64()
fixedGenerator := FixedGenerator()
variableNormalGenerator := VariableNormalGenerator(deviation, rand.New(rand.NewSource(testSeed)))
variableUniformGenerator := VariableUniformGenerator(deviation, rand.New(rand.NewSource(testSeed)))
if fixedGenerator.NextWaitTime(initialValue).Seconds() != 1 {
t.Fatal("Fixed generator output incorrect wait time")
}
if math.Abs(variableNormalGenerator.NextWaitTime(initialValue).Seconds()-(firstRandomNormal*deviation.Seconds()+initialValue.Seconds())) > 0.00001 {
t.Fatal("Normalized variable delay generator output incorrect wait time")
}
if math.Abs(variableUniformGenerator.NextWaitTime(initialValue).Seconds()-(firstRandom*deviation.Seconds()+initialValue.Seconds())) > 0.00001 {
t.Fatal("Uniform variable delay output incorrect wait time")
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment