Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
dms3
go-dms3-delay
Commits
f1bb990c
Commit
f1bb990c
authored
Nov 09, 2018
by
hannahhoward
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor for DI approach + more testing
parent
2cb8bd4d
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
173 additions
and
99 deletions
+173
-99
delay.go
delay.go
+22
-54
delay_test.go
delay_test.go
+29
-45
generator.go
generator.go
+68
-0
generator_test.go
generator_test.go
+34
-0
sleeper.go
sleeper.go
+20
-0
No files found.
delay.go
View file @
f1bb990c
...
...
@@ -6,7 +6,7 @@ import (
"time"
)
var
sharedR
NG
=
rand
.
New
(
rand
.
NewSource
(
time
.
Now
()
.
UnixNano
())
)
var
sharedR
ealSleeper
=
NewRealSleeper
(
)
// D (Delay) makes it easy to add (threadsafe) configurable delays to other
// objects.
...
...
@@ -17,14 +17,11 @@ type D interface {
Get
()
time
.
Duration
}
// Fixed returns a delay with fixed latency
func
Fixed
(
t
time
.
Duration
)
D
{
return
&
delay
{
t
:
t
}
}
type
delay
struct
{
l
sync
.
RWMutex
t
time
.
Duration
l
sync
.
RWMutex
t
time
.
Duration
sleeper
Sleeper
generator
Generator
}
func
(
d
*
delay
)
Set
(
t
time
.
Duration
)
time
.
Duration
{
...
...
@@ -36,16 +33,15 @@ func (d *delay) Set(t time.Duration) time.Duration {
}
func
(
d
*
delay
)
Wait
()
{
nextWaitTime
:=
d
.
NextWaitTime
()
d
.
l
.
RLock
()
defer
d
.
l
.
RUnlock
()
time
.
Sleep
(
n
extWaitTime
)
d
.
sleeper
.
Sleep
(
d
.
generator
.
N
extWaitTime
(
d
.
t
)
)
}
func
(
d
*
delay
)
NextWaitTime
()
time
.
Duration
{
d
.
l
.
Lock
()
defer
d
.
l
.
Unlock
()
return
d
.
t
return
d
.
generator
.
NextWaitTime
(
d
.
t
)
}
func
(
d
*
delay
)
Get
()
time
.
Duration
{
...
...
@@ -54,58 +50,30 @@ func (d *delay) Get() time.Duration {
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
,
// Delay generates a generic delay form a t, a sleeper, and a generator
func
Delay
(
t
time
.
Duration
,
sleeper
Sleeper
,
generator
Generator
)
D
{
return
&
delay
{
t
:
t
,
sleeper
:
sleeper
,
generator
:
generator
,
}
v
.
t
=
t
return
v
}
type
variableNormal
struct
{
delay
std
time
.
Duration
rng
*
rand
.
Rand
}
func
(
d
*
variableNormal
)
NextWaitTime
()
time
.
Duration
{
d
.
l
.
RLock
()
defer
d
.
l
.
RUnlock
()
return
time
.
Duration
(
d
.
rng
.
NormFloat64
()
*
float64
(
d
.
std
))
+
d
.
t
// Fixed returns a delay with fixed latency
func
Fixed
(
t
time
.
Duration
)
D
{
return
Delay
(
t
,
sharedRealSleeper
,
FixedGenerator
())
}
// 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
return
Delay
(
t
,
sharedRealSleeper
,
VariableUniformGenerator
(
d
,
rng
))
}
func
(
d
*
variableUniform
)
NextWaitTime
()
time
.
Duration
{
d
.
l
.
RLock
()
defer
d
.
l
.
RUnlock
()
return
time
.
Duration
(
d
.
rng
.
Float64
()
*
float64
(
d
.
d
))
+
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
{
return
Delay
(
t
,
sharedRealSleeper
,
VariableNormalGenerator
(
std
,
rng
))
}
delay_test.go
View file @
f1bb990c
package
delay
import
(
"math"
"math/rand"
"testing"
"time"
)
const
testSeed
=
99
func
TestDelaySetAndGet
(
t
*
testing
.
T
)
{
initialValue
,
err
:=
time
.
ParseDuration
(
"1000ms"
)
if
err
!=
nil
{
t
.
Fatal
(
"Parse error during setup"
)
}
modifiedValue
,
err
:=
time
.
ParseDuration
(
"2000ms"
)
if
err
!=
nil
{
t
.
Fatal
(
"Parse error during setup"
)
}
deviation
,
err
:=
time
.
ParseDuration
(
"1000ms"
)
if
err
!=
nil
{
t
.
Fatal
(
"Parse error during setup"
)
}
initialValue
:=
1000
*
time
.
Millisecond
modifiedValue
:=
2000
*
time
.
Millisecond
deviation
:=
1000
*
time
.
Millisecond
fixed
:=
Fixed
(
initialValue
)
variableNormal
:=
VariableNormal
(
initialValue
,
deviation
,
rand
.
New
(
rand
.
NewSource
(
testSeed
))
)
variableUniform
:=
VariableUniform
(
initialValue
,
deviation
,
rand
.
New
(
rand
.
NewSource
(
testSeed
))
)
variableNormal
:=
VariableNormal
(
initialValue
,
deviation
,
nil
)
variableUniform
:=
VariableUniform
(
initialValue
,
deviation
,
nil
)
if
fixed
.
Get
()
.
Seconds
()
!=
1
{
t
.
Fatal
(
"Fixed delay not initialized correctly"
)
...
...
@@ -64,34 +46,36 @@ func TestDelaySetAndGet(t *testing.T) {
}
func
TestDelayNextWaitTime
(
t
*
testing
.
T
)
{
initialValue
,
err
:=
time
.
ParseDuration
(
"1000ms"
)
type
recordSleeper
struct
{
lastSleep
time
.
Duration
}
if
err
!=
nil
{
t
.
Fatal
(
"Parse error during setup"
)
}
func
(
rs
*
recordSleeper
)
Sleep
(
t
time
.
Duration
)
{
rs
.
lastSleep
=
t
}
deviation
,
err
:=
time
.
ParseDuration
(
"1000ms"
)
type
fixedAdd
struct
{
toAdd
time
.
Duration
}
if
err
!=
nil
{
t
.
Fatal
(
"Parse error during setup"
)
}
func
(
fa
*
fixedAdd
)
NextWaitTime
(
t
time
.
Duration
)
time
.
Duration
{
return
t
+
fa
.
toAdd
}
fixed
:=
Fixed
(
initialValue
)
firstRandomNormal
:=
rand
.
New
(
rand
.
NewSource
(
testSeed
))
.
NormFloat64
()
firstRandom
:=
rand
.
New
(
rand
.
NewSource
(
testSeed
))
.
Float64
()
variableNormal
:=
VariableNormal
(
initialValue
,
deviation
,
rand
.
New
(
rand
.
NewSource
(
testSeed
)))
variableUniform
:=
VariableUniform
(
initialValue
,
deviation
,
rand
.
New
(
rand
.
NewSource
(
testSeed
)))
if
fixed
.
NextWaitTime
()
.
Seconds
()
!=
1
{
t
.
Fatal
(
"Fixed delay output incorrect wait time"
)
}
func
TestDelaySleep
(
t
*
testing
.
T
)
{
initialValue
:=
1000
*
time
.
Millisecond
toAdd
:=
500
*
time
.
Millisecond
generator
:=
&
fixedAdd
{
toAdd
:
toAdd
}
sleeper
:=
&
recordSleeper
{
lastSleep
:
-
1
}
if
math
.
Abs
(
variableNormal
.
NextWaitTime
()
.
Seconds
()
-
(
firstRandomNormal
*
deviation
.
Seconds
()
+
initialValue
.
Seconds
()))
>
0.00001
{
t
.
Fatal
(
"Normalized variable delay output incorrect wait time"
)
}
delay
:=
Delay
(
initialValue
,
sleeper
,
generator
)
if
math
.
Abs
(
variableUniform
.
NextWaitTime
()
.
Seconds
()
-
(
firstRandom
*
deviation
.
Seconds
()
+
initialValue
.
Seconds
()))
>
0.00001
{
t
.
Fatal
(
"
Uniform variable delay output incorrect wait time
"
)
if
delay
.
NextWaitTime
()
!=
initialValue
+
toAdd
{
t
.
Fatal
(
"
NextWaitTime should call the generator
"
)
}
delay
.
Wait
()
if
sleeper
.
lastSleep
!=
initialValue
+
toAdd
{
t
.
Fatal
(
"Wait should sleep based on the next wait time generated"
)
}
}
generator.go
0 → 100644
View file @
f1bb990c
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
}
v
:=
&
variableNormal
{
std
:
std
,
rng
:
rng
,
}
return
v
}
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
}
v
:=
&
variableUniform
{
d
:
d
,
rng
:
rng
,
}
return
v
}
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
}
generator_test.go
0 → 100644
View file @
f1bb990c
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"
)
}
}
sleeper.go
0 → 100644
View file @
f1bb990c
package
delay
import
"time"
// Sleeper - a generic interface for wrapping a sleep function
// So that sleeping can be mocked in tests
type
Sleeper
interface
{
Sleep
(
time
.
Duration
)
}
type
realSleeper
struct
{}
func
(
rc
*
realSleeper
)
Sleep
(
d
time
.
Duration
)
{
time
.
Sleep
(
d
)
}
// NewRealSleeper - returns a new sleeper that uses the real time.Sleep function
func
NewRealSleeper
()
Sleeper
{
return
&
realSleeper
{}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment