mirror of
https://github.com/hibiken/asynq.git
synced 2025-04-21 16:20:18 +08:00
Add a function to return a list of queues to query
This commit is contained in:
parent
6de80f9f04
commit
fc5b65ddae
56
processor.go
56
processor.go
@ -3,6 +3,7 @@ package asynq
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -17,11 +18,6 @@ type processor struct {
|
|||||||
|
|
||||||
retryDelayFunc retryDelayFunc
|
retryDelayFunc retryDelayFunc
|
||||||
|
|
||||||
// timeout for blocking dequeue operation.
|
|
||||||
// dequeue needs to timeout to avoid blocking forever
|
|
||||||
// in case of a program shutdown or additon of a new queue.
|
|
||||||
dequeueTimeout time.Duration
|
|
||||||
|
|
||||||
// sema is a counting semaphore to ensure the number of active workers
|
// sema is a counting semaphore to ensure the number of active workers
|
||||||
// does not exceed the limit.
|
// does not exceed the limit.
|
||||||
sema chan struct{}
|
sema chan struct{}
|
||||||
@ -44,7 +40,6 @@ func newProcessor(r *rdb.RDB, n int, fn retryDelayFunc) *processor {
|
|||||||
return &processor{
|
return &processor{
|
||||||
rdb: r,
|
rdb: r,
|
||||||
retryDelayFunc: fn,
|
retryDelayFunc: fn,
|
||||||
dequeueTimeout: 2 * time.Second,
|
|
||||||
sema: make(chan struct{}, n),
|
sema: make(chan struct{}, n),
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
abort: make(chan struct{}),
|
abort: make(chan struct{}),
|
||||||
@ -102,8 +97,7 @@ func (p *processor) start() {
|
|||||||
// exec pulls a task out of the queue and starts a worker goroutine to
|
// exec pulls a task out of the queue and starts a worker goroutine to
|
||||||
// process the task.
|
// process the task.
|
||||||
func (p *processor) exec() {
|
func (p *processor) exec() {
|
||||||
// TODO(hibiken): sort the queues based on weight, but prevent starvation
|
msg, err := p.rdb.Dequeue(weightedQueues()...)
|
||||||
msg, err := p.rdb.Dequeue(base.HighPriorityQueue, base.DefaultQueue, base.LowPriorityQueue)
|
|
||||||
if err == rdb.ErrNoProcessableTask {
|
if err == rdb.ErrNoProcessableTask {
|
||||||
// queues are empty, this is a normal behavior.
|
// queues are empty, this is a normal behavior.
|
||||||
return
|
return
|
||||||
@ -206,3 +200,49 @@ func perform(h Handler, task *Task) (err error) {
|
|||||||
}()
|
}()
|
||||||
return h.ProcessTask(task)
|
return h.ProcessTask(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// weightedQueues returns a list of redis queue key to query
|
||||||
|
// order is roughly based on the weight, it's randomized to
|
||||||
|
// prevent starvation.
|
||||||
|
//
|
||||||
|
// For example, if weight distribution is
|
||||||
|
// high = 6, default = 3, low = 1
|
||||||
|
// then high priority tasks will get processed first 60% of the time,
|
||||||
|
// and default priority task will get processed first 30% of the time,
|
||||||
|
// and low priority task will get processed first 10% of the time
|
||||||
|
func weightedQueues() []string {
|
||||||
|
const numOfQueues = 3
|
||||||
|
const (
|
||||||
|
highWeight = 6
|
||||||
|
defaultWeight = 3
|
||||||
|
lowWeight = 1
|
||||||
|
)
|
||||||
|
queues := make([]string, 10)
|
||||||
|
for i := 0; i < highWeight; i++ {
|
||||||
|
queues[i] = base.HighPriorityQueue
|
||||||
|
}
|
||||||
|
for i := highWeight; i < highWeight+defaultWeight; i++ {
|
||||||
|
queues[i] = base.DefaultQueue
|
||||||
|
}
|
||||||
|
for i := highWeight + defaultWeight; i < highWeight+defaultWeight+lowWeight; i++ {
|
||||||
|
queues[i] = base.LowPriorityQueue
|
||||||
|
}
|
||||||
|
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
rnd.Shuffle(len(queues), func(i, j int) {
|
||||||
|
queues[i], queues[j] = queues[j], queues[i]
|
||||||
|
})
|
||||||
|
|
||||||
|
var unique []string
|
||||||
|
seen := make(map[string]struct{})
|
||||||
|
|
||||||
|
for _, q := range queues {
|
||||||
|
if _, ok := seen[q]; !ok {
|
||||||
|
unique = append(unique, q)
|
||||||
|
}
|
||||||
|
seen[q] = struct{}{}
|
||||||
|
if len(unique) == numOfQueues {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unique
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package asynq
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -61,7 +62,6 @@ func TestProcessorSuccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
p := newProcessor(rdbClient, 10, defaultDelayFunc)
|
p := newProcessor(rdbClient, 10, defaultDelayFunc)
|
||||||
p.handler = HandlerFunc(handler)
|
p.handler = HandlerFunc(handler)
|
||||||
p.dequeueTimeout = time.Second // short time out for test purpose
|
|
||||||
|
|
||||||
p.start()
|
p.start()
|
||||||
for _, msg := range tc.incoming {
|
for _, msg := range tc.incoming {
|
||||||
@ -145,7 +145,6 @@ func TestProcessorRetry(t *testing.T) {
|
|||||||
}
|
}
|
||||||
p := newProcessor(rdbClient, 10, delayFunc)
|
p := newProcessor(rdbClient, 10, delayFunc)
|
||||||
p.handler = HandlerFunc(handler)
|
p.handler = HandlerFunc(handler)
|
||||||
p.dequeueTimeout = time.Second // short time out for test purpose
|
|
||||||
|
|
||||||
p.start()
|
p.start()
|
||||||
for _, msg := range tc.incoming {
|
for _, msg := range tc.incoming {
|
||||||
@ -219,3 +218,24 @@ func TestPerform(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWeightedQueues(t *testing.T) {
|
||||||
|
want := []string{base.HighPriorityQueue, base.DefaultQueue, base.LowPriorityQueue}
|
||||||
|
|
||||||
|
got := weightedQueues()
|
||||||
|
|
||||||
|
sortOpt := cmp.Transformer("SortString", func(in []string) []string {
|
||||||
|
out := append([]string(nil), in...) // Copy input to avoid mutating it
|
||||||
|
sort.Strings(out)
|
||||||
|
return out
|
||||||
|
})
|
||||||
|
if diff := cmp.Diff(want, got, sortOpt); diff != "" {
|
||||||
|
t.Errorf("weightedQueues = %v, want %v; (-want,+got):\n%s", got, want, diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkWeightedQueues(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
weightedQueues()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user