Skip to content

Commit 0b11e7d

Browse files
committed
revert task permit to use channel implementation instead
1 parent d2e13fd commit 0b11e7d

File tree

9 files changed

+283
-236
lines changed

9 files changed

+283
-236
lines changed

internal/common/autoscaler/autoscaler.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ package autoscaler
2424
type (
2525
AutoScaler interface {
2626
Estimator
27-
// Acquire X ResourceUnit of resource
28-
Acquire(ResourceUnit) error
29-
// Release X ResourceUnit of resource
30-
Release(ResourceUnit)
3127
// GetCurrent ResourceUnit of resource
3228
GetCurrent() ResourceUnit
3329
// Start starts the autoscaler go routine that scales the ResourceUnit according to Estimator

internal/internal_poller_autoscaler.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,6 @@ func newPollerScaler(
108108
}
109109
}
110110

111-
// Acquire concurrent poll quota
112-
func (p *pollerAutoScaler) Acquire(resource autoscaler.ResourceUnit) error {
113-
return p.permit.Acquire(p.ctx, int(resource))
114-
}
115-
116-
// Release concurrent poll quota
117-
func (p *pollerAutoScaler) Release(resource autoscaler.ResourceUnit) {
118-
p.permit.Release(int(resource))
119-
}
120-
121111
// GetCurrent poll quota
122112
func (p *pollerAutoScaler) GetCurrent() autoscaler.ResourceUnit {
123113
return autoscaler.ResourceUnit(p.permit.Quota())

internal/internal_poller_autoscaler_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package internal
2222

2323
import (
24+
"context"
2425
"math/rand"
2526
"sync"
2627
"testing"
@@ -172,7 +173,7 @@ func Test_pollerAutoscaler(t *testing.T) {
172173
TargetUtilization: float64(tt.args.targetMilliUsage) / 1000,
173174
},
174175
testlogger.NewZap(t),
175-
worker.NewPermit(tt.args.initialPollerCount),
176+
worker.NewResizablePermit(tt.args.initialPollerCount),
176177
// hook function that collects number of iterations
177178
func() {
178179
autoscalerEpoch.Add(1)
@@ -192,9 +193,9 @@ func Test_pollerAutoscaler(t *testing.T) {
192193
go func() {
193194
defer wg.Done()
194195
for pollResult := range pollChan {
195-
pollerScaler.Acquire(1)
196+
pollerScaler.permit.Acquire(context.Background())
196197
pollerScaler.CollectUsage(pollResult)
197-
pollerScaler.Release(1)
198+
pollerScaler.permit.Release()
198199
}
199200
}()
200201
}

internal/internal_worker_base.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,13 @@ func newBaseWorker(options baseWorkerOptions, logger *zap.Logger, metricsScope t
169169
ctx, cancel := context.WithCancel(context.Background())
170170

171171
concurrency := &worker.ConcurrencyLimit{
172-
PollerPermit: worker.NewPermit(options.pollerCount),
173-
TaskPermit: worker.NewPermit(options.maxConcurrentTask),
172+
PollerPermit: worker.NewResizablePermit(options.pollerCount),
173+
TaskPermit: worker.NewChannelPermit(options.maxConcurrentTask),
174174
}
175175

176176
var pollerAS *pollerAutoScaler
177177
if pollerOptions := options.pollerAutoScaler; pollerOptions.Enabled {
178-
concurrency.PollerPermit = worker.NewPermit(pollerOptions.InitCount)
178+
concurrency.PollerPermit = worker.NewResizablePermit(pollerOptions.InitCount)
179179
pollerAS = newPollerScaler(
180180
pollerOptions,
181181
logger,
@@ -252,7 +252,7 @@ func (bw *baseWorker) runPoller() {
252252
select {
253253
case <-bw.shutdownCh:
254254
return
255-
case <-bw.concurrency.TaskPermit.AcquireChan(bw.limiterContext, &bw.shutdownWG): // don't poll unless there is a task permit
255+
case <-bw.concurrency.TaskPermit.GetChan(): // don't poll unless there is a task permit
256256
// TODO move to a centralized place inside the worker
257257
// emit metrics on concurrent task permit quota and current task permit count
258258
// NOTE task permit doesn't mean there is a task running, it still needs to poll until it gets a task to process
@@ -300,10 +300,10 @@ func (bw *baseWorker) pollTask() {
300300
var task interface{}
301301

302302
if bw.pollerAutoScaler != nil {
303-
if pErr := bw.pollerAutoScaler.Acquire(1); pErr == nil {
304-
defer bw.pollerAutoScaler.Release(1)
303+
if pErr := bw.concurrency.PollerPermit.Acquire(bw.limiterContext); pErr == nil {
304+
defer bw.concurrency.PollerPermit.Release()
305305
} else {
306-
bw.logger.Warn("poller auto scaler acquire error", zap.Error(pErr))
306+
bw.logger.Warn("poller permit acquire error", zap.Error(pErr))
307307
}
308308
}
309309

@@ -339,7 +339,7 @@ func (bw *baseWorker) pollTask() {
339339
case <-bw.shutdownCh:
340340
}
341341
} else {
342-
bw.concurrency.TaskPermit.Release(1) // poll failed, trigger a new poll by returning a task permit
342+
bw.concurrency.TaskPermit.Release() // poll failed, trigger a new poll by returning a task permit
343343
}
344344
}
345345

@@ -374,7 +374,7 @@ func (bw *baseWorker) processTask(task interface{}) {
374374
}
375375

376376
if isPolledTask {
377-
bw.concurrency.TaskPermit.Release(1) // task processed, trigger a new poll by returning a task permit
377+
bw.concurrency.TaskPermit.Release() // task processed, trigger a new poll by returning a task permit
378378
}
379379
}()
380380
err := bw.options.taskWorker.ProcessTask(task)

internal/worker/channel_permit.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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 worker
22+
23+
import (
24+
"context"
25+
"fmt"
26+
)
27+
28+
type channelPermit struct {
29+
channel chan struct{}
30+
}
31+
32+
// NewChannelPermit creates a static permit that's not resizable
33+
func NewChannelPermit(count int) ChannelPermit {
34+
channel := make(chan struct{}, count)
35+
for i := 0; i < count; i++ {
36+
channel <- struct{}{}
37+
}
38+
return &channelPermit{channel: channel}
39+
}
40+
41+
func (p *channelPermit) Acquire(ctx context.Context) error {
42+
select {
43+
case <-ctx.Done():
44+
return fmt.Errorf("failed to acquire permit before context is done")
45+
case p.channel <- struct{}{}:
46+
return nil
47+
}
48+
}
49+
50+
// AcquireChan returns a permit ready channel
51+
func (p *channelPermit) GetChan() <-chan struct{} {
52+
return p.channel
53+
}
54+
55+
func (p *channelPermit) Release() {
56+
p.channel <- struct{}{}
57+
}
58+
59+
// Count returns the number of permits available
60+
func (p *channelPermit) Count() int {
61+
return len(p.channel)
62+
}
63+
64+
func (p *channelPermit) Quota() int {
65+
return cap(p.channel)
66+
}
67+
68+
// SetQuota on static permit doesn't take effect
69+
func (p *channelPermit) SetQuota(_ int) {
70+
}

internal/worker/concurrency.go

Lines changed: 10 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -22,79 +22,27 @@ package worker
2222

2323
import (
2424
"context"
25-
"fmt"
26-
"sync"
27-
28-
"github.com/marusama/semaphore/v2"
2925
)
3026

31-
var _ Permit = (*permit)(nil)
27+
var _ Permit = (*resizablePermit)(nil)
28+
var _ ChannelPermit = (*channelPermit)(nil)
3229

3330
// ConcurrencyLimit contains synchronization primitives for dynamically controlling the concurrencies in workers
3431
type ConcurrencyLimit struct {
35-
PollerPermit Permit // controls concurrency of pollers
36-
TaskPermit Permit // controls concurrency of task processing
32+
PollerPermit Permit // controls concurrency of pollers
33+
TaskPermit ChannelPermit // controls concurrency of task processing
3734
}
3835

3936
// Permit is an adaptive permit issuer to control concurrency
4037
type Permit interface {
41-
Acquire(context.Context, int) error
42-
AcquireChan(context.Context, *sync.WaitGroup) <-chan struct{}
38+
Acquire(context.Context) error
39+
Count() int
4340
Quota() int
41+
Release()
4442
SetQuota(int)
45-
Count() int
46-
Release(count int)
47-
}
48-
49-
type permit struct {
50-
sem semaphore.Semaphore
51-
}
52-
53-
// NewPermit creates a dynamic permit that's resizable
54-
func NewPermit(initCount int) Permit {
55-
return &permit{sem: semaphore.New(initCount)}
56-
}
57-
58-
// Acquire is blocking until a permit is acquired or returns error after context is done
59-
// Remember to call Release(count) to release the permit after usage
60-
func (p *permit) Acquire(ctx context.Context, count int) error {
61-
if err := p.sem.Acquire(ctx, count); err != nil {
62-
return fmt.Errorf("failed to acquire permit before context is done: %w", err)
63-
}
64-
return nil
65-
}
66-
67-
// AcquireChan returns a permit ready channel. Similar to Acquire, but non-blocking.
68-
// Remember to call Release(1) to release the permit after usage
69-
func (p *permit) AcquireChan(ctx context.Context, wg *sync.WaitGroup) <-chan struct{} {
70-
ch := make(chan struct{})
71-
wg.Add(1)
72-
go func() {
73-
defer wg.Done()
74-
if err := p.sem.Acquire(ctx, 1); err != nil {
75-
return
76-
}
77-
select { // try to send to channel, but don't block if listener is gone
78-
case ch <- struct{}{}:
79-
case <-ctx.Done():
80-
p.sem.Release(1)
81-
}
82-
}()
83-
return ch
84-
}
85-
86-
func (p *permit) Release(count int) {
87-
p.sem.Release(count)
88-
}
89-
90-
func (p *permit) Quota() int {
91-
return p.sem.GetLimit()
92-
}
93-
94-
func (p *permit) SetQuota(c int) {
95-
p.sem.SetLimit(c)
9643
}
9744

98-
func (p *permit) Count() int {
99-
return p.sem.GetCount()
45+
type ChannelPermit interface {
46+
Permit
47+
GetChan() <-chan struct{} // fetch the underlying channel
10048
}

0 commit comments

Comments
 (0)