@@ -23,19 +23,18 @@ package worker
23
23
import (
24
24
"math"
25
25
"sync"
26
- "sync/atomic"
27
26
"time"
28
27
29
28
"github.com/jonboulle/clockwork"
30
29
"github.com/uber-go/tally"
31
30
"go.uber.org/zap"
32
31
33
32
"go.uber.org/cadence/.gen/go/shared"
33
+ "go.uber.org/cadence/internal/common/metrics"
34
34
)
35
35
36
36
const (
37
37
defaultAutoScalerUpdateTick = time .Second
38
- // concurrencyAutoScalerObservabilityTick = time.Millisecond * 500
39
38
targetPollerWaitTimeInMsLog2 = 4 // 16 ms
40
39
numberOfPollsInRollingAverage = 20
41
40
@@ -50,6 +49,11 @@ const (
50
49
autoScalerEventStop = "stop"
51
50
autoScalerEventLogMsg string = "concurrency auto scaler event"
52
51
testTimeFormat string = "15:04:05"
52
+
53
+ metricsEnabled = "enabled"
54
+ metricsDisabled = "disabled"
55
+ metricsPollerQuota = "poller-quota"
56
+ metricsPollerWaitTime = "poller-wait-time"
53
57
)
54
58
55
59
type (
@@ -64,14 +68,15 @@ type (
64
68
cooldown time.Duration
65
69
updateTick time.Duration
66
70
67
- // enable auto scaler on concurrency or not
68
- enable atomic.Bool
71
+ // state of autoscaler
72
+ lock sync.RWMutex
73
+ enabled bool
69
74
70
75
// poller
71
76
pollerInitCount int
72
77
pollerMaxCount int
73
78
pollerMinCount int
74
- pollerWaitTimeInMsLog2 * rollingAverage // log2(pollerWaitTimeInMs+1) for smoothing (ideal value is 0)
79
+ pollerWaitTime * rollingAverage [time. Duration ]
75
80
pollerPermitLastUpdate time.Time
76
81
}
77
82
@@ -98,15 +103,15 @@ func NewConcurrencyAutoScaler(input ConcurrencyAutoScalerInput) *ConcurrencyAuto
98
103
shutdownChan : make (chan struct {}),
99
104
concurrency : input .Concurrency ,
100
105
cooldown : input .Cooldown ,
101
- log : input .Logger ,
102
- scope : input .Scope ,
106
+ log : input .Logger . Named ( metrics . ConcurrencyAutoScalerScope ) ,
107
+ scope : input .Scope . SubScope ( metrics . ConcurrencyAutoScalerScope ) ,
103
108
clock : input .Clock ,
104
109
updateTick : tick ,
105
- enable : atomic. Bool {} , // initial value should be false and is only turned on from auto config hint
110
+ enabled : false , // initial value should be false and is only turned on from auto config hint
106
111
pollerInitCount : input .Concurrency .PollerPermit .Quota (),
107
112
pollerMaxCount : input .PollerMaxCount ,
108
113
pollerMinCount : input .PollerMinCount ,
109
- pollerWaitTimeInMsLog2 : newRollingAverage (numberOfPollsInRollingAverage ),
114
+ pollerWaitTime : newRollingAverage [time. Duration ] (numberOfPollsInRollingAverage ),
110
115
pollerPermitLastUpdate : input .Clock .Now (),
111
116
}
112
117
}
@@ -126,7 +131,9 @@ func (c *ConcurrencyAutoScaler) Start() {
126
131
return
127
132
case <- ticker .Chan ():
128
133
c .logEvent (autoScalerEventMetrics )
134
+ c .lock .Lock ()
129
135
c .updatePollerPermit ()
136
+ c .lock .Unlock ()
130
137
}
131
138
}
132
139
}()
@@ -138,27 +145,27 @@ func (c *ConcurrencyAutoScaler) Stop() {
138
145
c .logEvent (autoScalerEventStop )
139
146
}
140
147
141
- // ProcessPollerHint reads the poller response hint and take actions
148
+ // ProcessPollerHint reads the poller response hint and take actions in a transactional way
142
149
// 1. update poller wait time
143
150
// 2. enable/disable auto scaler
144
151
func (c * ConcurrencyAutoScaler ) ProcessPollerHint (hint * shared.AutoConfigHint ) {
152
+ c .lock .Lock ()
153
+ defer c .lock .Unlock ()
154
+
145
155
if hint == nil {
146
- c .log .Warn ("auto config hint is nil, this results in no action" )
147
156
return
148
157
}
149
158
if hint .PollerWaitTimeInMs != nil {
150
159
waitTimeInMs := * hint .PollerWaitTimeInMs
151
- c .pollerWaitTimeInMsLog2 .Add (math . Log2 ( float64 ( waitTimeInMs + 1 ) ))
160
+ c .pollerWaitTime .Add (time . Millisecond * time . Duration ( waitTimeInMs ))
152
161
}
153
162
154
- /*
155
- Atomically compare and switch the auto scaler enable flag. If auto scaler is turned off, IMMEDIATELY reset the concurrency limits.
156
- */
157
163
var shouldEnable bool
158
164
if hint .EnableAutoConfig != nil && * hint .EnableAutoConfig {
159
165
shouldEnable = true
160
166
}
161
- if switched := c .enable .CompareAndSwap (! shouldEnable , shouldEnable ); switched {
167
+ if shouldEnable != c .enabled { // flag switched
168
+ c .enabled = shouldEnable
162
169
if shouldEnable {
163
170
c .logEvent (autoScalerEventEnable )
164
171
} else {
@@ -174,25 +181,23 @@ func (c *ConcurrencyAutoScaler) resetConcurrency() {
174
181
}
175
182
176
183
func (c * ConcurrencyAutoScaler ) logEvent (event autoScalerEvent ) {
177
- if c .enable . Load () {
178
- c .scope .Counter ("concurrency_auto_scaler.enabled" ).Inc (1 )
184
+ if c .enabled {
185
+ c .scope .Counter (metricsEnabled ).Inc (1 )
179
186
} else {
180
- c .scope .Counter ("concurrency_auto_scaler.disabled" ).Inc (1 )
187
+ c .scope .Counter (metricsDisabled ).Inc (1 )
181
188
}
182
- c .scope .Gauge ("poller_in_action" ).Update (float64 (c .concurrency .PollerPermit .Count ()))
183
- c .scope .Gauge ("poller_quota" ).Update (float64 (c .concurrency .PollerPermit .Quota ()))
184
- c .scope .Gauge ("poller_wait_time" ).Update (math .Exp2 (c .pollerWaitTimeInMsLog2 .Average ()))
189
+ c .scope .Gauge (metricsPollerQuota ).Update (float64 (c .concurrency .PollerPermit .Quota ()))
190
+ c .scope .Timer (metricsPollerWaitTime ).Record (c .pollerWaitTime .Average ())
185
191
c .log .Debug (autoScalerEventLogMsg ,
186
192
zap .Time ("time" , c .clock .Now ()),
187
193
zap .String ("event" , string (event )),
188
- zap .Bool ("enabled" , c .enable . Load () ),
194
+ zap .Bool ("enabled" , c .enabled ),
189
195
zap .Int ("poller_quota" , c .concurrency .PollerPermit .Quota ()),
190
- zap .Int ("poller_in_action" , c .concurrency .PollerPermit .Count ()),
191
196
)
192
197
}
193
198
194
199
func (c * ConcurrencyAutoScaler ) updatePollerPermit () {
195
- if ! c .enable . Load () { // skip update if auto scaler is disabled
200
+ if ! c .enabled { // skip update if auto scaler is disabled
196
201
c .logEvent (autoScalerEventPollerSkipUpdateNotEnabled )
197
202
return
198
203
}
@@ -202,7 +207,9 @@ func (c *ConcurrencyAutoScaler) updatePollerPermit() {
202
207
return
203
208
}
204
209
currentQuota := c .concurrency .PollerPermit .Quota ()
205
- newQuota := int (math .Round (float64 (currentQuota ) * targetPollerWaitTimeInMsLog2 / c .pollerWaitTimeInMsLog2 .Average () ))
210
+ // smoothing the scaling through log2
211
+ newQuota := int (math .Round (float64 (currentQuota ) * targetPollerWaitTimeInMsLog2 / math .Log2 (
212
+ 1 + float64 (c .pollerWaitTime .Average ()/ time .Millisecond )) ))
206
213
if newQuota < c .pollerMinCount {
207
214
newQuota = c .pollerMinCount
208
215
}
@@ -218,22 +225,26 @@ func (c *ConcurrencyAutoScaler) updatePollerPermit() {
218
225
c .logEvent (autoScalerEventPollerUpdate )
219
226
}
220
227
221
- type rollingAverage struct {
228
+ type number interface {
229
+ int64 | float64 | time.Duration
230
+ }
231
+
232
+ type rollingAverage [T number ] struct {
222
233
mu sync.RWMutex
223
- window []float64
234
+ window []T
224
235
index int
225
- sum float64
236
+ sum T
226
237
count int
227
238
}
228
239
229
- func newRollingAverage (capacity int ) * rollingAverage {
230
- return & rollingAverage {
231
- window : make ([]float64 , capacity ),
240
+ func newRollingAverage [ T number ] (capacity int ) * rollingAverage [ T ] {
241
+ return & rollingAverage [ T ] {
242
+ window : make ([]T , capacity ),
232
243
}
233
244
}
234
245
235
246
// Add always add positive numbers
236
- func (r * rollingAverage ) Add (value float64 ) {
247
+ func (r * rollingAverage [ T ] ) Add (value T ) {
237
248
r .mu .Lock ()
238
249
defer r .mu .Unlock ()
239
250
@@ -243,21 +254,21 @@ func (r *rollingAverage) Add(value float64) {
243
254
}
244
255
245
256
// replace the old value with the new value
246
- r .index %= len (r .window )
247
257
r .sum += value - r .window [r .index ]
248
258
r .window [r .index ] = value
249
259
r .index ++
260
+ r .index %= len (r .window )
250
261
251
262
if r .count < len (r .window ) {
252
263
r .count ++
253
264
}
254
265
}
255
266
256
- func (r * rollingAverage ) Average () float64 {
267
+ func (r * rollingAverage [ T ] ) Average () T {
257
268
r .mu .RLock ()
258
269
defer r .mu .RUnlock ()
259
270
if r .count == 0 {
260
271
return 0
261
272
}
262
- return r .sum / float64 (r .count )
273
+ return r .sum / T (r .count )
263
274
}
0 commit comments