2020-01-03 10:13:16 +08:00
|
|
|
// Copyright 2020 Kentaro Hibino. All rights reserved.
|
|
|
|
// Use of this source code is governed by a MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
2020-01-01 04:36:46 +08:00
|
|
|
package asynq
|
|
|
|
|
|
|
|
import (
|
2020-02-11 23:06:52 +08:00
|
|
|
"context"
|
2020-01-01 04:36:46 +08:00
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2020-02-09 03:06:14 +08:00
|
|
|
// Simple E2E Benchmark testing with no scheduled tasks and retries.
|
2020-01-01 04:36:46 +08:00
|
|
|
func BenchmarkEndToEndSimple(b *testing.B) {
|
|
|
|
const count = 100000
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
|
|
b.StopTimer() // begin setup
|
2020-01-19 02:17:39 +08:00
|
|
|
setup(b)
|
2020-08-29 21:54:08 +08:00
|
|
|
redis := getRedisConnOpt(b)
|
2020-01-19 02:17:39 +08:00
|
|
|
client := NewClient(redis)
|
2020-04-12 23:16:42 +08:00
|
|
|
srv := NewServer(redis, Config{
|
2020-01-01 04:36:46 +08:00
|
|
|
Concurrency: 10,
|
|
|
|
RetryDelayFunc: func(n int, err error, t *Task) time.Duration {
|
|
|
|
return time.Second
|
|
|
|
},
|
2020-06-08 04:04:27 +08:00
|
|
|
LogLevel: testLogLevel,
|
2020-01-01 04:36:46 +08:00
|
|
|
})
|
|
|
|
// Create a bunch of tasks
|
|
|
|
for i := 0; i < count; i++ {
|
2020-01-05 05:13:46 +08:00
|
|
|
t := NewTask(fmt.Sprintf("task%d", i), map[string]interface{}{"data": i})
|
2020-07-03 20:49:52 +08:00
|
|
|
if _, err := client.Enqueue(t); err != nil {
|
2020-02-24 07:40:04 +08:00
|
|
|
b.Fatalf("could not enqueue a task: %v", err)
|
|
|
|
}
|
2020-01-01 04:36:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(count)
|
2020-02-11 23:06:52 +08:00
|
|
|
handler := func(ctx context.Context, t *Task) error {
|
2020-01-01 04:36:46 +08:00
|
|
|
wg.Done()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
b.StartTimer() // end setup
|
|
|
|
|
2020-04-13 02:00:45 +08:00
|
|
|
srv.Start(HandlerFunc(handler))
|
2020-01-01 04:36:46 +08:00
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
b.StopTimer() // begin teardown
|
2020-04-13 02:00:45 +08:00
|
|
|
srv.Stop()
|
2020-01-01 04:36:46 +08:00
|
|
|
b.StartTimer() // end teardown
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// E2E benchmark with scheduled tasks and retries.
|
|
|
|
func BenchmarkEndToEnd(b *testing.B) {
|
|
|
|
const count = 100000
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
|
|
b.StopTimer() // begin setup
|
2020-01-19 02:17:39 +08:00
|
|
|
setup(b)
|
2020-08-29 21:54:08 +08:00
|
|
|
redis := getRedisConnOpt(b)
|
2020-01-19 02:17:39 +08:00
|
|
|
client := NewClient(redis)
|
2020-04-12 23:16:42 +08:00
|
|
|
srv := NewServer(redis, Config{
|
2020-01-01 04:36:46 +08:00
|
|
|
Concurrency: 10,
|
|
|
|
RetryDelayFunc: func(n int, err error, t *Task) time.Duration {
|
|
|
|
return time.Second
|
|
|
|
},
|
2020-06-08 04:04:27 +08:00
|
|
|
LogLevel: testLogLevel,
|
2020-01-01 04:36:46 +08:00
|
|
|
})
|
|
|
|
// Create a bunch of tasks
|
|
|
|
for i := 0; i < count; i++ {
|
2020-01-05 05:13:46 +08:00
|
|
|
t := NewTask(fmt.Sprintf("task%d", i), map[string]interface{}{"data": i})
|
2020-07-03 20:49:52 +08:00
|
|
|
if _, err := client.Enqueue(t); err != nil {
|
2020-02-24 07:40:04 +08:00
|
|
|
b.Fatalf("could not enqueue a task: %v", err)
|
|
|
|
}
|
2020-01-01 04:36:46 +08:00
|
|
|
}
|
|
|
|
for i := 0; i < count; i++ {
|
2020-01-05 05:13:46 +08:00
|
|
|
t := NewTask(fmt.Sprintf("scheduled%d", i), map[string]interface{}{"data": i})
|
2020-09-05 21:29:08 +08:00
|
|
|
if _, err := client.Enqueue(t, ProcessIn(1*time.Second)); err != nil {
|
2020-02-24 07:40:04 +08:00
|
|
|
b.Fatalf("could not enqueue a task: %v", err)
|
|
|
|
}
|
2020-01-01 04:36:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(count * 2)
|
2020-02-11 23:06:52 +08:00
|
|
|
handler := func(ctx context.Context, t *Task) error {
|
2020-06-08 04:04:27 +08:00
|
|
|
n, err := t.Payload.GetInt("data")
|
|
|
|
if err != nil {
|
|
|
|
b.Logf("internal error: %v", err)
|
|
|
|
}
|
|
|
|
retried, ok := GetRetryCount(ctx)
|
|
|
|
if !ok {
|
|
|
|
b.Logf("internal error: %v", err)
|
|
|
|
}
|
|
|
|
// Fail 1% of tasks for the first attempt.
|
|
|
|
if retried == 0 && n%100 == 0 {
|
2020-01-01 04:36:46 +08:00
|
|
|
return fmt.Errorf(":(")
|
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
b.StartTimer() // end setup
|
|
|
|
|
2020-04-13 02:00:45 +08:00
|
|
|
srv.Start(HandlerFunc(handler))
|
2020-01-01 04:36:46 +08:00
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
b.StopTimer() // begin teardown
|
2020-04-13 02:00:45 +08:00
|
|
|
srv.Stop()
|
2020-01-01 04:36:46 +08:00
|
|
|
b.StartTimer() // end teardown
|
|
|
|
}
|
|
|
|
}
|
2020-02-09 03:06:14 +08:00
|
|
|
|
|
|
|
// Simple E2E Benchmark testing with no scheduled tasks and retries with multiple queues.
|
|
|
|
func BenchmarkEndToEndMultipleQueues(b *testing.B) {
|
|
|
|
// number of tasks to create for each queue
|
|
|
|
const (
|
|
|
|
highCount = 20000
|
|
|
|
defaultCount = 20000
|
|
|
|
lowCount = 20000
|
|
|
|
)
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
|
|
b.StopTimer() // begin setup
|
|
|
|
setup(b)
|
2020-08-29 21:54:08 +08:00
|
|
|
redis := getRedisConnOpt(b)
|
2020-02-09 03:06:14 +08:00
|
|
|
client := NewClient(redis)
|
2020-04-12 23:16:42 +08:00
|
|
|
srv := NewServer(redis, Config{
|
2020-02-09 03:06:14 +08:00
|
|
|
Concurrency: 10,
|
2020-02-13 14:23:25 +08:00
|
|
|
Queues: map[string]int{
|
2020-02-09 03:06:14 +08:00
|
|
|
"high": 6,
|
|
|
|
"default": 3,
|
|
|
|
"low": 1,
|
|
|
|
},
|
2020-06-08 04:04:27 +08:00
|
|
|
LogLevel: testLogLevel,
|
2020-02-09 03:06:14 +08:00
|
|
|
})
|
|
|
|
// Create a bunch of tasks
|
|
|
|
for i := 0; i < highCount; i++ {
|
|
|
|
t := NewTask(fmt.Sprintf("task%d", i), map[string]interface{}{"data": i})
|
2020-07-03 20:49:52 +08:00
|
|
|
if _, err := client.Enqueue(t, Queue("high")); err != nil {
|
2020-02-24 07:40:04 +08:00
|
|
|
b.Fatalf("could not enqueue a task: %v", err)
|
|
|
|
}
|
2020-02-09 03:06:14 +08:00
|
|
|
}
|
|
|
|
for i := 0; i < defaultCount; i++ {
|
|
|
|
t := NewTask(fmt.Sprintf("task%d", i), map[string]interface{}{"data": i})
|
2020-07-03 20:49:52 +08:00
|
|
|
if _, err := client.Enqueue(t); err != nil {
|
2020-02-24 07:40:04 +08:00
|
|
|
b.Fatalf("could not enqueue a task: %v", err)
|
|
|
|
}
|
2020-02-09 03:06:14 +08:00
|
|
|
}
|
|
|
|
for i := 0; i < lowCount; i++ {
|
|
|
|
t := NewTask(fmt.Sprintf("task%d", i), map[string]interface{}{"data": i})
|
2020-07-03 20:49:52 +08:00
|
|
|
if _, err := client.Enqueue(t, Queue("low")); err != nil {
|
2020-02-24 07:40:04 +08:00
|
|
|
b.Fatalf("could not enqueue a task: %v", err)
|
|
|
|
}
|
2020-02-09 03:06:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(highCount + defaultCount + lowCount)
|
2020-02-11 23:06:52 +08:00
|
|
|
handler := func(ctx context.Context, t *Task) error {
|
2020-02-09 03:06:14 +08:00
|
|
|
wg.Done()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
b.StartTimer() // end setup
|
|
|
|
|
2020-04-13 02:00:45 +08:00
|
|
|
srv.Start(HandlerFunc(handler))
|
2020-02-09 03:06:14 +08:00
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
b.StopTimer() // begin teardown
|
2020-04-13 02:00:45 +08:00
|
|
|
srv.Stop()
|
2020-02-09 03:06:14 +08:00
|
|
|
b.StartTimer() // end teardown
|
|
|
|
}
|
|
|
|
}
|
2020-06-08 20:51:36 +08:00
|
|
|
|
|
|
|
// E2E benchmark to check client enqueue operation performs correctly,
|
|
|
|
// while server is busy processing tasks.
|
|
|
|
func BenchmarkClientWhileServerRunning(b *testing.B) {
|
|
|
|
const count = 10000
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
|
|
b.StopTimer() // begin setup
|
|
|
|
setup(b)
|
2020-08-29 21:54:08 +08:00
|
|
|
redis := getRedisConnOpt(b)
|
2020-06-08 20:51:36 +08:00
|
|
|
client := NewClient(redis)
|
|
|
|
srv := NewServer(redis, Config{
|
|
|
|
Concurrency: 10,
|
|
|
|
RetryDelayFunc: func(n int, err error, t *Task) time.Duration {
|
|
|
|
return time.Second
|
|
|
|
},
|
|
|
|
LogLevel: testLogLevel,
|
|
|
|
})
|
|
|
|
// Enqueue 10,000 tasks.
|
|
|
|
for i := 0; i < count; i++ {
|
|
|
|
t := NewTask(fmt.Sprintf("task%d", i), map[string]interface{}{"data": i})
|
2020-07-03 20:49:52 +08:00
|
|
|
if _, err := client.Enqueue(t); err != nil {
|
2020-06-08 20:51:36 +08:00
|
|
|
b.Fatalf("could not enqueue a task: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Schedule 10,000 tasks.
|
|
|
|
for i := 0; i < count; i++ {
|
|
|
|
t := NewTask(fmt.Sprintf("scheduled%d", i), map[string]interface{}{"data": i})
|
2020-09-05 21:29:08 +08:00
|
|
|
if _, err := client.Enqueue(t, ProcessIn(1*time.Second)); err != nil {
|
2020-06-08 20:51:36 +08:00
|
|
|
b.Fatalf("could not enqueue a task: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handler := func(ctx context.Context, t *Task) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
srv.Start(HandlerFunc(handler))
|
|
|
|
|
|
|
|
b.StartTimer() // end setup
|
|
|
|
|
|
|
|
b.Log("Starting enqueueing")
|
|
|
|
enqueued := 0
|
|
|
|
for enqueued < 100000 {
|
|
|
|
t := NewTask(fmt.Sprintf("enqueued%d", enqueued), map[string]interface{}{"data": enqueued})
|
2020-07-03 20:49:52 +08:00
|
|
|
if _, err := client.Enqueue(t); err != nil {
|
2020-06-08 20:51:36 +08:00
|
|
|
b.Logf("could not enqueue task %d: %v", enqueued, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
enqueued++
|
|
|
|
}
|
|
|
|
b.Logf("Finished enqueueing %d tasks", enqueued)
|
|
|
|
|
|
|
|
b.StopTimer() // begin teardown
|
|
|
|
srv.Stop()
|
|
|
|
b.StartTimer() // end teardown
|
|
|
|
}
|
|
|
|
}
|