Skip to content

Commit d94db89

Browse files
authored
Add poller autoscaler (#1184)
* add poller autoscaler * add tests * address comments
1 parent f5e0fd2 commit d94db89

File tree

10 files changed

+764
-1
lines changed

10 files changed

+764
-1
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/gogo/protobuf v1.3.2
1010
github.com/golang/mock v1.4.4
1111
github.com/kisielk/errcheck v1.5.0
12+
github.com/marusama/semaphore/v2 v2.5.0
1213
github.com/opentracing/opentracing-go v1.1.0
1314
github.com/pborman/uuid v0.0.0-20160209185913-a97ce2ca70fa
1415
github.com/robfig/cron v1.2.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
115115
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
116116
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
117117
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
118+
github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
119+
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
118120
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
119121
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
120122
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=

internal/common/autoscaler/auto_scaler.go

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2017-2021 Uber Technologies Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package autoscaler
22+
23+
// AutoScaler collects data and estimate usage
24+
type (
25+
AutoScaler interface {
26+
Estimator
27+
// Acquire X ResourceUnit of resource
28+
Acquire(ResourceUnit) error
29+
// Release X ResourceUnit of resource
30+
Release(ResourceUnit)
31+
// GetCurrent ResourceUnit of resource
32+
GetCurrent() ResourceUnit
33+
// Start starts the autoscaler go routine that scales the ResourceUnit according to Estimator
34+
Start() DoneFunc
35+
}
36+
// DoneFunc func to turn off auto scaler
37+
DoneFunc func()
38+
)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) 2017-2021 Uber Technologies Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package autoscaler
22+
23+
type (
24+
// Estimator collects data and estimate usage
25+
Estimator interface {
26+
CollectUsage(data interface{}) error
27+
Estimate() (Usages, error)
28+
Reset()
29+
}
30+
)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2017-2021 Uber Technologies Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package autoscaler
22+
23+
import "math"
24+
25+
// Recommender a recommendation generator for ResourceUnit
26+
type Recommender interface {
27+
Recommend(currentResource ResourceUnit, currentUsages Usages) ResourceUnit
28+
}
29+
30+
type linearRecommender struct {
31+
lower, upper ResourceUnit
32+
targetUsages Usages
33+
}
34+
35+
// NewLinearRecommender create a linear Recommender
36+
func NewLinearRecommender(lower, upper ResourceUnit, targetUsages Usages) Recommender {
37+
return &linearRecommender{
38+
lower: lower,
39+
upper: upper,
40+
targetUsages: targetUsages,
41+
}
42+
}
43+
44+
// Recommend recommends the new value
45+
func (l *linearRecommender) Recommend(currentResource ResourceUnit, currentUsages Usages) ResourceUnit {
46+
var recommend float64
47+
48+
// average recommendation over all UsageType
49+
for usageType := range currentUsages {
50+
var r float64
51+
if l.targetUsages[usageType] == 0 { // avoid division by zero
52+
r = math.MaxFloat64
53+
} else {
54+
r = currentResource.Value() * currentUsages[usageType].Value() / l.targetUsages[usageType].Value()
55+
}
56+
// boundary check
57+
r = math.Min(l.upper.Value(), math.Max(l.lower.Value(), r))
58+
recommend += r
59+
}
60+
recommend /= float64(len(currentUsages))
61+
return ResourceUnit(recommend)
62+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) 2017-2021 Uber Technologies Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package autoscaler
22+
23+
import "testing"
24+
25+
func Test_linearRecommender_Recommend(t *testing.T) {
26+
type fields struct {
27+
lower ResourceUnit
28+
upper ResourceUnit
29+
targetUsages Usages
30+
}
31+
type args struct {
32+
currentResource ResourceUnit
33+
currentUsages Usages
34+
}
35+
36+
defaultFields := fields{
37+
lower: 5,
38+
upper: 15,
39+
targetUsages: map[UsageType]MilliUsage{
40+
PollerUtilizationRate: 500,
41+
},
42+
}
43+
44+
tests := []struct {
45+
name string
46+
fields fields
47+
args args
48+
want ResourceUnit
49+
}{
50+
{
51+
name: "on target usage",
52+
fields: defaultFields,
53+
args: args{
54+
currentResource: 10,
55+
currentUsages: map[UsageType]MilliUsage{
56+
PollerUtilizationRate: 500,
57+
},
58+
},
59+
want: ResourceUnit(10),
60+
},
61+
{
62+
name: "under utilized, scale down",
63+
fields: defaultFields,
64+
args: args{
65+
currentResource: 10,
66+
currentUsages: map[UsageType]MilliUsage{
67+
PollerUtilizationRate: 400,
68+
},
69+
},
70+
want: ResourceUnit(8),
71+
},
72+
{
73+
name: "under utilized, scale down but bounded",
74+
fields: defaultFields,
75+
args: args{
76+
currentResource: 10,
77+
currentUsages: map[UsageType]MilliUsage{
78+
PollerUtilizationRate: 200,
79+
},
80+
},
81+
want: ResourceUnit(5),
82+
},
83+
{
84+
name: "zero utilization, scale down to min",
85+
fields: defaultFields,
86+
args: args{
87+
currentResource: 10,
88+
currentUsages: map[UsageType]MilliUsage{
89+
PollerUtilizationRate: 0,
90+
},
91+
},
92+
want: ResourceUnit(5),
93+
},
94+
{
95+
name: "over utilized, scale up",
96+
fields: defaultFields,
97+
args: args{
98+
currentResource: 10,
99+
currentUsages: map[UsageType]MilliUsage{
100+
PollerUtilizationRate: 600,
101+
},
102+
},
103+
want: ResourceUnit(12),
104+
},
105+
{
106+
name: "over utilized, scale up but bounded",
107+
fields: defaultFields,
108+
args: args{
109+
currentResource: 10,
110+
currentUsages: map[UsageType]MilliUsage{
111+
PollerUtilizationRate: 1000,
112+
},
113+
},
114+
want: ResourceUnit(15),
115+
},
116+
}
117+
for _, tt := range tests {
118+
t.Run(tt.name, func(t *testing.T) {
119+
l := NewLinearRecommender(tt.fields.lower, tt.fields.upper, tt.fields.targetUsages)
120+
if got := l.Recommend(tt.args.currentResource, tt.args.currentUsages); got != tt.want {
121+
t.Errorf("Recommend() = %v, want %v", got, tt.want)
122+
}
123+
})
124+
}
125+
}

internal/common/autoscaler/types.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2017-2021 Uber Technologies Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package autoscaler
22+
23+
type (
24+
// ResourceUnit is the unit of scalable resources
25+
ResourceUnit uint
26+
27+
// MilliUsage is the custom defined usage of ResourceUnit times 1000
28+
MilliUsage uint64
29+
30+
// Usages are different measurements used by a Recommender to provide a recommended ResourceUnit
31+
Usages map[UsageType]MilliUsage
32+
33+
// UsageType type of usage
34+
UsageType string
35+
)
36+
37+
const (
38+
// PollerUtilizationRate is a scale from 0 to 1 to indicate poller usages
39+
PollerUtilizationRate UsageType = "pollerUtilizationRate"
40+
)
41+
42+
// Value helper method for type conversion
43+
func (r ResourceUnit) Value() float64 {
44+
return float64(r)
45+
}
46+
47+
// Value helper method for type conversion
48+
func (u MilliUsage) Value() float64 {
49+
return float64(u)
50+
}

0 commit comments

Comments
 (0)