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.
|
|
|
|
|
2019-11-30 12:49:18 +08:00
|
|
|
package asynq
|
|
|
|
|
|
|
|
import (
|
2020-02-11 23:06:52 +08:00
|
|
|
"context"
|
2020-05-02 21:55:44 +08:00
|
|
|
"fmt"
|
2020-04-27 22:19:14 +08:00
|
|
|
"syscall"
|
2019-11-30 12:49:18 +08:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2020-05-02 21:55:44 +08:00
|
|
|
"github.com/hibiken/asynq/internal/rdb"
|
|
|
|
"github.com/hibiken/asynq/internal/testbroker"
|
2022-03-19 22:16:55 +08:00
|
|
|
"github.com/hibiken/asynq/internal/testutil"
|
2019-11-30 12:49:18 +08:00
|
|
|
"go.uber.org/goleak"
|
|
|
|
)
|
|
|
|
|
2020-04-12 23:16:42 +08:00
|
|
|
func TestServer(t *testing.T) {
|
2019-11-30 12:49:18 +08:00
|
|
|
// https://github.com/go-redis/redis/issues/1029
|
2023-04-19 22:33:44 +08:00
|
|
|
ignoreOpt := goleak.IgnoreTopFunction("github.com/redis/go-redis/v9/internal/pool.(*ConnPool).reaper")
|
2022-05-09 01:17:22 +08:00
|
|
|
defer goleak.VerifyNone(t, ignoreOpt)
|
2019-11-30 12:49:18 +08:00
|
|
|
|
2020-08-29 21:54:08 +08:00
|
|
|
redisConnOpt := getRedisConnOpt(t)
|
|
|
|
c := NewClient(redisConnOpt)
|
2020-09-08 21:51:01 +08:00
|
|
|
defer c.Close()
|
2020-08-29 21:54:08 +08:00
|
|
|
srv := NewServer(redisConnOpt, Config{
|
2019-12-30 08:55:51 +08:00
|
|
|
Concurrency: 10,
|
2020-05-11 07:47:46 +08:00
|
|
|
LogLevel: testLogLevel,
|
2019-12-30 08:55:51 +08:00
|
|
|
})
|
2019-11-30 12:49:18 +08:00
|
|
|
|
|
|
|
// no-op handler
|
2020-02-11 23:06:52 +08:00
|
|
|
h := func(ctx context.Context, task *Task) error {
|
2019-11-30 12:49:18 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-13 02:00:45 +08:00
|
|
|
err := srv.Start(HandlerFunc(h))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2019-11-30 12:49:18 +08:00
|
|
|
|
2022-03-19 22:16:55 +08:00
|
|
|
_, err = c.Enqueue(NewTask("send_email", testutil.JSON(map[string]interface{}{"recipient_id": 123})))
|
2020-02-24 07:40:04 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("could not enqueue a task: %v", err)
|
|
|
|
}
|
2020-01-05 05:13:46 +08:00
|
|
|
|
2022-03-19 22:16:55 +08:00
|
|
|
_, err = c.Enqueue(NewTask("send_email", testutil.JSON(map[string]interface{}{"recipient_id": 456})), ProcessIn(1*time.Hour))
|
2020-02-24 07:40:04 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("could not enqueue a task: %v", err)
|
|
|
|
}
|
2019-11-30 12:49:18 +08:00
|
|
|
|
2021-03-23 21:20:54 +08:00
|
|
|
srv.Shutdown()
|
2019-11-30 12:49:18 +08:00
|
|
|
}
|
2020-01-08 13:17:01 +08:00
|
|
|
|
2020-04-27 22:19:14 +08:00
|
|
|
func TestServerRun(t *testing.T) {
|
|
|
|
// https://github.com/go-redis/redis/issues/1029
|
2023-04-19 22:33:44 +08:00
|
|
|
ignoreOpt := goleak.IgnoreTopFunction("github.com/redis/go-redis/v9/internal/pool.(*ConnPool).reaper")
|
2022-05-09 01:17:22 +08:00
|
|
|
defer goleak.VerifyNone(t, ignoreOpt)
|
2020-04-27 22:19:14 +08:00
|
|
|
|
2020-05-11 07:47:46 +08:00
|
|
|
srv := NewServer(RedisClientOpt{Addr: ":6379"}, Config{LogLevel: testLogLevel})
|
2020-04-27 22:19:14 +08:00
|
|
|
|
|
|
|
done := make(chan struct{})
|
|
|
|
// Make sure server exits when receiving TERM signal.
|
|
|
|
go func() {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
|
|
|
|
done <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
select {
|
|
|
|
case <-time.After(10 * time.Second):
|
2021-03-06 06:28:22 +08:00
|
|
|
panic("server did not stop after receiving TERM signal")
|
2020-04-27 22:19:14 +08:00
|
|
|
case <-done:
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
mux := NewServeMux()
|
|
|
|
if err := srv.Run(mux); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 21:20:54 +08:00
|
|
|
func TestServerErrServerClosed(t *testing.T) {
|
2020-05-11 07:47:46 +08:00
|
|
|
srv := NewServer(RedisClientOpt{Addr: ":6379"}, Config{LogLevel: testLogLevel})
|
2020-04-15 00:01:22 +08:00
|
|
|
handler := NewServeMux()
|
|
|
|
if err := srv.Start(handler); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2021-03-23 21:20:54 +08:00
|
|
|
srv.Shutdown()
|
2020-04-15 00:01:22 +08:00
|
|
|
err := srv.Start(handler)
|
2021-03-23 21:20:54 +08:00
|
|
|
if err != ErrServerClosed {
|
|
|
|
t.Errorf("Restarting server: (*Server).Start(handler) = %v, want ErrServerClosed error", err)
|
2020-04-15 00:01:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestServerErrNilHandler(t *testing.T) {
|
2020-05-11 07:47:46 +08:00
|
|
|
srv := NewServer(RedisClientOpt{Addr: ":6379"}, Config{LogLevel: testLogLevel})
|
2020-04-15 00:01:22 +08:00
|
|
|
err := srv.Start(nil)
|
|
|
|
if err == nil {
|
|
|
|
t.Error("Starting server with nil handler: (*Server).Start(nil) did not return error")
|
2021-03-23 21:20:54 +08:00
|
|
|
srv.Shutdown()
|
2020-04-15 00:01:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestServerErrServerRunning(t *testing.T) {
|
2020-05-11 07:47:46 +08:00
|
|
|
srv := NewServer(RedisClientOpt{Addr: ":6379"}, Config{LogLevel: testLogLevel})
|
2020-04-15 00:01:22 +08:00
|
|
|
handler := NewServeMux()
|
|
|
|
if err := srv.Start(handler); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
err := srv.Start(handler)
|
|
|
|
if err == nil {
|
|
|
|
t.Error("Calling (*Server).Start(handler) on already running server did not return error")
|
|
|
|
}
|
2021-03-23 21:20:54 +08:00
|
|
|
srv.Shutdown()
|
2020-04-15 00:01:22 +08:00
|
|
|
}
|
2020-05-02 21:55:44 +08:00
|
|
|
|
|
|
|
func TestServerWithRedisDown(t *testing.T) {
|
|
|
|
// Make sure that server does not panic and exit if redis is down.
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
t.Errorf("panic occurred: %v", r)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
r := rdb.NewRDB(setup(t))
|
|
|
|
testBroker := testbroker.NewTestBroker(r)
|
2020-05-11 07:47:46 +08:00
|
|
|
srv := NewServer(RedisClientOpt{Addr: ":6379"}, Config{LogLevel: testLogLevel})
|
2020-05-02 21:55:44 +08:00
|
|
|
srv.broker = testBroker
|
2020-09-27 08:33:29 +08:00
|
|
|
srv.forwarder.broker = testBroker
|
2020-05-02 21:55:44 +08:00
|
|
|
srv.heartbeater.broker = testBroker
|
|
|
|
srv.processor.broker = testBroker
|
|
|
|
srv.subscriber.broker = testBroker
|
|
|
|
testBroker.Sleep()
|
|
|
|
|
|
|
|
// no-op handler
|
|
|
|
h := func(ctx context.Context, task *Task) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err := srv.Start(HandlerFunc(h))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
time.Sleep(3 * time.Second)
|
|
|
|
|
2021-03-23 21:20:54 +08:00
|
|
|
srv.Shutdown()
|
2020-05-02 21:55:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestServerWithFlakyBroker(t *testing.T) {
|
|
|
|
// Make sure that server does not panic and exit if redis is down.
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
t.Errorf("panic occurred: %v", r)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
r := rdb.NewRDB(setup(t))
|
|
|
|
testBroker := testbroker.NewTestBroker(r)
|
2020-08-29 21:54:08 +08:00
|
|
|
redisConnOpt := getRedisConnOpt(t)
|
|
|
|
srv := NewServer(redisConnOpt, Config{LogLevel: testLogLevel})
|
2020-05-02 21:55:44 +08:00
|
|
|
srv.broker = testBroker
|
2020-09-27 08:33:29 +08:00
|
|
|
srv.forwarder.broker = testBroker
|
2020-05-02 21:55:44 +08:00
|
|
|
srv.heartbeater.broker = testBroker
|
|
|
|
srv.processor.broker = testBroker
|
|
|
|
srv.subscriber.broker = testBroker
|
|
|
|
|
2020-08-29 21:54:08 +08:00
|
|
|
c := NewClient(redisConnOpt)
|
2020-05-02 21:55:44 +08:00
|
|
|
|
|
|
|
h := func(ctx context.Context, task *Task) error {
|
|
|
|
// force task retry.
|
2021-03-21 04:42:13 +08:00
|
|
|
if task.Type() == "bad_task" {
|
|
|
|
return fmt.Errorf("could not process %q", task.Type())
|
2020-05-02 21:55:44 +08:00
|
|
|
}
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err := srv.Start(HandlerFunc(h))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
2020-07-03 20:49:52 +08:00
|
|
|
_, err := c.Enqueue(NewTask("enqueued", nil), MaxRetry(i))
|
2020-05-02 21:55:44 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2020-07-03 20:49:52 +08:00
|
|
|
_, err = c.Enqueue(NewTask("bad_task", nil))
|
2020-05-02 21:55:44 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2020-09-05 21:29:08 +08:00
|
|
|
_, err = c.Enqueue(NewTask("scheduled", nil), ProcessIn(time.Duration(i)*time.Second))
|
2020-05-02 21:55:44 +08:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// simulate redis going down.
|
|
|
|
testBroker.Sleep()
|
|
|
|
|
|
|
|
time.Sleep(3 * time.Second)
|
|
|
|
|
|
|
|
// simulate redis comes back online.
|
|
|
|
testBroker.Wakeup()
|
|
|
|
|
|
|
|
time.Sleep(3 * time.Second)
|
|
|
|
|
2021-03-23 21:20:54 +08:00
|
|
|
srv.Shutdown()
|
2020-05-02 21:55:44 +08:00
|
|
|
}
|
2020-05-10 01:59:50 +08:00
|
|
|
|
|
|
|
func TestLogLevel(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
flagVal string
|
|
|
|
want LogLevel
|
|
|
|
wantStr string
|
|
|
|
}{
|
|
|
|
{"debug", DebugLevel, "debug"},
|
|
|
|
{"Info", InfoLevel, "info"},
|
|
|
|
{"WARN", WarnLevel, "warn"},
|
|
|
|
{"warning", WarnLevel, "warn"},
|
|
|
|
{"Error", ErrorLevel, "error"},
|
|
|
|
{"fatal", FatalLevel, "fatal"},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
level := new(LogLevel)
|
|
|
|
if err := level.Set(tc.flagVal); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if *level != tc.want {
|
|
|
|
t.Errorf("Set(%q): got %v, want %v", tc.flagVal, level, &tc.want)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if got := level.String(); got != tc.wantStr {
|
|
|
|
t.Errorf("String() returned %q, want %q", got, tc.wantStr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|