Add Attack constant to pacer

This commit is contained in:
Nick Craig-Wood 2016-03-05 16:07:39 +00:00
parent 874a64e5f6
commit 0166544319
2 changed files with 81 additions and 7 deletions

View File

@ -15,6 +15,7 @@ type Pacer struct {
minSleep time.Duration // minimum sleep time
maxSleep time.Duration // maximum sleep time
decayConstant uint // decay constant
attackConstant uint // attack constant
pacer chan struct{} // To pace the operations
sleepTime time.Duration // Time to sleep for each transaction
retries int // Max number of retries
@ -58,11 +59,12 @@ type Paced func() (bool, error)
// New returns a Pacer with sensible defaults
func New() *Pacer {
p := &Pacer{
minSleep: 10 * time.Millisecond,
maxSleep: 2 * time.Second,
decayConstant: 2,
retries: fs.Config.LowLevelRetries,
pacer: make(chan struct{}, 1),
minSleep: 10 * time.Millisecond,
maxSleep: 2 * time.Second,
decayConstant: 2,
attackConstant: 1,
retries: fs.Config.LowLevelRetries,
pacer: make(chan struct{}, 1),
}
p.sleepTime = p.minSleep
p.SetPacer(DefaultPacer)
@ -116,7 +118,7 @@ func (p *Pacer) SetMaxConnections(n int) *Pacer {
// This is the speed the time falls back to the minimum after errors
// have occurred.
//
// bigger for slower decay, exponential
// bigger for slower decay, exponential. 1 is halve, 0 is go straight to minimum
func (p *Pacer) SetDecayConstant(decay uint) *Pacer {
p.mu.Lock()
defer p.mu.Unlock()
@ -124,6 +126,19 @@ func (p *Pacer) SetDecayConstant(decay uint) *Pacer {
return p
}
// SetAttackConstant sets the attack constant for the pacer
//
// This is the speed the time grows from the minimum after errors have
// occurred.
//
// bigger for slower attack, 1 is double, 0 is go straight to maximum
func (p *Pacer) SetAttackConstant(attack uint) *Pacer {
p.mu.Lock()
defer p.mu.Unlock()
p.attackConstant = attack
return p
}
// SetRetries sets the max number of tries for Call
func (p *Pacer) SetRetries(retries int) *Pacer {
p.mu.Lock()
@ -185,7 +200,11 @@ func (p *Pacer) beginCall() {
func (p *Pacer) defaultPacer(retry bool) {
oldSleepTime := p.sleepTime
if retry {
p.sleepTime *= 2
if p.attackConstant == 0 {
p.sleepTime = p.maxSleep
} else {
p.sleepTime = (p.sleepTime << p.attackConstant) / ((1 << p.attackConstant) - 1)
}
if p.sleepTime > p.maxSleep {
p.sleepTime = p.maxSleep
}

View File

@ -27,6 +27,9 @@ func TestNew(t *testing.T) {
if p.decayConstant != 2 {
t.Errorf("decayConstant")
}
if p.attackConstant != 1 {
t.Errorf("attackConstant")
}
if cap(p.pacer) != 1 {
t.Errorf("pacer 1")
}
@ -85,6 +88,58 @@ func TestSetDecayConstant(t *testing.T) {
}
}
func TestDecay(t *testing.T) {
p := New().SetMinSleep(time.Microsecond).SetPacer(DefaultPacer).SetMaxSleep(time.Second)
for _, test := range []struct {
in time.Duration
attackConstant uint
want time.Duration
}{
{8 * time.Millisecond, 1, 4 * time.Millisecond},
{1 * time.Millisecond, 0, time.Microsecond},
{1 * time.Millisecond, 2, (3 * time.Millisecond) / 4},
{1 * time.Millisecond, 3, (7 * time.Millisecond) / 8},
} {
p.sleepTime = test.in
p.SetDecayConstant(test.attackConstant)
p.defaultPacer(false)
got := p.sleepTime
if got != test.want {
t.Errorf("bad sleep want %v got %v", test.want, got)
}
}
}
func TestSetAttackConstant(t *testing.T) {
p := New().SetAttackConstant(19)
if p.attackConstant != 19 {
t.Errorf("didn't set")
}
}
func TestAttack(t *testing.T) {
p := New().SetMinSleep(time.Microsecond).SetPacer(DefaultPacer).SetMaxSleep(time.Second)
for _, test := range []struct {
in time.Duration
attackConstant uint
want time.Duration
}{
{1 * time.Millisecond, 1, 2 * time.Millisecond},
{1 * time.Millisecond, 0, time.Second},
{1 * time.Millisecond, 2, (4 * time.Millisecond) / 3},
{1 * time.Millisecond, 3, (8 * time.Millisecond) / 7},
} {
p.sleepTime = test.in
p.SetAttackConstant(test.attackConstant)
p.defaultPacer(true)
got := p.sleepTime
if got != test.want {
t.Errorf("bad sleep want %v got %v", test.want, got)
}
}
}
func TestSetRetries(t *testing.T) {
p := New().SetRetries(18)
if p.retries != 18 {