Extract Backoff interface
This commit is contained in:
parent
503a17f9ef
commit
e462d4420c
|
|
@ -125,7 +125,7 @@ type ClusterStateRegistry struct {
|
||||||
incorrectNodeGroupSizes map[string]IncorrectNodeGroupSize
|
incorrectNodeGroupSizes map[string]IncorrectNodeGroupSize
|
||||||
unregisteredNodes map[string]UnregisteredNode
|
unregisteredNodes map[string]UnregisteredNode
|
||||||
candidatesForScaleDown map[string][]string
|
candidatesForScaleDown map[string][]string
|
||||||
nodeGroupBackoffInfo *backoff.Backoff
|
nodeGroupBackoffInfo backoff.Backoff
|
||||||
lastStatus *api.ClusterAutoscalerStatus
|
lastStatus *api.ClusterAutoscalerStatus
|
||||||
lastScaleDownUpdateTime time.Time
|
lastScaleDownUpdateTime time.Time
|
||||||
logRecorder *utils.LogEventRecorder
|
logRecorder *utils.LogEventRecorder
|
||||||
|
|
@ -148,7 +148,7 @@ func NewClusterStateRegistry(cloudProvider cloudprovider.CloudProvider, config C
|
||||||
incorrectNodeGroupSizes: make(map[string]IncorrectNodeGroupSize),
|
incorrectNodeGroupSizes: make(map[string]IncorrectNodeGroupSize),
|
||||||
unregisteredNodes: make(map[string]UnregisteredNode),
|
unregisteredNodes: make(map[string]UnregisteredNode),
|
||||||
candidatesForScaleDown: make(map[string][]string),
|
candidatesForScaleDown: make(map[string][]string),
|
||||||
nodeGroupBackoffInfo: backoff.NewBackoff(InitialNodeGroupBackoffDuration, MaxNodeGroupBackoffDuration, NodeGroupBackoffResetTimeout),
|
nodeGroupBackoffInfo: backoff.NewExponentialBackoff(InitialNodeGroupBackoffDuration, MaxNodeGroupBackoffDuration, NodeGroupBackoffResetTimeout),
|
||||||
lastStatus: emptyStatus,
|
lastStatus: emptyStatus,
|
||||||
logRecorder: logRecorder,
|
logRecorder: logRecorder,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,64 +20,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type backoffInfo struct {
|
// Backoff allows time-based backing off of node groups considered in scale up algorithm
|
||||||
duration time.Duration
|
type Backoff interface {
|
||||||
backoffUntil time.Time
|
// Backoff execution for the given key. Returns time till execution is backed off.
|
||||||
lastFailedExecution time.Time
|
Backoff(key string, currentTime time.Time) time.Time
|
||||||
}
|
// IsBackedOff returns true if execution is backed off for the given key.
|
||||||
|
IsBackedOff(key string, currentTime time.Time) bool
|
||||||
// Backoff handles backing off executions.
|
// RemoveBackoff removes backoff data for the given key.
|
||||||
type Backoff struct {
|
RemoveBackoff(key string)
|
||||||
maxBackoffDuration time.Duration
|
// RemoveStaleBackoffData removes stale backoff data.
|
||||||
initialBackoffDuration time.Duration
|
RemoveStaleBackoffData(currentTime time.Time)
|
||||||
backoffResetTimeout time.Duration
|
|
||||||
backoffInfo map[string]backoffInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBackoff creates an instance of Backoff.
|
|
||||||
func NewBackoff(initialBackoffDuration time.Duration, maxBackoffDuration time.Duration, backoffResetTimeout time.Duration) *Backoff {
|
|
||||||
return &Backoff{maxBackoffDuration, initialBackoffDuration, backoffResetTimeout, make(map[string]backoffInfo)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveStaleBackoffData removes stale backoff data.
|
|
||||||
func (b *Backoff) RemoveStaleBackoffData(currentTime time.Time) {
|
|
||||||
for key, backoffInfo := range b.backoffInfo {
|
|
||||||
if backoffInfo.lastFailedExecution.Add(b.backoffResetTimeout).Before(currentTime) {
|
|
||||||
delete(b.backoffInfo, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backoff execution for the given key. Returns time till execution is backed off.
|
|
||||||
func (b *Backoff) Backoff(key string, currentTime time.Time) time.Time {
|
|
||||||
duration := b.initialBackoffDuration
|
|
||||||
if backoffInfo, found := b.backoffInfo[key]; found {
|
|
||||||
// Multiple concurrent scale-ups failing shouldn't cause backoff
|
|
||||||
// duration to increase, so we only increase it if we're not in
|
|
||||||
// backoff right now.
|
|
||||||
if backoffInfo.backoffUntil.Before(currentTime) {
|
|
||||||
duration = 2 * backoffInfo.duration
|
|
||||||
if duration > b.maxBackoffDuration {
|
|
||||||
duration = b.maxBackoffDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
backoffUntil := currentTime.Add(duration)
|
|
||||||
b.backoffInfo[key] = backoffInfo{
|
|
||||||
duration: duration,
|
|
||||||
backoffUntil: backoffUntil,
|
|
||||||
lastFailedExecution: currentTime,
|
|
||||||
}
|
|
||||||
return backoffUntil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveBackoff removes backoff data for the given key.
|
|
||||||
func (b *Backoff) RemoveBackoff(key string) {
|
|
||||||
delete(b.backoffInfo, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsBackedOff returns true if execution is backed off for the given key.
|
|
||||||
func (b *Backoff) IsBackedOff(key string, currentTime time.Time) bool {
|
|
||||||
backoffInfo, found := b.backoffInfo[key]
|
|
||||||
return found && backoffInfo.backoffUntil.After(currentTime)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package backoff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Backoff handles backing off executions.
|
||||||
|
type exponentialBackoff struct {
|
||||||
|
maxBackoffDuration time.Duration
|
||||||
|
initialBackoffDuration time.Duration
|
||||||
|
backoffResetTimeout time.Duration
|
||||||
|
backoffInfo map[string]exponentialBackoffInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type exponentialBackoffInfo struct {
|
||||||
|
duration time.Duration
|
||||||
|
backoffUntil time.Time
|
||||||
|
lastFailedExecution time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExponentialBackoff creates an instance of exponential backoff.
|
||||||
|
func NewExponentialBackoff(initialBackoffDuration time.Duration, maxBackoffDuration time.Duration, backoffResetTimeout time.Duration) Backoff {
|
||||||
|
return &exponentialBackoff{maxBackoffDuration, initialBackoffDuration, backoffResetTimeout, make(map[string]exponentialBackoffInfo)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backoff execution for the given key. Returns time till execution is backed off.
|
||||||
|
func (b *exponentialBackoff) Backoff(key string, currentTime time.Time) time.Time {
|
||||||
|
duration := b.initialBackoffDuration
|
||||||
|
if backoffInfo, found := b.backoffInfo[key]; found {
|
||||||
|
// Multiple concurrent scale-ups failing shouldn't cause backoff
|
||||||
|
// duration to increase, so we only increase it if we're not in
|
||||||
|
// backoff right now.
|
||||||
|
if backoffInfo.backoffUntil.Before(currentTime) {
|
||||||
|
duration = 2 * backoffInfo.duration
|
||||||
|
if duration > b.maxBackoffDuration {
|
||||||
|
duration = b.maxBackoffDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
backoffUntil := currentTime.Add(duration)
|
||||||
|
b.backoffInfo[key] = exponentialBackoffInfo{
|
||||||
|
duration: duration,
|
||||||
|
backoffUntil: backoffUntil,
|
||||||
|
lastFailedExecution: currentTime,
|
||||||
|
}
|
||||||
|
return backoffUntil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBackedOff returns true if execution is backed off for the given key.
|
||||||
|
func (b *exponentialBackoff) IsBackedOff(key string, currentTime time.Time) bool {
|
||||||
|
backoffInfo, found := b.backoffInfo[key]
|
||||||
|
return found && backoffInfo.backoffUntil.After(currentTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveBackoff removes backoff data for the given key.
|
||||||
|
func (b *exponentialBackoff) RemoveBackoff(key string) {
|
||||||
|
delete(b.backoffInfo, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveStaleBackoffData removes stale backoff data.
|
||||||
|
func (b *exponentialBackoff) RemoveStaleBackoffData(currentTime time.Time) {
|
||||||
|
for key, backoffInfo := range b.backoffInfo {
|
||||||
|
if backoffInfo.lastFailedExecution.Add(b.backoffResetTimeout).Before(currentTime) {
|
||||||
|
delete(b.backoffInfo, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBackoffTwoKeys(t *testing.T) {
|
func TestBackoffTwoKeys(t *testing.T) {
|
||||||
backoff := NewBackoff(10*time.Minute, time.Hour, 3*time.Hour)
|
backoff := NewExponentialBackoff(10*time.Minute, time.Hour, 3*time.Hour)
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
assert.False(t, backoff.IsBackedOff("key1", startTime))
|
assert.False(t, backoff.IsBackedOff("key1", startTime))
|
||||||
assert.False(t, backoff.IsBackedOff("key2", startTime))
|
assert.False(t, backoff.IsBackedOff("key2", startTime))
|
||||||
|
|
@ -35,7 +35,7 @@ func TestBackoffTwoKeys(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMaxBackoff(t *testing.T) {
|
func TestMaxBackoff(t *testing.T) {
|
||||||
backoff := NewBackoff(1*time.Minute, 3*time.Minute, 3*time.Hour)
|
backoff := NewExponentialBackoff(1*time.Minute, 3*time.Minute, 3*time.Hour)
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
backoff.Backoff("key1", startTime)
|
backoff.Backoff("key1", startTime)
|
||||||
assert.True(t, backoff.IsBackedOff("key1", startTime))
|
assert.True(t, backoff.IsBackedOff("key1", startTime))
|
||||||
|
|
@ -49,7 +49,7 @@ func TestMaxBackoff(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoveBackoff(t *testing.T) {
|
func TestRemoveBackoff(t *testing.T) {
|
||||||
backoff := NewBackoff(1*time.Minute, 3*time.Minute, 3*time.Hour)
|
backoff := NewExponentialBackoff(1*time.Minute, 3*time.Minute, 3*time.Hour)
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
backoff.Backoff("key1", startTime)
|
backoff.Backoff("key1", startTime)
|
||||||
assert.True(t, backoff.IsBackedOff("key1", startTime))
|
assert.True(t, backoff.IsBackedOff("key1", startTime))
|
||||||
|
|
@ -58,14 +58,14 @@ func TestRemoveBackoff(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResetStaleBackoffData(t *testing.T) {
|
func TestResetStaleBackoffData(t *testing.T) {
|
||||||
backoff := NewBackoff(1*time.Minute, 3*time.Minute, 3*time.Hour)
|
backoff := NewExponentialBackoff(1*time.Minute, 3*time.Minute, 3*time.Hour)
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
backoff.Backoff("key1", startTime)
|
backoff.Backoff("key1", startTime)
|
||||||
backoff.Backoff("key2", startTime.Add(time.Hour))
|
backoff.Backoff("key2", startTime.Add(time.Hour))
|
||||||
backoff.RemoveStaleBackoffData(startTime.Add(time.Hour))
|
backoff.RemoveStaleBackoffData(startTime.Add(time.Hour))
|
||||||
assert.Equal(t, 2, len(backoff.backoffInfo))
|
assert.Equal(t, 2, len(backoff.(*exponentialBackoff).backoffInfo))
|
||||||
backoff.RemoveStaleBackoffData(startTime.Add(4 * time.Hour))
|
backoff.RemoveStaleBackoffData(startTime.Add(4 * time.Hour))
|
||||||
assert.Equal(t, 1, len(backoff.backoffInfo))
|
assert.Equal(t, 1, len(backoff.(*exponentialBackoff).backoffInfo))
|
||||||
backoff.RemoveStaleBackoffData(startTime.Add(5 * time.Hour))
|
backoff.RemoveStaleBackoffData(startTime.Add(5 * time.Hour))
|
||||||
assert.Equal(t, 0, len(backoff.backoffInfo))
|
assert.Equal(t, 0, len(backoff.(*exponentialBackoff).backoffInfo))
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue