1
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-01-03 14:57:55 -05:00

Add MaxWorker settings to queues

This commit is contained in:
Andrew Thornton 2019-12-30 15:54:19 +00:00
parent a492b3071c
commit b1c9fa7f1a
No known key found for this signature in database
GPG Key ID: 3CDE74631F13A748
15 changed files with 312 additions and 73 deletions

View File

@ -28,17 +28,28 @@ type Manager struct {
// Description represents a working queue inheriting from Gitea. // Description represents a working queue inheriting from Gitea.
type Description struct { type Description struct {
mutex sync.Mutex mutex sync.Mutex
QID int64 QID int64
Queue Queue Queue Queue
Type Type Type Type
Name string Name string
Configuration interface{} Configuration interface{}
ExemplarType string ExemplarType string
addWorkers func(number int, timeout time.Duration) context.CancelFunc Pool PoolManager
numberOfWorkers func() int counter int64
counter int64 PoolWorkers map[int64]*PoolWorkers
PoolWorkers map[int64]*PoolWorkers }
// PoolManager is a simple interface to get certain details from a worker pool
type PoolManager interface {
AddWorkers(number int, timeout time.Duration) context.CancelFunc
NumberOfWorkers() int
MaxNumberOfWorkers() int
SetMaxNumberOfWorkers(int)
BoostTimeout() time.Duration
BlockTimeout() time.Duration
BoostWorkers() int
SetSettings(maxNumberOfWorkers, boostWorkers int, timeout time.Duration)
} }
// DescriptionList implements the sort.Interface // DescriptionList implements the sort.Interface
@ -76,18 +87,16 @@ func (m *Manager) Add(queue Queue,
t Type, t Type,
configuration, configuration,
exemplar interface{}, exemplar interface{},
addWorkers func(number int, timeout time.Duration) context.CancelFunc, pool PoolManager) int64 {
numberOfWorkers func() int) int64 {
cfg, _ := json.Marshal(configuration) cfg, _ := json.Marshal(configuration)
desc := &Description{ desc := &Description{
Queue: queue, Queue: queue,
Type: t, Type: t,
Configuration: string(cfg), Configuration: string(cfg),
ExemplarType: reflect.TypeOf(exemplar).String(), ExemplarType: reflect.TypeOf(exemplar).String(),
PoolWorkers: make(map[int64]*PoolWorkers), PoolWorkers: make(map[int64]*PoolWorkers),
addWorkers: addWorkers, Pool: pool,
numberOfWorkers: numberOfWorkers,
} }
m.mutex.Lock() m.mutex.Lock()
m.counter++ m.counter++
@ -177,20 +186,61 @@ func (q *Description) RemoveWorkers(pid int64) {
} }
// AddWorkers adds workers to the queue if it has registered an add worker function // AddWorkers adds workers to the queue if it has registered an add worker function
func (q *Description) AddWorkers(number int, timeout time.Duration) { func (q *Description) AddWorkers(number int, timeout time.Duration) context.CancelFunc {
if q.addWorkers != nil { if q.Pool != nil {
_ = q.addWorkers(number, timeout) // the cancel will be added to the pool workers description above
return q.Pool.AddWorkers(number, timeout)
} }
return nil
} }
// NumberOfWorkers returns the number of workers in the queue // NumberOfWorkers returns the number of workers in the queue
func (q *Description) NumberOfWorkers() int { func (q *Description) NumberOfWorkers() int {
if q.numberOfWorkers != nil { if q.Pool != nil {
return q.numberOfWorkers() return q.Pool.NumberOfWorkers()
} }
return -1 return -1
} }
// MaxNumberOfWorkers returns the maximum number of workers for the pool
func (q *Description) MaxNumberOfWorkers() int {
if q.Pool != nil {
return q.Pool.MaxNumberOfWorkers()
}
return 0
}
// BoostWorkers returns the number of workers for a boost
func (q *Description) BoostWorkers() int {
if q.Pool != nil {
return q.Pool.BoostWorkers()
}
return -1
}
// BoostTimeout returns the timeout of the next boost
func (q *Description) BoostTimeout() time.Duration {
if q.Pool != nil {
return q.Pool.BoostTimeout()
}
return 0
}
// BlockTimeout returns the timeout til the next boost
func (q *Description) BlockTimeout() time.Duration {
if q.Pool != nil {
return q.Pool.BlockTimeout()
}
return 0
}
// SetSettings sets the setable boost values
func (q *Description) SetSettings(maxNumberOfWorkers, boostWorkers int, timeout time.Duration) {
if q.Pool != nil {
q.Pool.SetSettings(maxNumberOfWorkers, boostWorkers, timeout)
}
}
func (l DescriptionList) Len() int { func (l DescriptionList) Len() int {
return len(l) return len(l)
} }

View File

@ -21,6 +21,7 @@ type ChannelQueueConfiguration struct {
QueueLength int QueueLength int
BatchLength int BatchLength int
Workers int Workers int
MaxWorkers int
BlockTimeout time.Duration BlockTimeout time.Duration
BoostTimeout time.Duration BoostTimeout time.Duration
BoostWorkers int BoostWorkers int
@ -50,20 +51,21 @@ func NewChannelQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, erro
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
queue := &ChannelQueue{ queue := &ChannelQueue{
pool: &WorkerPool{ pool: &WorkerPool{
baseCtx: ctx, baseCtx: ctx,
cancel: cancel, cancel: cancel,
batchLength: config.BatchLength, batchLength: config.BatchLength,
handle: handle, handle: handle,
dataChan: dataChan, dataChan: dataChan,
blockTimeout: config.BlockTimeout, blockTimeout: config.BlockTimeout,
boostTimeout: config.BoostTimeout, boostTimeout: config.BoostTimeout,
boostWorkers: config.BoostWorkers, boostWorkers: config.BoostWorkers,
maxNumberOfWorkers: config.MaxWorkers,
}, },
exemplar: exemplar, exemplar: exemplar,
workers: config.Workers, workers: config.Workers,
name: config.Name, name: config.Name,
} }
queue.pool.qid = GetManager().Add(queue, ChannelQueueType, config, exemplar, queue.pool.AddWorkers, queue.pool.NumberOfWorkers) queue.pool.qid = GetManager().Add(queue, ChannelQueueType, config, exemplar, queue.pool)
return queue, nil return queue, nil
} }

View File

@ -27,6 +27,7 @@ func TestChannelQueue(t *testing.T) {
ChannelQueueConfiguration{ ChannelQueueConfiguration{
QueueLength: 20, QueueLength: 20,
Workers: 1, Workers: 1,
MaxWorkers: 10,
BlockTimeout: 1 * time.Second, BlockTimeout: 1 * time.Second,
BoostTimeout: 5 * time.Minute, BoostTimeout: 5 * time.Minute,
BoostWorkers: 5, BoostWorkers: 5,
@ -62,6 +63,7 @@ func TestChannelQueue_Batch(t *testing.T) {
QueueLength: 20, QueueLength: 20,
BatchLength: 2, BatchLength: 2,
Workers: 1, Workers: 1,
MaxWorkers: 10,
BlockTimeout: 1 * time.Second, BlockTimeout: 1 * time.Second,
BoostTimeout: 5 * time.Minute, BoostTimeout: 5 * time.Minute,
BoostWorkers: 5, BoostWorkers: 5,

View File

@ -25,6 +25,7 @@ type LevelQueueConfiguration struct {
QueueLength int QueueLength int
BatchLength int BatchLength int
Workers int Workers int
MaxWorkers int
BlockTimeout time.Duration BlockTimeout time.Duration
BoostTimeout time.Duration BoostTimeout time.Duration
BoostWorkers int BoostWorkers int
@ -60,14 +61,15 @@ func NewLevelQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error)
queue := &LevelQueue{ queue := &LevelQueue{
pool: &WorkerPool{ pool: &WorkerPool{
baseCtx: ctx, baseCtx: ctx,
cancel: cancel, cancel: cancel,
batchLength: config.BatchLength, batchLength: config.BatchLength,
handle: handle, handle: handle,
dataChan: dataChan, dataChan: dataChan,
blockTimeout: config.BlockTimeout, blockTimeout: config.BlockTimeout,
boostTimeout: config.BoostTimeout, boostTimeout: config.BoostTimeout,
boostWorkers: config.BoostWorkers, boostWorkers: config.BoostWorkers,
maxNumberOfWorkers: config.MaxWorkers,
}, },
queue: internal, queue: internal,
exemplar: exemplar, exemplar: exemplar,
@ -76,7 +78,7 @@ func NewLevelQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error)
workers: config.Workers, workers: config.Workers,
name: config.Name, name: config.Name,
} }
queue.pool.qid = GetManager().Add(queue, LevelQueueType, config, exemplar, queue.pool.AddWorkers, queue.pool.NumberOfWorkers) queue.pool.qid = GetManager().Add(queue, LevelQueueType, config, exemplar, queue.pool)
return queue, nil return queue, nil
} }

View File

@ -23,6 +23,7 @@ type PersistableChannelQueueConfiguration struct {
Timeout time.Duration Timeout time.Duration
MaxAttempts int MaxAttempts int
Workers int Workers int
MaxWorkers int
BlockTimeout time.Duration BlockTimeout time.Duration
BoostTimeout time.Duration BoostTimeout time.Duration
BoostWorkers int BoostWorkers int
@ -48,6 +49,7 @@ func NewPersistableChannelQueue(handle HandlerFunc, cfg, exemplar interface{}) (
QueueLength: config.QueueLength, QueueLength: config.QueueLength,
BatchLength: config.BatchLength, BatchLength: config.BatchLength,
Workers: config.Workers, Workers: config.Workers,
MaxWorkers: config.MaxWorkers,
BlockTimeout: config.BlockTimeout, BlockTimeout: config.BlockTimeout,
BoostTimeout: config.BoostTimeout, BoostTimeout: config.BoostTimeout,
BoostWorkers: config.BoostWorkers, BoostWorkers: config.BoostWorkers,
@ -63,6 +65,7 @@ func NewPersistableChannelQueue(handle HandlerFunc, cfg, exemplar interface{}) (
QueueLength: config.QueueLength, QueueLength: config.QueueLength,
BatchLength: config.BatchLength, BatchLength: config.BatchLength,
Workers: 1, Workers: 1,
MaxWorkers: 6,
BlockTimeout: 1 * time.Second, BlockTimeout: 1 * time.Second,
BoostTimeout: 5 * time.Minute, BoostTimeout: 5 * time.Minute,
BoostWorkers: 5, BoostWorkers: 5,
@ -96,7 +99,7 @@ func NewPersistableChannelQueue(handle HandlerFunc, cfg, exemplar interface{}) (
}, },
closed: make(chan struct{}), closed: make(chan struct{}),
} }
_ = GetManager().Add(queue, PersistableChannelQueueType, config, exemplar, nil, nil) _ = GetManager().Add(queue, PersistableChannelQueueType, config, exemplar, nil)
return queue, nil return queue, nil
} }

View File

@ -36,6 +36,7 @@ func TestPersistableChannelQueue(t *testing.T) {
BatchLength: 2, BatchLength: 2,
QueueLength: 20, QueueLength: 20,
Workers: 1, Workers: 1,
MaxWorkers: 10,
}, &testData{}) }, &testData{})
assert.NoError(t, err) assert.NoError(t, err)
@ -89,6 +90,7 @@ func TestPersistableChannelQueue(t *testing.T) {
BatchLength: 2, BatchLength: 2,
QueueLength: 20, QueueLength: 20,
Workers: 1, Workers: 1,
MaxWorkers: 10,
}, &testData{}) }, &testData{})
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -35,6 +35,7 @@ func TestLevelQueue(t *testing.T) {
DataDir: tmpDir, DataDir: tmpDir,
BatchLength: 2, BatchLength: 2,
Workers: 1, Workers: 1,
MaxWorkers: 10,
QueueLength: 20, QueueLength: 20,
BlockTimeout: 1 * time.Second, BlockTimeout: 1 * time.Second,
BoostTimeout: 5 * time.Minute, BoostTimeout: 5 * time.Minute,
@ -94,6 +95,7 @@ func TestLevelQueue(t *testing.T) {
DataDir: tmpDir, DataDir: tmpDir,
BatchLength: 2, BatchLength: 2,
Workers: 1, Workers: 1,
MaxWorkers: 10,
QueueLength: 20, QueueLength: 20,
BlockTimeout: 1 * time.Second, BlockTimeout: 1 * time.Second,
BoostTimeout: 5 * time.Minute, BoostTimeout: 5 * time.Minute,

View File

@ -49,6 +49,7 @@ type RedisQueueConfiguration struct {
QueueLength int QueueLength int
QueueName string QueueName string
Workers int Workers int
MaxWorkers int
BlockTimeout time.Duration BlockTimeout time.Duration
BoostTimeout time.Duration BoostTimeout time.Duration
BoostWorkers int BoostWorkers int
@ -70,14 +71,15 @@ func NewRedisQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error)
var queue = &RedisQueue{ var queue = &RedisQueue{
pool: &WorkerPool{ pool: &WorkerPool{
baseCtx: ctx, baseCtx: ctx,
cancel: cancel, cancel: cancel,
batchLength: config.BatchLength, batchLength: config.BatchLength,
handle: handle, handle: handle,
dataChan: dataChan, dataChan: dataChan,
blockTimeout: config.BlockTimeout, blockTimeout: config.BlockTimeout,
boostTimeout: config.BoostTimeout, boostTimeout: config.BoostTimeout,
boostWorkers: config.BoostWorkers, boostWorkers: config.BoostWorkers,
maxNumberOfWorkers: config.MaxWorkers,
}, },
queueName: config.QueueName, queueName: config.QueueName,
exemplar: exemplar, exemplar: exemplar,
@ -102,7 +104,7 @@ func NewRedisQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error)
if err := queue.client.Ping().Err(); err != nil { if err := queue.client.Ping().Err(); err != nil {
return nil, err return nil, err
} }
queue.pool.qid = GetManager().Add(queue, RedisQueueType, config, exemplar, queue.pool.AddWorkers, queue.pool.NumberOfWorkers) queue.pool.qid = GetManager().Add(queue, RedisQueueType, config, exemplar, queue.pool)
return queue, nil return queue, nil
} }

View File

@ -123,7 +123,7 @@ func NewWrappedQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, erro
name: config.Name, name: config.Name,
}, },
} }
_ = GetManager().Add(queue, WrappedQueueType, config, exemplar, nil, nil) _ = GetManager().Add(queue, WrappedQueueType, config, exemplar, nil)
return queue, nil return queue, nil
} }

View File

@ -14,24 +14,25 @@ import (
// WorkerPool takes // WorkerPool takes
type WorkerPool struct { type WorkerPool struct {
lock sync.Mutex lock sync.Mutex
baseCtx context.Context baseCtx context.Context
cancel context.CancelFunc cancel context.CancelFunc
cond *sync.Cond cond *sync.Cond
qid int64 qid int64
numberOfWorkers int maxNumberOfWorkers int
batchLength int numberOfWorkers int
handle HandlerFunc batchLength int
dataChan chan Data handle HandlerFunc
blockTimeout time.Duration dataChan chan Data
boostTimeout time.Duration blockTimeout time.Duration
boostWorkers int boostTimeout time.Duration
boostWorkers int
} }
// Push pushes the data to the internal channel // Push pushes the data to the internal channel
func (p *WorkerPool) Push(data Data) { func (p *WorkerPool) Push(data Data) {
p.lock.Lock() p.lock.Lock()
if p.blockTimeout > 0 && p.boostTimeout > 0 { if p.blockTimeout > 0 && p.boostTimeout > 0 && (p.numberOfWorkers <= p.maxNumberOfWorkers || p.maxNumberOfWorkers < 0) {
p.lock.Unlock() p.lock.Unlock()
p.pushBoost(data) p.pushBoost(data)
} else { } else {
@ -63,7 +64,7 @@ func (p *WorkerPool) pushBoost(data Data) {
} }
case <-timer.C: case <-timer.C:
p.lock.Lock() p.lock.Lock()
if p.blockTimeout > ourTimeout { if p.blockTimeout > ourTimeout || (p.numberOfWorkers > p.maxNumberOfWorkers && p.maxNumberOfWorkers >= 0) {
p.lock.Unlock() p.lock.Unlock()
p.dataChan <- data p.dataChan <- data
return return
@ -71,11 +72,15 @@ func (p *WorkerPool) pushBoost(data Data) {
p.blockTimeout *= 2 p.blockTimeout *= 2
ctx, cancel := context.WithCancel(p.baseCtx) ctx, cancel := context.WithCancel(p.baseCtx)
desc := GetManager().GetDescription(p.qid) desc := GetManager().GetDescription(p.qid)
boost := p.boostWorkers
if (boost+p.numberOfWorkers) > p.maxNumberOfWorkers && p.maxNumberOfWorkers >= 0 {
boost = p.maxNumberOfWorkers - p.numberOfWorkers
}
if desc != nil { if desc != nil {
log.Warn("WorkerPool: %d (for %s) Channel blocked for %v - adding %d temporary workers for %s, block timeout now %v", p.qid, desc.Name, ourTimeout, p.boostWorkers, p.boostTimeout, p.blockTimeout) log.Warn("WorkerPool: %d (for %s) Channel blocked for %v - adding %d temporary workers for %s, block timeout now %v", p.qid, desc.Name, ourTimeout, boost, p.boostTimeout, p.blockTimeout)
start := time.Now() start := time.Now()
pid := desc.RegisterWorkers(p.boostWorkers, start, false, start, cancel) pid := desc.RegisterWorkers(boost, start, false, start, cancel)
go func() { go func() {
<-ctx.Done() <-ctx.Done()
desc.RemoveWorkers(pid) desc.RemoveWorkers(pid)
@ -91,7 +96,7 @@ func (p *WorkerPool) pushBoost(data Data) {
p.blockTimeout /= 2 p.blockTimeout /= 2
p.lock.Unlock() p.lock.Unlock()
}() }()
p.addWorkers(ctx, p.boostWorkers) p.addWorkers(ctx, boost)
p.lock.Unlock() p.lock.Unlock()
p.dataChan <- data p.dataChan <- data
} }
@ -105,7 +110,53 @@ func (p *WorkerPool) NumberOfWorkers() int {
return p.numberOfWorkers return p.numberOfWorkers
} }
// AddWorkers adds workers to the pool // MaxNumberOfWorkers returns the maximum number of workers automatically added to the pool
func (p *WorkerPool) MaxNumberOfWorkers() int {
p.lock.Lock()
defer p.lock.Unlock()
return p.maxNumberOfWorkers
}
// BoostWorkers returns the number of workers for a boost
func (p *WorkerPool) BoostWorkers() int {
p.lock.Lock()
defer p.lock.Unlock()
return p.boostWorkers
}
// BoostTimeout returns the timeout of the next boost
func (p *WorkerPool) BoostTimeout() time.Duration {
p.lock.Lock()
defer p.lock.Unlock()
return p.boostTimeout
}
// BlockTimeout returns the timeout til the next boost
func (p *WorkerPool) BlockTimeout() time.Duration {
p.lock.Lock()
defer p.lock.Unlock()
return p.blockTimeout
}
// SetSettings sets the setable boost values
func (p *WorkerPool) SetSettings(maxNumberOfWorkers, boostWorkers int, timeout time.Duration) {
p.lock.Lock()
defer p.lock.Unlock()
p.maxNumberOfWorkers = maxNumberOfWorkers
p.boostWorkers = boostWorkers
p.boostTimeout = timeout
}
// SetMaxNumberOfWorkers sets the maximum number of workers automatically added to the pool
// Changing this number will not change the number of current workers but will change the limit
// for future additions
func (p *WorkerPool) SetMaxNumberOfWorkers(newMax int) {
p.lock.Lock()
defer p.lock.Unlock()
p.maxNumberOfWorkers = newMax
}
// AddWorkers adds workers to the pool - this allows the number of workers to go above the limit
func (p *WorkerPool) AddWorkers(number int, timeout time.Duration) context.CancelFunc { func (p *WorkerPool) AddWorkers(number int, timeout time.Duration) context.CancelFunc {
var ctx context.Context var ctx context.Context
var cancel context.CancelFunc var cancel context.CancelFunc

View File

@ -31,6 +31,7 @@ type queueSettings struct {
MaxAttempts int MaxAttempts int
Timeout time.Duration Timeout time.Duration
Workers int Workers int
MaxWorkers int
BlockTimeout time.Duration BlockTimeout time.Duration
BoostTimeout time.Duration BoostTimeout time.Duration
BoostWorkers int BoostWorkers int
@ -53,6 +54,7 @@ func CreateQueue(name string, handle queue.HandlerFunc, exemplar interface{}) qu
opts["DBIndex"] = q.DBIndex opts["DBIndex"] = q.DBIndex
opts["QueueName"] = q.QueueName opts["QueueName"] = q.QueueName
opts["Workers"] = q.Workers opts["Workers"] = q.Workers
opts["MaxWorkers"] = q.MaxWorkers
opts["BlockTimeout"] = q.BlockTimeout opts["BlockTimeout"] = q.BlockTimeout
opts["BoostTimeout"] = q.BoostTimeout opts["BoostTimeout"] = q.BoostTimeout
opts["BoostWorkers"] = q.BoostWorkers opts["BoostWorkers"] = q.BoostWorkers
@ -108,6 +110,7 @@ func getQueueSettings(name string) queueSettings {
q.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Queue.MaxAttempts) q.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Queue.MaxAttempts)
q.Timeout = sec.Key("TIMEOUT").MustDuration(Queue.Timeout) q.Timeout = sec.Key("TIMEOUT").MustDuration(Queue.Timeout)
q.Workers = sec.Key("WORKERS").MustInt(Queue.Workers) q.Workers = sec.Key("WORKERS").MustInt(Queue.Workers)
q.MaxWorkers = sec.Key("MAX_WORKERS").MustInt(Queue.MaxWorkers)
q.BlockTimeout = sec.Key("BLOCK_TIMEOUT").MustDuration(Queue.BlockTimeout) q.BlockTimeout = sec.Key("BLOCK_TIMEOUT").MustDuration(Queue.BlockTimeout)
q.BoostTimeout = sec.Key("BOOST_TIMEOUT").MustDuration(Queue.BoostTimeout) q.BoostTimeout = sec.Key("BOOST_TIMEOUT").MustDuration(Queue.BoostTimeout)
q.BoostWorkers = sec.Key("BOOST_WORKERS").MustInt(Queue.BoostWorkers) q.BoostWorkers = sec.Key("BOOST_WORKERS").MustInt(Queue.BoostWorkers)
@ -135,6 +138,7 @@ func NewQueueService() {
Queue.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(10) Queue.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(10)
Queue.Timeout = sec.Key("TIMEOUT").MustDuration(GracefulHammerTime + 30*time.Second) Queue.Timeout = sec.Key("TIMEOUT").MustDuration(GracefulHammerTime + 30*time.Second)
Queue.Workers = sec.Key("WORKERS").MustInt(1) Queue.Workers = sec.Key("WORKERS").MustInt(1)
Queue.MaxWorkers = sec.Key("MAX_WORKERS").MustInt(10)
Queue.BlockTimeout = sec.Key("BLOCK_TIMEOUT").MustDuration(1 * time.Second) Queue.BlockTimeout = sec.Key("BLOCK_TIMEOUT").MustDuration(1 * time.Second)
Queue.BoostTimeout = sec.Key("BOOST_TIMEOUT").MustDuration(5 * time.Minute) Queue.BoostTimeout = sec.Key("BOOST_TIMEOUT").MustDuration(5 * time.Minute)
Queue.BoostWorkers = sec.Key("BOOST_WORKERS").MustInt(5) Queue.BoostWorkers = sec.Key("BOOST_WORKERS").MustInt(5)

View File

@ -1408,7 +1408,7 @@ settings.protect_check_status_contexts_list = Status checks found in the last we
settings.protect_required_approvals = Required approvals: settings.protect_required_approvals = Required approvals:
settings.protect_required_approvals_desc = Allow only to merge pull request with enough positive reviews. settings.protect_required_approvals_desc = Allow only to merge pull request with enough positive reviews.
settings.protect_approvals_whitelist_enabled = Restrict approvals to whitelisted users or teams settings.protect_approvals_whitelist_enabled = Restrict approvals to whitelisted users or teams
settings.protect_approvals_whitelist_enabled_desc = Only reviews from whitelisted users or teams will count to the required approvals. Without approval whitelist, reviews from anyone with write access count to the required approvals. settings.protect_approvals_whitelist_enabled_desc = Only reviews from whitelisted users or teams will count to the required approvals. Without approval whitelist, reviews from anyone with write access count to the required approvals.
settings.protect_approvals_whitelist_users = Whitelisted reviewers: settings.protect_approvals_whitelist_users = Whitelisted reviewers:
settings.protect_approvals_whitelist_teams = Whitelisted teams for reviews: settings.protect_approvals_whitelist_teams = Whitelisted teams for reviews:
settings.add_protected_branch = Enable protection settings.add_protected_branch = Enable protection
@ -2028,6 +2028,7 @@ monitor.queue.name = Name
monitor.queue.type = Type monitor.queue.type = Type
monitor.queue.exemplar = Exemplar Type monitor.queue.exemplar = Exemplar Type
monitor.queue.numberworkers = Number of Workers monitor.queue.numberworkers = Number of Workers
monitor.queue.maxnumberworkers = Max Number of Workers
monitor.queue.review = Review Config monitor.queue.review = Review Config
monitor.queue.review_add = Review/Add Workers monitor.queue.review_add = Review/Add Workers
monitor.queue.configuration = Initial Configuration monitor.queue.configuration = Initial Configuration
@ -2043,7 +2044,26 @@ monitor.queue.pool.addworkers.numberworkers.placeholder = Number of Workers
monitor.queue.pool.addworkers.timeout.placeholder = Set to 0 for no timeout monitor.queue.pool.addworkers.timeout.placeholder = Set to 0 for no timeout
monitor.queue.pool.addworkers.mustnumbergreaterzero = Number of Workers to add must be greater than zero monitor.queue.pool.addworkers.mustnumbergreaterzero = Number of Workers to add must be greater than zero
monitor.queue.pool.addworkers.musttimeoutduration = Timeout must be a golang duration eg. 5m or be 0 monitor.queue.pool.addworkers.musttimeoutduration = Timeout must be a golang duration eg. 5m or be 0
monitor.queue.settings.title = Pool Settings
monitor.queue.settings.desc = Pools dynamically grow with a boost in response to their worker queue blocking. These changes will not affect current worker groups.
monitor.queue.settings.timeout = Boost Timeout
monitor.queue.settings.timeout.placeholder = Currently %[1]v
monitor.queue.settings.timeout.error = Timeout must be a golang duration eg. 5m or be 0
monitor.queue.settings.numberworkers = Boost Number of Workers
monitor.queue.settings.numberworkers.placeholder = Currently %[1]d
monitor.queue.settings.numberworkers.error = Number of Workers to add must be greater than or equal to zero
monitor.queue.settings.maxnumberworkers = Max Number of workers
monitor.queue.settings.maxnumberworkers.placeholder = Currently %[1]d
monitor.queue.settings.maxnumberworkers.error = Max number of workers must be a number
monitor.queue.settings.submit = Change Settings
monitor.queue.settings.changed = Settings Updated
monitor.queue.settings.blocktimeout = Current Block Timeout
monitor.queue.settings.blocktimeout.value = %[1]v
monitor.queue.pool.none = This queue does not have a Pool
monitor.queue.pool.added = Worker Group Added monitor.queue.pool.added = Worker Group Added
monitor.queue.pool.max_changed = Maximum number of workers changed
monitor.queue.pool.workers.title = Active Worker Groups monitor.queue.pool.workers.title = Active Worker Groups
monitor.queue.pool.workers.none = No worker groups. monitor.queue.pool.workers.none = No worker groups.
monitor.queue.pool.cancel = Shutdown Worker Group monitor.queue.pool.cancel = Shutdown Worker Group

View File

@ -11,6 +11,7 @@ import (
"net/url" "net/url"
"os" "os"
"runtime" "runtime"
"strconv"
"strings" "strings"
"time" "time"
@ -421,7 +422,74 @@ func AddWorkers(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid)) ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid))
return return
} }
if desc.Pool == nil {
ctx.Flash.Error(ctx.Tr("admin.monitor.queue.pool.none"))
ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid))
return
}
desc.AddWorkers(number, timeout) desc.AddWorkers(number, timeout)
ctx.Flash.Success(ctx.Tr("admin.monitor.queue.pool.added")) ctx.Flash.Success(ctx.Tr("admin.monitor.queue.pool.added"))
ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid)) ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid))
} }
// SetQueueSettings sets the maximum number of workers for this queue
func SetQueueSettings(ctx *context.Context) {
qid := ctx.ParamsInt64("qid")
desc := queue.GetManager().GetDescription(qid)
if desc == nil {
ctx.Status(404)
return
}
if desc.Pool == nil {
ctx.Flash.Error(ctx.Tr("admin.monitor.queue.pool.none"))
ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid))
return
}
maxNumberStr := ctx.Query("max-number")
numberStr := ctx.Query("number")
timeoutStr := ctx.Query("timeout")
var err error
var maxNumber, number int
var timeout time.Duration
if len(maxNumberStr) > 0 {
maxNumber, err = strconv.Atoi(maxNumberStr)
if err != nil {
ctx.Flash.Error(ctx.Tr("admin.monitor.queue.settings.maxnumberworkers.error"))
ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid))
return
}
if maxNumber < -1 {
maxNumber = -1
}
} else {
maxNumber = desc.MaxNumberOfWorkers()
}
if len(numberStr) > 0 {
number, err = strconv.Atoi(numberStr)
if err != nil || number < 0 {
ctx.Flash.Error(ctx.Tr("admin.monitor.queue.settings.numberworkers.error"))
ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid))
return
}
} else {
number = desc.BoostWorkers()
}
if len(timeoutStr) > 0 {
timeout, err = time.ParseDuration(timeoutStr)
if err != nil {
ctx.Flash.Error(ctx.Tr("admin.monitor.queue.settings.timeout.error"))
ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid))
return
}
} else {
timeout = desc.Pool.BoostTimeout()
}
desc.SetSettings(maxNumber, number, timeout)
ctx.Flash.Success(ctx.Tr("admin.monitor.queue.settings.changed"))
ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/admin/monitor/queue/%d", qid))
}

View File

@ -416,6 +416,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/cancel/:pid", admin.MonitorCancel) m.Post("/cancel/:pid", admin.MonitorCancel)
m.Group("/queue/:qid", func() { m.Group("/queue/:qid", func() {
m.Get("", admin.Queue) m.Get("", admin.Queue)
m.Post("/set", admin.SetQueueSettings)
m.Post("/add", admin.AddWorkers) m.Post("/add", admin.AddWorkers)
m.Post("/cancel/:pid", admin.WorkerCancel) m.Post("/cancel/:pid", admin.WorkerCancel)
}) })

View File

@ -14,6 +14,7 @@
<th>{{.i18n.Tr "admin.monitor.queue.type"}}</th> <th>{{.i18n.Tr "admin.monitor.queue.type"}}</th>
<th>{{.i18n.Tr "admin.monitor.queue.exemplar"}}</th> <th>{{.i18n.Tr "admin.monitor.queue.exemplar"}}</th>
<th>{{.i18n.Tr "admin.monitor.queue.numberworkers"}}</th> <th>{{.i18n.Tr "admin.monitor.queue.numberworkers"}}</th>
<th>{{.i18n.Tr "admin.monitor.queue.maxnumberworkers"}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -22,6 +23,7 @@
<td>{{.Queue.Type}}</td> <td>{{.Queue.Type}}</td>
<td>{{.Queue.ExemplarType}}</td> <td>{{.Queue.ExemplarType}}</td>
<td>{{$sum := .Queue.NumberOfWorkers}}{{if lt $sum 0}}-{{else}}{{$sum}}{{end}}</td> <td>{{$sum := .Queue.NumberOfWorkers}}{{if lt $sum 0}}-{{else}}{{$sum}}{{end}}</td>
<td>{{if lt $sum 0}}-{{else}}{{.Queue.MaxNumberOfWorkers}}{{end}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -40,6 +42,34 @@
{{end}} {{end}}
</div> </div>
{{else}} {{else}}
<h4 class="ui top attached header">
{{.i18n.Tr "admin.monitor.queue.settings.title"}}
</h4>
<div class="ui attached segment">
<p>{{.i18n.Tr "admin.monitor.queue.settings.desc"}}</p>
<form method="POST" action="{{.Link}}/set">
{{$.CsrfTokenHtml}}
<div class="ui form">
<div class="inline field">
<label for="max-number">{{.i18n.Tr "admin.monitor.queue.settings.maxnumberworkers"}}</label>
<input name="max-number" type="text" placeholder="{{.i18n.Tr "admin.monitor.queue.settings.maxnumberworkers.placeholder" .Queue.MaxNumberOfWorkers}}">
</div>
<div class="inline field">
<label for="timeout">{{.i18n.Tr "admin.monitor.queue.settings.timeout"}}</label>
<input name="timeout" type="text" placeholder="{{.i18n.Tr "admin.monitor.queue.settings.timeout.placeholder" .Queue.BoostTimeout }}">
</div>
<div class="inline field">
<label for="number">{{.i18n.Tr "admin.monitor.queue.settings.numberworkers"}}</label>
<input name="number" type="text" placeholder="{{.i18n.Tr "admin.monitor.queue.settings.numberworkers.placeholder" .Queue.BoostWorkers}}">
</div>
<div class="inline field">
<label>{{.i18n.Tr "admin.monitor.queue.settings.blocktimeout"}}</label>
<span>{{.i18n.Tr "admin.monitor.queue.settings.blocktimeout.value" .Queue.BlockTimeout}}</span>
</div>
<button class="ui submit button">{{.i18n.Tr "admin.monitor.queue.settings.submit"}}</button>
</div>
</form>
</div>
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.i18n.Tr "admin.monitor.queue.pool.addworkers.title"}} {{.i18n.Tr "admin.monitor.queue.pool.addworkers.title"}}
</h4> </h4>