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-12-04 13:01:26 +08:00
package rdb
2019-11-20 23:01:24 +08:00
import (
2020-02-21 23:18:22 +08:00
"encoding/json"
2020-08-29 21:54:08 +08:00
"flag"
2019-11-27 23:16:16 +08:00
"fmt"
2020-08-29 21:54:08 +08:00
"strings"
2020-02-23 06:30:24 +08:00
"sync"
2019-11-20 23:01:24 +08:00
"testing"
"time"
2019-12-29 12:12:14 +08:00
"github.com/go-redis/redis/v7"
2019-11-20 23:01:24 +08:00
"github.com/google/go-cmp/cmp"
2020-02-02 14:22:48 +08:00
"github.com/google/go-cmp/cmp/cmpopts"
2020-07-02 21:21:20 +08:00
"github.com/google/uuid"
2019-12-29 12:12:14 +08:00
h "github.com/hibiken/asynq/internal/asynqtest"
2019-12-22 23:15:45 +08:00
"github.com/hibiken/asynq/internal/base"
2019-11-20 23:01:24 +08:00
)
2020-08-29 21:54:08 +08:00
// variables used for package testing.
var (
redisAddr string
redisDB int
useRedisCluster bool
redisClusterAddrs string // comma-separated list of host:port
)
func init ( ) {
flag . StringVar ( & redisAddr , "redis_addr" , "localhost:6379" , "redis address to use in testing" )
flag . IntVar ( & redisDB , "redis_db" , 14 , "redis db number to use in testing" )
flag . BoolVar ( & useRedisCluster , "redis_cluster" , false , "use redis cluster as a broker in testing" )
flag . StringVar ( & redisClusterAddrs , "redis_cluster_addrs" , "localhost:7000,localhost:7001,localhost:7002" , "comma separated list of redis server addresses" )
}
func setup ( t * testing . T ) ( r * RDB ) {
2019-12-29 12:12:14 +08:00
t . Helper ( )
2020-08-29 21:54:08 +08:00
if useRedisCluster {
addrs := strings . Split ( redisClusterAddrs , "," )
if len ( addrs ) == 0 {
t . Fatal ( "No redis cluster addresses provided. Please set addresses using --redis_cluster_addrs flag." )
}
r = NewRDB ( redis . NewClusterClient ( & redis . ClusterOptions {
Addrs : addrs ,
} ) )
} else {
r = NewRDB ( redis . NewClient ( & redis . Options {
Addr : redisAddr ,
DB : redisDB ,
} ) )
}
2019-12-29 12:12:14 +08:00
// Start each test with a clean slate.
h . FlushDB ( t , r . client )
return r
}
2019-11-26 11:58:24 +08:00
func TestEnqueue ( t * testing . T ) {
2019-11-26 10:55:17 +08:00
r := setup ( t )
2020-01-09 22:21:43 +08:00
t1 := h . NewTaskMessage ( "send_email" , map [ string ] interface { } { "to" : "exampleuser@gmail.com" , "from" : "noreply@example.com" } )
2020-08-07 21:31:02 +08:00
t2 := h . NewTaskMessageWithQueue ( "generate_csv" , map [ string ] interface { } { } , "csv" )
t3 := h . NewTaskMessageWithQueue ( "sync" , nil , "low" )
2020-01-09 22:21:43 +08:00
2019-11-26 10:55:17 +08:00
tests := [ ] struct {
2019-12-22 23:15:45 +08:00
msg * base . TaskMessage
2019-11-26 10:55:17 +08:00
} {
2020-01-09 22:21:43 +08:00
{ t1 } ,
{ t2 } ,
{ t3 } ,
2019-11-20 23:01:24 +08:00
}
2019-11-26 10:55:17 +08:00
for _ , tc := range tests {
2019-12-29 12:12:14 +08:00
h . FlushDB ( t , r . client ) // clean up db before each test case.
2019-12-13 11:49:41 +08:00
2019-12-04 13:01:26 +08:00
err := r . Enqueue ( tc . msg )
2019-11-26 10:55:17 +08:00
if err != nil {
2020-01-09 22:21:43 +08:00
t . Errorf ( "(*RDB).Enqueue(msg) = %v, want nil" , err )
2019-11-26 10:55:17 +08:00
}
2020-01-09 22:21:43 +08:00
gotEnqueued := h . GetEnqueuedMessages ( t , r . client , tc . msg . Queue )
2019-12-29 12:12:14 +08:00
if len ( gotEnqueued ) != 1 {
2020-08-07 21:31:02 +08:00
t . Errorf ( "%q has length %d, want 1" , base . QueueKey ( tc . msg . Queue ) , len ( gotEnqueued ) )
2019-11-26 10:55:17 +08:00
continue
}
2019-12-29 12:12:14 +08:00
if diff := cmp . Diff ( tc . msg , gotEnqueued [ 0 ] ) ; diff != "" {
2019-11-26 10:55:17 +08:00
t . Errorf ( "persisted data differed from the original input (-want, +got)\n%s" , diff )
}
2020-08-07 21:31:02 +08:00
if ! r . client . SIsMember ( base . AllQueues , tc . msg . Queue ) . Val ( ) {
t . Errorf ( "%q is not a member of SET %q" , tc . msg . Queue , base . AllQueues )
2020-01-09 22:21:43 +08:00
}
2019-11-20 23:01:24 +08:00
}
}
2020-03-18 21:49:39 +08:00
func TestEnqueueUnique ( t * testing . T ) {
r := setup ( t )
m1 := base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-03-18 21:49:39 +08:00
Type : "email" ,
Payload : map [ string ] interface { } { "user_id" : 123 } ,
Queue : base . DefaultQueueName ,
2020-08-07 21:31:02 +08:00
UniqueKey : base . UniqueKey ( base . DefaultQueueName , "email" , map [ string ] interface { } { "user_id" : 123 } ) ,
2020-03-18 21:49:39 +08:00
}
tests := [ ] struct {
msg * base . TaskMessage
ttl time . Duration // uniqueness ttl
} {
{ & m1 , time . Minute } ,
}
for _ , tc := range tests {
h . FlushDB ( t , r . client ) // clean up db before each test case.
err := r . EnqueueUnique ( tc . msg , tc . ttl )
if err != nil {
t . Errorf ( "First message: (*RDB).EnqueueUnique(%v, %v) = %v, want nil" ,
tc . msg , tc . ttl , err )
continue
}
got := r . EnqueueUnique ( tc . msg , tc . ttl )
if got != ErrDuplicateTask {
t . Errorf ( "Second message: (*RDB).EnqueueUnique(%v, %v) = %v, want %v" ,
tc . msg , tc . ttl , got , ErrDuplicateTask )
continue
}
gotTTL := r . client . TTL ( tc . msg . UniqueKey ) . Val ( )
if ! cmp . Equal ( tc . ttl . Seconds ( ) , gotTTL . Seconds ( ) , cmpopts . EquateApprox ( 0 , 1 ) ) {
t . Errorf ( "TTL %q = %v, want %v" , tc . msg . UniqueKey , gotTTL , tc . ttl )
continue
}
2020-08-07 21:31:02 +08:00
if ! r . client . SIsMember ( base . AllQueues , tc . msg . Queue ) . Val ( ) {
t . Errorf ( "%q is not a member of SET %q" , tc . msg . Queue , base . AllQueues )
}
2020-03-18 21:49:39 +08:00
}
}
2019-11-26 12:57:53 +08:00
func TestDequeue ( t * testing . T ) {
2019-11-26 10:55:17 +08:00
r := setup ( t )
2020-06-17 21:46:54 +08:00
now := time . Now ( )
t1 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-17 21:46:54 +08:00
Type : "send_email" ,
Payload : map [ string ] interface { } { "subject" : "hello!" } ,
Timeout : 1800 ,
Deadline : 0 ,
}
2020-06-22 23:33:58 +08:00
t1Deadline := now . Unix ( ) + t1 . Timeout
2020-06-17 21:46:54 +08:00
t2 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-17 21:46:54 +08:00
Type : "export_csv" ,
Payload : nil ,
Timeout : 0 ,
Deadline : 1593021600 ,
}
t2Deadline := t2 . Deadline
t3 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-17 21:46:54 +08:00
Type : "reindex" ,
Payload : nil ,
2020-06-22 23:33:58 +08:00
Timeout : int64 ( ( 5 * time . Minute ) . Seconds ( ) ) ,
Deadline : time . Now ( ) . Add ( 10 * time . Minute ) . Unix ( ) ,
2020-06-17 21:46:54 +08:00
}
2020-06-22 23:33:58 +08:00
t3Deadline := now . Unix ( ) + t3 . Timeout // use whichever is earliest
2020-01-07 22:28:34 +08:00
2019-11-26 12:57:53 +08:00
tests := [ ] struct {
2020-01-07 22:28:34 +08:00
enqueued map [ string ] [ ] * base . TaskMessage
args [ ] string // list of queues to query
2020-06-18 20:45:10 +08:00
wantMsg * base . TaskMessage
2020-06-19 02:25:33 +08:00
wantDeadline time . Time
2019-12-28 12:37:15 +08:00
err error
2020-01-07 22:28:34 +08:00
wantEnqueued map [ string ] [ ] * base . TaskMessage
2020-08-08 21:04:16 +08:00
wantInProgress map [ string ] [ ] * base . TaskMessage
wantDeadlines map [ string ] [ ] base . Z
2019-11-26 12:57:53 +08:00
} {
2019-12-28 12:37:15 +08:00
{
2020-01-07 22:28:34 +08:00
enqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
} ,
2020-06-18 20:45:10 +08:00
args : [ ] string { "default" } ,
wantMsg : t1 ,
2020-06-23 21:34:59 +08:00
wantDeadline : time . Unix ( t1Deadline , 0 ) ,
2020-06-18 20:45:10 +08:00
err : nil ,
2020-01-07 22:28:34 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
2020-08-08 21:04:16 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : t1Deadline } } ,
2020-06-17 21:46:54 +08:00
} ,
2019-12-28 12:37:15 +08:00
} ,
{
2020-01-07 22:28:34 +08:00
enqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
2020-06-18 20:45:10 +08:00
args : [ ] string { "default" } ,
wantMsg : nil ,
2020-06-19 02:25:33 +08:00
wantDeadline : time . Time { } ,
2020-06-18 20:45:10 +08:00
err : ErrNoProcessableTask ,
2020-01-07 22:28:34 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
2020-08-08 21:04:16 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { } ,
} ,
2020-01-07 22:28:34 +08:00
} ,
{
enqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"critical" : { t2 } ,
"low" : { t3 } ,
} ,
2020-06-18 20:45:10 +08:00
args : [ ] string { "critical" , "default" , "low" } ,
wantMsg : t2 ,
2020-06-23 21:34:59 +08:00
wantDeadline : time . Unix ( t2Deadline , 0 ) ,
2020-06-18 20:45:10 +08:00
err : nil ,
2020-01-07 22:28:34 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"critical" : { } ,
"low" : { t3 } ,
} ,
2020-08-08 21:04:16 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { t2 } ,
"low" : { } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { } ,
"critical" : { { Message : t2 , Score : t2Deadline } } ,
"low" : { } ,
2020-06-17 21:46:54 +08:00
} ,
2020-01-07 22:28:34 +08:00
} ,
{
enqueued : map [ string ] [ ] * base . TaskMessage {
2020-06-17 21:46:54 +08:00
"default" : { t3 } ,
2020-01-07 22:28:34 +08:00
"critical" : { } ,
2020-06-17 21:46:54 +08:00
"low" : { t2 , t1 } ,
2020-01-07 22:28:34 +08:00
} ,
2020-06-18 20:45:10 +08:00
args : [ ] string { "critical" , "default" , "low" } ,
wantMsg : t3 ,
2020-06-23 21:34:59 +08:00
wantDeadline : time . Unix ( t3Deadline , 0 ) ,
2020-06-18 20:45:10 +08:00
err : nil ,
2020-01-07 22:28:34 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { } ,
2020-06-17 21:46:54 +08:00
"low" : { t2 , t1 } ,
} ,
2020-08-08 21:04:16 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t3 } ,
"critical" : { } ,
"low" : { } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t3 , Score : t3Deadline } } ,
"critical" : { } ,
"low" : { } ,
2020-01-07 22:28:34 +08:00
} ,
} ,
{
enqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { } ,
"low" : { } ,
} ,
2020-06-18 20:45:10 +08:00
args : [ ] string { "critical" , "default" , "low" } ,
wantMsg : nil ,
2020-06-19 02:25:33 +08:00
wantDeadline : time . Time { } ,
2020-06-18 20:45:10 +08:00
err : ErrNoProcessableTask ,
2020-01-07 22:28:34 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { } ,
"low" : { } ,
} ,
2020-08-08 21:04:16 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { } ,
"low" : { } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { } ,
"critical" : { } ,
"low" : { } ,
} ,
2019-12-28 12:37:15 +08:00
} ,
2019-11-22 13:45:27 +08:00
}
2019-11-20 23:01:24 +08:00
2019-11-26 12:57:53 +08:00
for _ , tc := range tests {
2019-12-29 12:12:14 +08:00
h . FlushDB ( t , r . client ) // clean up db before each test case
2020-08-11 12:49:12 +08:00
h . SeedAllEnqueuedQueues ( t , r . client , tc . enqueued )
2019-12-13 11:49:41 +08:00
2020-06-18 20:45:10 +08:00
gotMsg , gotDeadline , err := r . Dequeue ( tc . args ... )
if err != tc . err {
t . Errorf ( "(*RDB).Dequeue(%v) returned error %v; want %v" ,
tc . args , err , tc . err )
continue
}
if ! cmp . Equal ( gotMsg , tc . wantMsg ) || err != tc . err {
t . Errorf ( "(*RDB).Dequeue(%v) returned message %v; want %v" ,
tc . args , gotMsg , tc . wantMsg )
continue
}
2020-08-29 21:54:08 +08:00
if ! cmp . Equal ( gotDeadline , tc . wantDeadline , cmpopts . EquateApproxTime ( 1 * time . Second ) ) {
2020-06-18 20:45:10 +08:00
t . Errorf ( "(*RDB).Dequeue(%v) returned deadline %v; want %v" ,
tc . args , gotDeadline , tc . wantDeadline )
2020-06-04 20:37:17 +08:00
continue
}
for queue , want := range tc . wantEnqueued {
gotEnqueued := h . GetEnqueuedMessages ( t , r . client , queue )
if diff := cmp . Diff ( want , gotEnqueued , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q: (-want,+got):\n%s" , base . QueueKey ( queue ) , diff )
}
}
2020-08-08 21:04:16 +08:00
for queue , want := range tc . wantInProgress {
gotInProgress := h . GetInProgressMessages ( t , r . client , queue )
if diff := cmp . Diff ( want , gotInProgress , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q: (-want,+got):\n%s" , base . InProgressKey ( queue ) , diff )
}
2020-06-04 20:37:17 +08:00
}
2020-08-08 21:04:16 +08:00
for queue , want := range tc . wantDeadlines {
gotDeadlines := h . GetDeadlinesEntries ( t , r . client , queue )
if diff := cmp . Diff ( want , gotDeadlines , h . SortZSetEntryOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q: (-want,+got):\n%s" , base . DeadlinesKey ( queue ) , diff )
}
2020-06-17 21:46:54 +08:00
}
2020-06-04 20:37:17 +08:00
}
}
func TestDequeueIgnoresPausedQueues ( t * testing . T ) {
r := setup ( t )
2020-08-13 21:54:32 +08:00
t1 := & base . TaskMessage {
ID : uuid . New ( ) ,
Type : "send_email" ,
Payload : map [ string ] interface { } { "subject" : "hello!" } ,
Queue : "default" ,
Timeout : 1800 ,
Deadline : 0 ,
}
t2 := & base . TaskMessage {
ID : uuid . New ( ) ,
Type : "export_csv" ,
Payload : nil ,
Queue : "critical" ,
Timeout : 1800 ,
Deadline : 0 ,
}
2020-06-04 20:37:17 +08:00
tests := [ ] struct {
paused [ ] string // list of paused queues
enqueued map [ string ] [ ] * base . TaskMessage
args [ ] string // list of queues to query
2020-06-18 20:45:10 +08:00
wantMsg * base . TaskMessage
2020-06-04 20:37:17 +08:00
err error
wantEnqueued map [ string ] [ ] * base . TaskMessage
2020-08-08 21:04:16 +08:00
wantInProgress map [ string ] [ ] * base . TaskMessage
2020-06-04 20:37:17 +08:00
} {
{
paused : [ ] string { "default" } ,
enqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"critical" : { t2 } ,
} ,
2020-06-18 20:45:10 +08:00
args : [ ] string { "default" , "critical" } ,
wantMsg : t2 ,
err : nil ,
2020-06-04 20:37:17 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"critical" : { } ,
} ,
2020-08-08 21:04:16 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { t2 } ,
} ,
2020-06-04 20:37:17 +08:00
} ,
{
paused : [ ] string { "default" } ,
enqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
} ,
2020-06-18 20:45:10 +08:00
args : [ ] string { "default" } ,
wantMsg : nil ,
err : ErrNoProcessableTask ,
2020-06-04 20:37:17 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
} ,
2020-08-08 21:04:16 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
2020-06-04 20:37:17 +08:00
} ,
{
paused : [ ] string { "critical" , "default" } ,
enqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"critical" : { t2 } ,
} ,
2020-06-18 20:45:10 +08:00
args : [ ] string { "default" , "critical" } ,
wantMsg : nil ,
err : ErrNoProcessableTask ,
2020-06-04 20:37:17 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"critical" : { t2 } ,
} ,
2020-08-08 21:04:16 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { } ,
} ,
2020-06-04 20:37:17 +08:00
} ,
}
for _ , tc := range tests {
h . FlushDB ( t , r . client ) // clean up db before each test case
for _ , qname := range tc . paused {
if err := r . Pause ( qname ) ; err != nil {
t . Fatal ( err )
}
}
2020-08-13 21:54:32 +08:00
h . SeedAllEnqueuedQueues ( t , r . client , tc . enqueued )
2020-06-04 20:37:17 +08:00
2020-06-18 20:45:10 +08:00
got , _ , err := r . Dequeue ( tc . args ... )
if ! cmp . Equal ( got , tc . wantMsg ) || err != tc . err {
2020-06-04 20:37:17 +08:00
t . Errorf ( "Dequeue(%v) = %v, %v; want %v, %v" ,
2020-06-18 20:45:10 +08:00
tc . args , got , err , tc . wantMsg , tc . err )
2019-11-26 12:57:53 +08:00
continue
}
2019-12-28 12:37:15 +08:00
2020-01-07 22:28:34 +08:00
for queue , want := range tc . wantEnqueued {
gotEnqueued := h . GetEnqueuedMessages ( t , r . client , queue )
if diff := cmp . Diff ( want , gotEnqueued , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q: (-want,+got):\n%s" , base . QueueKey ( queue ) , diff )
}
}
2020-08-08 21:04:16 +08:00
for queue , want := range tc . wantInProgress {
gotInProgress := h . GetInProgressMessages ( t , r . client , queue )
if diff := cmp . Diff ( want , gotInProgress , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q: (-want,+got):\n%s" , base . InProgressKey ( queue ) , diff )
}
2019-11-26 12:57:53 +08:00
}
2019-11-20 23:01:24 +08:00
}
}
2019-11-24 07:09:50 +08:00
2019-12-04 22:33:05 +08:00
func TestDone ( t * testing . T ) {
2019-11-28 22:50:05 +08:00
r := setup ( t )
2020-06-18 22:10:57 +08:00
now := time . Now ( )
t1 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-18 22:10:57 +08:00
Type : "send_email" ,
Payload : nil ,
Timeout : 1800 ,
Deadline : 0 ,
2020-08-08 21:48:49 +08:00
Queue : "default" ,
2020-06-18 22:10:57 +08:00
}
t2 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-18 22:10:57 +08:00
Type : "export_csv" ,
Payload : nil ,
Timeout : 0 ,
Deadline : 1592485787 ,
2020-08-08 21:48:49 +08:00
Queue : "custom" ,
2020-06-18 22:10:57 +08:00
}
2020-03-18 21:49:39 +08:00
t3 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-03-18 21:49:39 +08:00
Type : "reindex" ,
Payload : nil ,
2020-06-18 22:10:57 +08:00
Timeout : 1800 ,
Deadline : 0 ,
2020-08-29 21:54:08 +08:00
UniqueKey : "asynq:{default}:unique:reindex:nil" ,
2020-03-18 21:49:39 +08:00
Queue : "default" ,
}
2020-08-08 21:48:49 +08:00
t1Deadline := now . Unix ( ) + t1 . Timeout
t2Deadline := t2 . Deadline
2020-06-22 23:33:58 +08:00
t3Deadline := now . Unix ( ) + t3 . Deadline
2019-11-28 22:50:05 +08:00
tests := [ ] struct {
2020-08-11 12:49:12 +08:00
desc string
2020-08-08 21:48:49 +08:00
inProgress map [ string ] [ ] * base . TaskMessage // initial state of the in-progress list
deadlines map [ string ] [ ] base . Z // initial state of deadlines set
target * base . TaskMessage // task to remove
wantInProgress map [ string ] [ ] * base . TaskMessage // final state of the in-progress list
wantDeadlines map [ string ] [ ] base . Z // final state of the deadline set
2019-11-28 22:50:05 +08:00
} {
{
2020-08-11 12:49:12 +08:00
desc : "removes message from the correct queue" ,
2020-08-08 21:48:49 +08:00
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"custom" : { t2 } ,
} ,
deadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : t1Deadline } } ,
"custom" : { { Message : t2 , Score : t2Deadline } } ,
} ,
target : t1 ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
2020-08-11 12:49:12 +08:00
"default" : { } ,
"custom" : { t2 } ,
2020-08-08 21:48:49 +08:00
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { } ,
"custom" : { { Message : t2 , Score : t2Deadline } } ,
2020-06-18 22:10:57 +08:00
} ,
2019-11-28 22:50:05 +08:00
} ,
{
2020-08-11 12:49:12 +08:00
desc : "with one queue" ,
2020-08-08 21:48:49 +08:00
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
} ,
deadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : t1Deadline } } ,
} ,
target : t1 ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { } ,
} ,
2019-11-28 22:50:05 +08:00
} ,
2020-03-18 21:49:39 +08:00
{
2020-08-11 12:49:12 +08:00
desc : "with multiple messages in a queue" ,
2020-08-08 21:48:49 +08:00
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t3 } ,
"custom" : { t2 } ,
} ,
deadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : t1Deadline } , { Message : t3 , Score : t3Deadline } } ,
"custom" : { { Message : t2 , Score : t2Deadline } } ,
} ,
target : t3 ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
2020-08-11 12:49:12 +08:00
"default" : { t1 } ,
2020-08-08 21:48:49 +08:00
"custom" : { t2 } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : t1Deadline } } ,
"custom" : { { Message : t2 , Score : t2Deadline } } ,
2020-06-18 22:10:57 +08:00
} ,
2020-03-18 21:49:39 +08:00
} ,
2019-11-28 22:50:05 +08:00
}
for _ , tc := range tests {
2019-12-29 12:12:14 +08:00
h . FlushDB ( t , r . client ) // clean up db before each test case
2020-08-08 21:48:49 +08:00
h . SeedAllDeadlines ( t , r . client , tc . deadlines )
h . SeedAllInProgressQueues ( t , r . client , tc . inProgress )
for _ , msgs := range tc . inProgress {
for _ , msg := range msgs {
// Set uniqueness lock if unique key is present.
if len ( msg . UniqueKey ) > 0 {
err := r . client . SetNX ( msg . UniqueKey , msg . ID . String ( ) , time . Minute ) . Err ( )
if err != nil {
t . Fatal ( err )
}
2020-03-18 21:49:39 +08:00
}
}
}
2019-11-28 22:50:05 +08:00
2019-12-04 22:33:05 +08:00
err := r . Done ( tc . target )
2019-11-28 22:50:05 +08:00
if err != nil {
2020-08-11 12:49:12 +08:00
t . Errorf ( "%s; (*RDB).Done(task) = %v, want nil" , tc . desc , err )
2019-11-28 22:50:05 +08:00
continue
}
2020-08-08 21:48:49 +08:00
for queue , want := range tc . wantInProgress {
gotInProgress := h . GetInProgressMessages ( t , r . client , queue )
if diff := cmp . Diff ( want , gotInProgress , h . SortMsgOpt ) ; diff != "" {
2020-08-11 12:49:12 +08:00
t . Errorf ( "%s; mismatch found in %q: (-want, +got):\n%s" , tc . desc , base . InProgressKey ( queue ) , diff )
2020-08-08 21:48:49 +08:00
continue
}
2019-11-28 23:47:12 +08:00
}
2020-08-08 21:48:49 +08:00
for queue , want := range tc . wantDeadlines {
gotDeadlines := h . GetDeadlinesEntries ( t , r . client , queue )
if diff := cmp . Diff ( want , gotDeadlines , h . SortZSetEntryOpt ) ; diff != "" {
2020-08-11 12:49:12 +08:00
t . Errorf ( "%s; mismatch found in %q: (-want, +got):\n%s" , tc . desc , base . DeadlinesKey ( queue ) , diff )
2020-08-08 21:48:49 +08:00
continue
}
2020-06-18 22:10:57 +08:00
}
2019-12-23 21:33:48 +08:00
2020-08-08 21:48:49 +08:00
processedKey := base . ProcessedKey ( tc . target . Queue , time . Now ( ) )
2019-12-23 21:33:48 +08:00
gotProcessed := r . client . Get ( processedKey ) . Val ( )
if gotProcessed != "1" {
2020-08-11 12:49:12 +08:00
t . Errorf ( "%s; GET %q = %q, want 1" , tc . desc , processedKey , gotProcessed )
2019-12-23 21:33:48 +08:00
}
gotTTL := r . client . TTL ( processedKey ) . Val ( )
if gotTTL > statsTTL {
2020-08-11 12:49:12 +08:00
t . Errorf ( "%s; TTL %q = %v, want less than or equal to %v" , tc . desc , processedKey , gotTTL , statsTTL )
2019-12-23 21:33:48 +08:00
}
2020-03-18 21:49:39 +08:00
if len ( tc . target . UniqueKey ) > 0 && r . client . Exists ( tc . target . UniqueKey ) . Val ( ) != 0 {
2020-08-11 12:49:12 +08:00
t . Errorf ( "%s; Uniqueness lock %q still exists" , tc . desc , tc . target . UniqueKey )
2020-03-18 21:49:39 +08:00
}
2019-11-28 23:47:12 +08:00
}
}
2019-12-18 12:07:17 +08:00
func TestRequeue ( t * testing . T ) {
r := setup ( t )
2020-06-19 03:12:29 +08:00
now := time . Now ( )
t1 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 03:12:29 +08:00
Type : "send_email" ,
Payload : nil ,
Queue : "default" ,
Timeout : 1800 ,
}
t2 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 03:12:29 +08:00
Type : "export_csv" ,
Payload : nil ,
Queue : "default" ,
Timeout : 3000 ,
}
t3 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 03:12:29 +08:00
Type : "send_email" ,
Payload : nil ,
Queue : "critical" ,
Timeout : 80 ,
}
2020-06-22 23:33:58 +08:00
t1Deadline := now . Unix ( ) + t1 . Timeout
t2Deadline := now . Unix ( ) + t2 . Timeout
t3Deadline := now . Unix ( ) + t3 . Timeout
2019-12-18 12:07:17 +08:00
tests := [ ] struct {
2020-01-27 05:41:06 +08:00
enqueued map [ string ] [ ] * base . TaskMessage // initial state of queues
2020-08-09 20:40:44 +08:00
inProgress map [ string ] [ ] * base . TaskMessage // initial state of the in-progress list
deadlines map [ string ] [ ] base . Z // initial state of the deadlines set
2020-01-27 05:41:06 +08:00
target * base . TaskMessage // task to requeue
2020-01-27 05:55:23 +08:00
wantEnqueued map [ string ] [ ] * base . TaskMessage // final state of queues
2020-08-09 20:40:44 +08:00
wantInProgress map [ string ] [ ] * base . TaskMessage // final state of the in-progress list
wantDeadlines map [ string ] [ ] base . Z // final state of the deadlines set
2019-12-18 12:07:17 +08:00
} {
{
2020-01-27 05:41:06 +08:00
enqueued : map [ string ] [ ] * base . TaskMessage {
2020-08-09 20:40:44 +08:00
"default" : { } ,
} ,
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t2 } ,
2020-01-27 05:41:06 +08:00
} ,
2020-08-11 12:49:12 +08:00
deadlines : map [ string ] [ ] base . Z {
2020-08-09 20:40:44 +08:00
"default" : {
{ Message : t1 , Score : t1Deadline } ,
{ Message : t2 , Score : t2Deadline } ,
} ,
2020-06-19 03:12:29 +08:00
} ,
target : t1 ,
2020-01-27 05:41:06 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
2020-08-09 20:40:44 +08:00
"default" : { t1 } ,
} ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t2 } ,
2020-01-27 05:41:06 +08:00
} ,
2020-08-11 12:49:12 +08:00
wantDeadlines : map [ string ] [ ] base . Z {
"default" : {
2020-08-09 20:40:44 +08:00
{ Message : t2 , Score : t2Deadline } ,
} ,
2020-06-19 03:12:29 +08:00
} ,
2019-12-18 12:07:17 +08:00
} ,
{
2020-01-27 05:41:06 +08:00
enqueued : map [ string ] [ ] * base . TaskMessage {
2020-08-09 20:40:44 +08:00
"default" : { t1 } ,
2020-01-27 05:41:06 +08:00
} ,
2020-08-09 20:40:44 +08:00
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t2 } ,
} ,
deadlines : map [ string ] [ ] base . Z {
"default" : {
{ Message : t2 , Score : t2Deadline } ,
} ,
2020-06-19 03:12:29 +08:00
} ,
target : t2 ,
2020-01-27 05:41:06 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
2020-08-09 20:40:44 +08:00
"default" : { t1 , t2 } ,
} ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { } ,
2020-01-27 05:41:06 +08:00
} ,
2019-12-18 12:07:17 +08:00
} ,
2020-01-27 05:41:06 +08:00
{
enqueued : map [ string ] [ ] * base . TaskMessage {
2020-08-09 20:40:44 +08:00
"default" : { t1 } ,
"critical" : { } ,
2020-01-27 05:41:06 +08:00
} ,
2020-08-09 20:40:44 +08:00
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t2 } ,
"critical" : { t3 } ,
} ,
deadlines : map [ string ] [ ] base . Z {
2020-08-11 12:49:12 +08:00
"default" : { { Message : t2 , Score : t2Deadline } } ,
"critical" : { { Message : t3 , Score : t3Deadline } } ,
2020-06-19 03:12:29 +08:00
} ,
target : t3 ,
2020-01-27 05:41:06 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
2020-08-09 20:40:44 +08:00
"default" : { t1 } ,
"critical" : { t3 } ,
2020-01-27 05:41:06 +08:00
} ,
2020-08-09 20:40:44 +08:00
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t2 } ,
"critical" : { } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t2 , Score : t2Deadline } } ,
"critical" : { } ,
2020-06-19 03:12:29 +08:00
} ,
2020-01-27 05:41:06 +08:00
} ,
2019-12-18 12:07:17 +08:00
}
for _ , tc := range tests {
2019-12-29 12:12:14 +08:00
h . FlushDB ( t , r . client ) // clean up db before each test case
2020-08-11 12:49:12 +08:00
h . SeedAllEnqueuedQueues ( t , r . client , tc . enqueued )
2020-08-09 20:40:44 +08:00
h . SeedAllInProgressQueues ( t , r . client , tc . inProgress )
h . SeedAllDeadlines ( t , r . client , tc . deadlines )
2019-12-18 12:07:17 +08:00
err := r . Requeue ( tc . target )
if err != nil {
t . Errorf ( "(*RDB).Requeue(task) = %v, want nil" , err )
continue
}
2020-01-27 05:41:06 +08:00
for qname , want := range tc . wantEnqueued {
gotEnqueued := h . GetEnqueuedMessages ( t , r . client , qname )
if diff := cmp . Diff ( want , gotEnqueued , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q; (-want, +got)\n%s" , base . QueueKey ( qname ) , diff )
}
2019-12-18 12:07:17 +08:00
}
2020-08-09 20:40:44 +08:00
for qname , want := range tc . wantInProgress {
gotInProgress := h . GetInProgressMessages ( t , r . client , qname )
if diff := cmp . Diff ( want , gotInProgress , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q: (-want, +got):\n%s" , base . InProgressKey ( qname ) , diff )
}
2019-12-18 12:07:17 +08:00
}
2020-08-09 20:40:44 +08:00
for qname , want := range tc . wantDeadlines {
gotDeadlines := h . GetDeadlinesEntries ( t , r . client , qname )
2020-08-11 12:49:12 +08:00
if diff := cmp . Diff ( want , gotDeadlines , h . SortZSetEntryOpt ) ; diff != "" {
2020-08-09 20:40:44 +08:00
t . Errorf ( "mismatch found in %q: (-want, +got):\n%s" , base . DeadlinesKey ( qname ) , diff )
}
2020-06-19 03:12:29 +08:00
}
2019-12-18 12:07:17 +08:00
}
}
2019-12-28 12:37:15 +08:00
func TestSchedule ( t * testing . T ) {
r := setup ( t )
2019-12-29 12:12:14 +08:00
t1 := h . NewTaskMessage ( "send_email" , map [ string ] interface { } { "subject" : "hello" } )
2019-12-28 12:37:15 +08:00
tests := [ ] struct {
msg * base . TaskMessage
processAt time . Time
} {
{ t1 , time . Now ( ) . Add ( 15 * time . Minute ) } ,
}
for _ , tc := range tests {
2019-12-29 12:12:14 +08:00
h . FlushDB ( t , r . client ) // clean up db before each test case
2019-12-28 12:37:15 +08:00
desc := fmt . Sprintf ( "(*RDB).Schedule(%v, %v)" , tc . msg , tc . processAt )
err := r . Schedule ( tc . msg , tc . processAt )
if err != nil {
t . Errorf ( "%s = %v, want nil" , desc , err )
continue
}
2020-08-07 21:31:02 +08:00
gotScheduled := h . GetScheduledEntries ( t , r . client , tc . msg . Queue )
2019-12-29 12:12:14 +08:00
if len ( gotScheduled ) != 1 {
2020-08-07 21:31:02 +08:00
t . Errorf ( "%s inserted %d items to %q, want 1 items inserted" ,
desc , len ( gotScheduled ) , base . ScheduledKey ( tc . msg . Queue ) )
2019-12-28 12:37:15 +08:00
continue
}
2020-01-12 02:02:13 +08:00
if int64 ( gotScheduled [ 0 ] . Score ) != tc . processAt . Unix ( ) {
2020-08-07 21:31:02 +08:00
t . Errorf ( "%s inserted an item with score %d, want %d" ,
desc , int64 ( gotScheduled [ 0 ] . Score ) , tc . processAt . Unix ( ) )
2019-12-28 12:37:15 +08:00
continue
}
2020-08-07 21:31:02 +08:00
if ! r . client . SIsMember ( base . AllQueues , tc . msg . Queue ) . Val ( ) {
t . Errorf ( "%q is not a member of SET %q" , tc . msg . Queue , base . AllQueues )
}
2019-12-28 12:37:15 +08:00
}
}
2020-03-18 21:49:39 +08:00
func TestScheduleUnique ( t * testing . T ) {
r := setup ( t )
m1 := base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-03-18 21:49:39 +08:00
Type : "email" ,
Payload : map [ string ] interface { } { "user_id" : 123 } ,
Queue : base . DefaultQueueName ,
2020-08-07 21:31:02 +08:00
UniqueKey : base . UniqueKey ( base . DefaultQueueName , "email" , map [ string ] interface { } { "user_id" : 123 } ) ,
2020-03-18 21:49:39 +08:00
}
tests := [ ] struct {
msg * base . TaskMessage
processAt time . Time
ttl time . Duration // uniqueness lock ttl
} {
{ & m1 , time . Now ( ) . Add ( 15 * time . Minute ) , time . Minute } ,
}
for _ , tc := range tests {
h . FlushDB ( t , r . client ) // clean up db before each test case
desc := fmt . Sprintf ( "(*RDB).ScheduleUnique(%v, %v, %v)" , tc . msg , tc . processAt , tc . ttl )
err := r . ScheduleUnique ( tc . msg , tc . processAt , tc . ttl )
if err != nil {
t . Errorf ( "Frist task: %s = %v, want nil" , desc , err )
continue
}
2020-08-07 21:31:02 +08:00
gotScheduled := h . GetScheduledEntries ( t , r . client , tc . msg . Queue )
2020-03-18 21:49:39 +08:00
if len ( gotScheduled ) != 1 {
2020-08-07 21:31:02 +08:00
t . Errorf ( "%s inserted %d items to %q, want 1 items inserted" , desc , len ( gotScheduled ) , base . ScheduledKey ( tc . msg . Queue ) )
2020-03-18 21:49:39 +08:00
continue
}
if int64 ( gotScheduled [ 0 ] . Score ) != tc . processAt . Unix ( ) {
t . Errorf ( "%s inserted an item with score %d, want %d" , desc , int64 ( gotScheduled [ 0 ] . Score ) , tc . processAt . Unix ( ) )
continue
}
got := r . ScheduleUnique ( tc . msg , tc . processAt , tc . ttl )
if got != ErrDuplicateTask {
t . Errorf ( "Second task: %s = %v, want %v" ,
desc , got , ErrDuplicateTask )
}
gotTTL := r . client . TTL ( tc . msg . UniqueKey ) . Val ( )
if ! cmp . Equal ( tc . ttl . Seconds ( ) , gotTTL . Seconds ( ) , cmpopts . EquateApprox ( 0 , 1 ) ) {
t . Errorf ( "TTL %q = %v, want %v" , tc . msg . UniqueKey , gotTTL , tc . ttl )
continue
}
2020-08-07 21:31:02 +08:00
if ! r . client . SIsMember ( base . AllQueues , tc . msg . Queue ) . Val ( ) {
t . Errorf ( "%q is not a member of SET %q" , tc . msg . Queue , base . AllQueues )
continue
}
2020-03-18 21:49:39 +08:00
}
}
2019-12-28 12:37:15 +08:00
func TestRetry ( t * testing . T ) {
r := setup ( t )
2020-06-19 01:25:01 +08:00
now := time . Now ( )
t1 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 01:25:01 +08:00
Type : "send_email" ,
Payload : map [ string ] interface { } { "subject" : "Hola!" } ,
2020-06-23 21:34:59 +08:00
Retried : 10 ,
2020-06-19 01:25:01 +08:00
Timeout : 1800 ,
2020-08-09 03:17:33 +08:00
Queue : "default" ,
2020-06-19 01:25:01 +08:00
}
t2 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 01:25:01 +08:00
Type : "gen_thumbnail" ,
Payload : map [ string ] interface { } { "path" : "some/path/to/image.jpg" } ,
Timeout : 3000 ,
2020-08-09 03:17:33 +08:00
Queue : "default" ,
2020-06-19 01:25:01 +08:00
}
t3 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 01:25:01 +08:00
Type : "reindex" ,
Payload : nil ,
Timeout : 60 ,
2020-08-09 03:17:33 +08:00
Queue : "default" ,
}
t4 := & base . TaskMessage {
ID : uuid . New ( ) ,
Type : "send_notification" ,
Payload : nil ,
Timeout : 1800 ,
Queue : "custom" ,
2020-06-19 01:25:01 +08:00
}
2020-08-09 03:17:33 +08:00
t1Deadline := now . Unix ( ) + t1 . Timeout
t2Deadline := now . Unix ( ) + t2 . Timeout
t4Deadline := now . Unix ( ) + t4 . Timeout
2019-12-28 12:37:15 +08:00
errMsg := "SMTP server is not responding"
tests := [ ] struct {
2020-08-09 03:17:33 +08:00
inProgress map [ string ] [ ] * base . TaskMessage
deadlines map [ string ] [ ] base . Z
retry map [ string ] [ ] base . Z
2019-12-28 12:37:15 +08:00
msg * base . TaskMessage
processAt time . Time
errMsg string
2020-08-09 03:17:33 +08:00
wantInProgress map [ string ] [ ] * base . TaskMessage
wantDeadlines map [ string ] [ ] base . Z
wantRetry map [ string ] [ ] base . Z
2019-12-28 12:37:15 +08:00
} {
{
2020-08-09 03:17:33 +08:00
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t2 } ,
2020-06-19 01:25:01 +08:00
} ,
2020-08-09 03:17:33 +08:00
deadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : t1Deadline } , { Message : t2 , Score : t2Deadline } } ,
2019-12-28 12:37:15 +08:00
} ,
2020-08-09 03:17:33 +08:00
retry : map [ string ] [ ] base . Z {
"default" : { { Message : t3 , Score : now . Add ( time . Minute ) . Unix ( ) } } ,
2020-06-19 01:25:01 +08:00
} ,
2020-08-09 03:17:33 +08:00
msg : t1 ,
processAt : now . Add ( 5 * time . Minute ) ,
errMsg : errMsg ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t2 } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t2 , Score : t2Deadline } } ,
} ,
wantRetry : map [ string ] [ ] base . Z {
"default" : {
{ Message : h . TaskMessageAfterRetry ( * t1 , errMsg ) , Score : now . Add ( 5 * time . Minute ) . Unix ( ) } ,
{ Message : t3 , Score : now . Add ( time . Minute ) . Unix ( ) } ,
2019-12-29 12:12:14 +08:00
} ,
2020-08-09 03:17:33 +08:00
} ,
} ,
{
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t2 } ,
"custom" : { t4 } ,
} ,
deadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : t1Deadline } , { Message : t2 , Score : t2Deadline } } ,
"custom" : { { Message : t4 , Score : t4Deadline } } ,
} ,
retry : map [ string ] [ ] base . Z {
"default" : { } ,
"custom" : { } ,
} ,
msg : t4 ,
processAt : now . Add ( 5 * time . Minute ) ,
errMsg : errMsg ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t2 } ,
"custom" : { } ,
} ,
wantDeadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : t1Deadline } , { Message : t2 , Score : t2Deadline } } ,
"custom" : { } ,
} ,
wantRetry : map [ string ] [ ] base . Z {
"default" : { } ,
"custom" : {
{ Message : h . TaskMessageAfterRetry ( * t4 , errMsg ) , Score : now . Add ( 5 * time . Minute ) . Unix ( ) } ,
2019-12-29 12:12:14 +08:00
} ,
2019-12-28 12:37:15 +08:00
} ,
} ,
}
for _ , tc := range tests {
2019-12-29 12:12:14 +08:00
h . FlushDB ( t , r . client )
2020-08-09 03:17:33 +08:00
h . SeedAllInProgressQueues ( t , r . client , tc . inProgress )
h . SeedAllDeadlines ( t , r . client , tc . deadlines )
h . SeedAllRetryQueues ( t , r . client , tc . retry )
2019-12-28 12:37:15 +08:00
err := r . Retry ( tc . msg , tc . processAt , tc . errMsg )
if err != nil {
t . Errorf ( "(*RDB).Retry = %v, want nil" , err )
continue
}
2020-08-09 03:17:33 +08:00
for queue , want := range tc . wantInProgress {
gotInProgress := h . GetInProgressMessages ( t , r . client , queue )
if diff := cmp . Diff ( want , gotInProgress , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q; (-want, +got)\n%s" , base . InProgressKey ( queue ) , diff )
}
2019-12-28 12:37:15 +08:00
}
2020-08-09 03:17:33 +08:00
for queue , want := range tc . wantDeadlines {
gotDeadlines := h . GetDeadlinesEntries ( t , r . client , queue )
if diff := cmp . Diff ( want , gotDeadlines , h . SortZSetEntryOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q; (-want, +got)\n%s" , base . DeadlinesKey ( queue ) , diff )
}
2020-06-19 01:25:01 +08:00
}
2020-08-09 03:17:33 +08:00
for queue , want := range tc . wantRetry {
gotRetry := h . GetRetryEntries ( t , r . client , queue )
if diff := cmp . Diff ( want , gotRetry , h . SortZSetEntryOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q; (-want, +got)\n%s" , base . RetryKey ( queue ) , diff )
}
2019-12-28 12:37:15 +08:00
}
2020-08-09 03:17:33 +08:00
processedKey := base . ProcessedKey ( tc . msg . Queue , time . Now ( ) )
2019-12-28 12:37:15 +08:00
gotProcessed := r . client . Get ( processedKey ) . Val ( )
if gotProcessed != "1" {
t . Errorf ( "GET %q = %q, want 1" , processedKey , gotProcessed )
}
gotTTL := r . client . TTL ( processedKey ) . Val ( )
if gotTTL > statsTTL {
t . Errorf ( "TTL %q = %v, want less than or equal to %v" , processedKey , gotTTL , statsTTL )
}
2020-08-09 03:17:33 +08:00
failedKey := base . FailedKey ( tc . msg . Queue , time . Now ( ) )
2020-08-09 03:44:08 +08:00
gotFailed := r . client . Get ( failedKey ) . Val ( )
if gotFailed != "1" {
t . Errorf ( "GET %q = %q, want 1" , failedKey , gotFailed )
2019-12-28 12:37:15 +08:00
}
2020-08-09 03:17:33 +08:00
gotTTL = r . client . TTL ( failedKey ) . Val ( )
2019-12-28 12:37:15 +08:00
if gotTTL > statsTTL {
2020-08-09 03:17:33 +08:00
t . Errorf ( "TTL %q = %v, want less than or equal to %v" , failedKey , gotTTL , statsTTL )
2019-12-28 12:37:15 +08:00
}
}
}
2019-12-31 01:22:25 +08:00
2019-11-28 23:47:12 +08:00
func TestKill ( t * testing . T ) {
r := setup ( t )
2020-06-19 01:53:58 +08:00
now := time . Now ( )
t1 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 01:53:58 +08:00
Type : "send_email" ,
Payload : nil ,
Queue : "default" ,
Retry : 25 ,
2020-08-09 03:44:08 +08:00
Retried : 25 ,
2020-06-19 01:53:58 +08:00
Timeout : 1800 ,
}
2020-06-22 23:33:58 +08:00
t1Deadline := now . Unix ( ) + t1 . Timeout
2020-06-19 01:53:58 +08:00
t2 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 01:53:58 +08:00
Type : "reindex" ,
Payload : nil ,
Queue : "default" ,
Retry : 25 ,
Retried : 0 ,
Timeout : 3000 ,
}
2020-06-22 23:33:58 +08:00
t2Deadline := now . Unix ( ) + t2 . Timeout
2020-06-19 01:53:58 +08:00
t3 := & base . TaskMessage {
2020-07-02 21:21:20 +08:00
ID : uuid . New ( ) ,
2020-06-19 01:53:58 +08:00
Type : "generate_csv" ,
Payload : nil ,
Queue : "default" ,
Retry : 25 ,
Retried : 0 ,
Timeout : 60 ,
}
2020-06-22 23:33:58 +08:00
t3Deadline := now . Unix ( ) + t3 . Timeout
2020-08-09 03:44:08 +08:00
t4 := & base . TaskMessage {
ID : uuid . New ( ) ,
Type : "send_email" ,
Payload : nil ,
Queue : "custom" ,
Retry : 25 ,
Retried : 25 ,
Timeout : 1800 ,
}
t4Deadline := now . Unix ( ) + t4 . Timeout
2019-12-16 09:16:13 +08:00
errMsg := "SMTP server not responding"
2019-11-28 23:47:12 +08:00
// TODO(hibiken): add test cases for trimming
tests := [ ] struct {
2020-08-09 03:44:08 +08:00
inProgress map [ string ] [ ] * base . TaskMessage
deadlines map [ string ] [ ] base . Z
dead map [ string ] [ ] base . Z
2019-12-22 23:15:45 +08:00
target * base . TaskMessage // task to kill
2020-08-09 03:44:08 +08:00
wantInProgress map [ string ] [ ] * base . TaskMessage
wantDeadlines map [ string ] [ ] base . Z
wantDead map [ string ] [ ] base . Z
2019-11-28 23:47:12 +08:00
} {
{
2020-08-09 03:44:08 +08:00
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t2 } ,
2020-06-19 01:53:58 +08:00
} ,
2020-08-09 03:44:08 +08:00
deadlines : map [ string ] [ ] base . Z {
"default" : {
{ Message : t1 , Score : t1Deadline } ,
{ Message : t2 , Score : t2Deadline } ,
2019-12-29 12:12:14 +08:00
} ,
2019-12-16 09:16:13 +08:00
} ,
2020-08-09 03:44:08 +08:00
dead : map [ string ] [ ] base . Z {
"default" : {
{ Message : t3 , Score : now . Add ( - time . Hour ) . Unix ( ) } ,
} ,
} ,
target : t1 ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t2 } ,
} ,
2020-08-11 12:49:12 +08:00
wantDeadlines : map [ string ] [ ] base . Z {
2020-08-09 03:44:08 +08:00
"default" : { { Message : t2 , Score : t2Deadline } } ,
} ,
wantDead : map [ string ] [ ] base . Z {
"default" : {
{ Message : h . TaskMessageWithError ( * t1 , errMsg ) , Score : now . Unix ( ) } ,
{ Message : t3 , Score : now . Add ( - time . Hour ) . Unix ( ) } ,
} ,
} ,
} ,
{
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t2 , t3 } ,
} ,
deadlines : map [ string ] [ ] base . Z {
"default" : {
{ Message : t1 , Score : t1Deadline } ,
{ Message : t2 , Score : t2Deadline } ,
{ Message : t3 , Score : t3Deadline } ,
} ,
} ,
dead : map [ string ] [ ] base . Z {
"default" : { } ,
} ,
target : t1 ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t2 , t3 } ,
2020-06-19 01:53:58 +08:00
} ,
2020-08-09 03:44:08 +08:00
wantDeadlines : map [ string ] [ ] base . Z {
"default" : {
{ Message : t2 , Score : t2Deadline } ,
{ Message : t3 , Score : t3Deadline } ,
2019-12-29 12:12:14 +08:00
} ,
2020-08-09 03:44:08 +08:00
} ,
wantDead : map [ string ] [ ] base . Z {
"default" : {
{ Message : h . TaskMessageWithError ( * t1 , errMsg ) , Score : now . Unix ( ) } ,
2019-12-29 12:12:14 +08:00
} ,
2019-12-16 09:16:13 +08:00
} ,
} ,
{
2020-08-09 03:44:08 +08:00
inProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"custom" : { t4 } ,
2020-06-19 01:53:58 +08:00
} ,
2020-08-09 03:44:08 +08:00
deadlines : map [ string ] [ ] base . Z {
"default" : {
{ Message : t1 , Score : t1Deadline } ,
} ,
"custom" : {
{ Message : t4 , Score : t4Deadline } ,
} ,
} ,
dead : map [ string ] [ ] base . Z {
"default" : { } ,
"custom" : { } ,
} ,
target : t4 ,
wantInProgress : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"custom" : { } ,
} ,
2020-08-11 12:49:12 +08:00
wantDeadlines : map [ string ] [ ] base . Z {
2020-08-09 03:44:08 +08:00
"default" : { { Message : t1 , Score : t1Deadline } } ,
"custom" : { } ,
2020-06-19 01:53:58 +08:00
} ,
2020-08-09 03:44:08 +08:00
wantDead : map [ string ] [ ] base . Z {
"default" : { } ,
"custom" : {
{ Message : h . TaskMessageWithError ( * t4 , errMsg ) , Score : now . Unix ( ) } ,
2019-12-29 12:12:14 +08:00
} ,
2019-12-16 09:16:13 +08:00
} ,
2019-11-28 23:47:12 +08:00
} ,
}
for _ , tc := range tests {
2019-12-29 12:12:14 +08:00
h . FlushDB ( t , r . client ) // clean up db before each test case
2020-08-09 03:44:08 +08:00
h . SeedAllInProgressQueues ( t , r . client , tc . inProgress )
h . SeedAllDeadlines ( t , r . client , tc . deadlines )
h . SeedAllDeadQueues ( t , r . client , tc . dead )
2019-11-28 23:47:12 +08:00
2019-12-16 09:16:13 +08:00
err := r . Kill ( tc . target , errMsg )
2019-11-28 23:47:12 +08:00
if err != nil {
2019-12-16 09:16:13 +08:00
t . Errorf ( "(*RDB).Kill(%v, %v) = %v, want nil" , tc . target , errMsg , err )
2019-11-28 23:47:12 +08:00
continue
}
2020-08-09 03:44:08 +08:00
for queue , want := range tc . wantInProgress {
gotInProgress := h . GetInProgressMessages ( t , r . client , queue )
if diff := cmp . Diff ( want , gotInProgress , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q: (-want, +got)\n%s" , base . InProgressKey ( queue ) , diff )
}
2019-12-16 09:16:13 +08:00
}
2020-08-09 03:44:08 +08:00
for queue , want := range tc . wantDeadlines {
gotDeadlines := h . GetDeadlinesEntries ( t , r . client , queue )
if diff := cmp . Diff ( want , gotDeadlines , h . SortZSetEntryOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q after calling (*RDB).Kill: (-want, +got):\n%s" , base . DeadlinesKey ( queue ) , diff )
}
2020-06-19 01:53:58 +08:00
}
2020-08-09 03:44:08 +08:00
for queue , want := range tc . wantDead {
gotDead := h . GetDeadEntries ( t , r . client , queue )
2020-08-29 21:54:08 +08:00
if diff := cmp . Diff ( want , gotDead , h . SortZSetEntryOpt , zScoreCmpOpt ) ; diff != "" {
2020-08-09 03:44:08 +08:00
t . Errorf ( "mismatch found in %q after calling (*RDB).Kill: (-want, +got):\n%s" , base . DeadKey ( queue ) , diff )
}
2019-11-28 22:50:05 +08:00
}
2019-12-23 21:33:48 +08:00
2020-08-09 03:44:08 +08:00
processedKey := base . ProcessedKey ( tc . target . Queue , time . Now ( ) )
2019-12-23 21:33:48 +08:00
gotProcessed := r . client . Get ( processedKey ) . Val ( )
if gotProcessed != "1" {
t . Errorf ( "GET %q = %q, want 1" , processedKey , gotProcessed )
}
gotTTL := r . client . TTL ( processedKey ) . Val ( )
if gotTTL > statsTTL {
t . Errorf ( "TTL %q = %v, want less than or equal to %v" , processedKey , gotTTL , statsTTL )
}
2020-08-09 03:44:08 +08:00
failedKey := base . FailedKey ( tc . target . Queue , time . Now ( ) )
gotFailed := r . client . Get ( failedKey ) . Val ( )
if gotFailed != "1" {
t . Errorf ( "GET %q = %q, want 1" , failedKey , gotFailed )
2019-12-23 21:33:48 +08:00
}
gotTTL = r . client . TTL ( processedKey ) . Val ( )
if gotTTL > statsTTL {
2020-08-09 03:44:08 +08:00
t . Errorf ( "TTL %q = %v, want less than or equal to %v" , failedKey , gotTTL , statsTTL )
2019-12-23 21:33:48 +08:00
}
2019-11-28 22:50:05 +08:00
}
}
2019-12-04 23:28:57 +08:00
func TestCheckAndEnqueue ( t * testing . T ) {
2019-11-26 10:55:17 +08:00
r := setup ( t )
2019-12-29 12:12:14 +08:00
t1 := h . NewTaskMessage ( "send_email" , nil )
t2 := h . NewTaskMessage ( "generate_csv" , nil )
t3 := h . NewTaskMessage ( "gen_thumbnail" , nil )
2020-08-09 21:26:14 +08:00
t4 := h . NewTaskMessageWithQueue ( "important_task" , nil , "critical" )
t5 := h . NewTaskMessageWithQueue ( "minor_task" , nil , "low" )
2019-11-26 22:38:11 +08:00
secondAgo := time . Now ( ) . Add ( - time . Second )
hourFromNow := time . Now ( ) . Add ( time . Hour )
tests := [ ] struct {
2020-08-09 21:26:14 +08:00
scheduled map [ string ] [ ] base . Z
retry map [ string ] [ ] base . Z
qnames [ ] string
2020-01-07 13:27:51 +08:00
wantEnqueued map [ string ] [ ] * base . TaskMessage
2020-08-09 21:26:14 +08:00
wantScheduled map [ string ] [ ] * base . TaskMessage
wantRetry map [ string ] [ ] * base . TaskMessage
2019-11-26 22:38:11 +08:00
} {
{
2020-08-09 21:26:14 +08:00
scheduled : map [ string ] [ ] base . Z {
"default" : {
{ Message : t1 , Score : secondAgo . Unix ( ) } ,
{ Message : t2 , Score : secondAgo . Unix ( ) } ,
} ,
} ,
retry : map [ string ] [ ] base . Z {
"default" : { { Message : t3 , Score : secondAgo . Unix ( ) } } ,
2019-12-29 12:12:14 +08:00
} ,
2020-08-09 21:26:14 +08:00
qnames : [ ] string { "default" } ,
2020-01-07 13:27:51 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t2 , t3 } ,
} ,
2020-08-09 21:26:14 +08:00
wantScheduled : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
wantRetry : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
2019-11-26 22:38:11 +08:00
} ,
{
2020-08-09 21:26:14 +08:00
scheduled : map [ string ] [ ] base . Z {
"default" : {
{ Message : t1 , Score : hourFromNow . Unix ( ) } ,
{ Message : t2 , Score : secondAgo . Unix ( ) } ,
} ,
} ,
retry : map [ string ] [ ] base . Z {
"default" : { { Message : t3 , Score : secondAgo . Unix ( ) } } ,
} ,
qnames : [ ] string { "default" } ,
2020-01-07 13:27:51 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t2 , t3 } ,
} ,
2020-08-09 21:26:14 +08:00
wantScheduled : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
} ,
wantRetry : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
2019-11-26 22:38:11 +08:00
} ,
{
2020-08-09 21:26:14 +08:00
scheduled : map [ string ] [ ] base . Z {
"default" : {
{ Message : t1 , Score : hourFromNow . Unix ( ) } ,
{ Message : t2 , Score : hourFromNow . Unix ( ) } ,
} ,
} ,
retry : map [ string ] [ ] base . Z {
"default" : { { Message : t3 , Score : hourFromNow . Unix ( ) } } ,
} ,
qnames : [ ] string { "default" } ,
2020-01-07 13:27:51 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
} ,
2020-08-09 21:26:14 +08:00
wantScheduled : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 , t2 } ,
} ,
wantRetry : map [ string ] [ ] * base . TaskMessage {
"default" : { t3 } ,
} ,
2019-11-26 22:38:11 +08:00
} ,
2020-01-07 13:27:51 +08:00
{
2020-08-09 21:26:14 +08:00
scheduled : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : secondAgo . Unix ( ) } } ,
"critical" : { { Message : t4 , Score : secondAgo . Unix ( ) } } ,
"low" : { } ,
2020-01-07 13:27:51 +08:00
} ,
2020-08-09 21:26:14 +08:00
retry : map [ string ] [ ] base . Z {
"default" : { } ,
"critical" : { } ,
"low" : { { Message : t5 , Score : secondAgo . Unix ( ) } } ,
} ,
qnames : [ ] string { "default" , "critical" , "low" } ,
2020-01-07 13:27:51 +08:00
wantEnqueued : map [ string ] [ ] * base . TaskMessage {
"default" : { t1 } ,
"critical" : { t4 } ,
"low" : { t5 } ,
} ,
2020-08-09 21:26:14 +08:00
wantScheduled : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { } ,
"low" : { } ,
} ,
wantRetry : map [ string ] [ ] * base . TaskMessage {
"default" : { } ,
"critical" : { } ,
"low" : { } ,
} ,
2020-01-07 13:27:51 +08:00
} ,
2019-11-26 22:38:11 +08:00
}
for _ , tc := range tests {
2019-12-29 12:12:14 +08:00
h . FlushDB ( t , r . client ) // clean up db before each test case
2020-08-09 21:26:14 +08:00
h . SeedAllScheduledQueues ( t , r . client , tc . scheduled )
h . SeedAllRetryQueues ( t , r . client , tc . retry )
2019-11-25 23:09:39 +08:00
2020-08-09 21:26:14 +08:00
err := r . CheckAndEnqueue ( tc . qnames ... )
2019-11-26 22:38:11 +08:00
if err != nil {
2020-08-09 21:26:14 +08:00
t . Errorf ( "(*RDB).CheckScheduled(%v) = %v, want nil" , tc . qnames , err )
2019-11-26 22:38:11 +08:00
continue
}
2019-12-29 12:12:14 +08:00
2020-01-07 13:27:51 +08:00
for qname , want := range tc . wantEnqueued {
gotEnqueued := h . GetEnqueuedMessages ( t , r . client , qname )
if diff := cmp . Diff ( want , gotEnqueued , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q; (-want, +got)\n%s" , base . QueueKey ( qname ) , diff )
}
2019-11-26 22:38:11 +08:00
}
2020-08-09 21:26:14 +08:00
for qname , want := range tc . wantScheduled {
gotScheduled := h . GetScheduledMessages ( t , r . client , qname )
if diff := cmp . Diff ( want , gotScheduled , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q; (-want, +got)\n%s" , base . ScheduledKey ( qname ) , diff )
}
2019-12-13 11:49:41 +08:00
}
2020-08-09 21:26:14 +08:00
for qname , want := range tc . wantRetry {
gotRetry := h . GetRetryMessages ( t , r . client , qname )
if diff := cmp . Diff ( want , gotRetry , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "mismatch found in %q; (-want, +got)\n%s" , base . RetryKey ( qname ) , diff )
}
2019-11-26 22:38:11 +08:00
}
2019-11-25 23:09:39 +08:00
}
}
2020-01-31 22:48:58 +08:00
2020-06-20 21:29:58 +08:00
func TestListDeadlineExceeded ( t * testing . T ) {
2020-08-10 20:37:49 +08:00
t1 := h . NewTaskMessageWithQueue ( "task1" , nil , "default" )
t2 := h . NewTaskMessageWithQueue ( "task2" , nil , "default" )
2020-06-20 21:29:58 +08:00
t3 := h . NewTaskMessageWithQueue ( "task3" , nil , "critical" )
now := time . Now ( )
oneHourFromNow := now . Add ( 1 * time . Hour )
fiveMinutesFromNow := now . Add ( 5 * time . Minute )
fiveMinutesAgo := now . Add ( - 5 * time . Minute )
oneHourAgo := now . Add ( - 1 * time . Hour )
tests := [ ] struct {
desc string
2020-08-10 20:37:49 +08:00
deadlines map [ string ] [ ] base . Z
qnames [ ] string
2020-06-20 21:29:58 +08:00
t time . Time
want [ ] * base . TaskMessage
} {
{
desc : "with one task in-progress" ,
2020-08-10 20:37:49 +08:00
deadlines : map [ string ] [ ] base . Z {
"default" : { { Message : t1 , Score : fiveMinutesAgo . Unix ( ) } } ,
2020-06-20 21:29:58 +08:00
} ,
2020-08-10 20:37:49 +08:00
qnames : [ ] string { "default" } ,
t : time . Now ( ) ,
want : [ ] * base . TaskMessage { t1 } ,
2020-06-20 21:29:58 +08:00
} ,
{
desc : "with multiple tasks in-progress, and one expired" ,
2020-08-10 20:37:49 +08:00
deadlines : map [ string ] [ ] base . Z {
"default" : {
{ Message : t1 , Score : oneHourAgo . Unix ( ) } ,
{ Message : t2 , Score : fiveMinutesFromNow . Unix ( ) } ,
} ,
"critical" : {
{ Message : t3 , Score : oneHourFromNow . Unix ( ) } ,
} ,
2020-06-20 21:29:58 +08:00
} ,
2020-08-10 20:37:49 +08:00
qnames : [ ] string { "default" , "critical" } ,
t : time . Now ( ) ,
want : [ ] * base . TaskMessage { t1 } ,
2020-06-20 21:29:58 +08:00
} ,
{
desc : "with multiple expired tasks in-progress" ,
2020-08-10 20:37:49 +08:00
deadlines : map [ string ] [ ] base . Z {
"default" : {
{ Message : t1 , Score : oneHourAgo . Unix ( ) } ,
{ Message : t2 , Score : oneHourFromNow . Unix ( ) } ,
} ,
"critical" : {
{ Message : t3 , Score : fiveMinutesAgo . Unix ( ) } ,
} ,
2020-06-20 21:29:58 +08:00
} ,
2020-08-10 20:37:49 +08:00
qnames : [ ] string { "default" , "critical" } ,
t : time . Now ( ) ,
2020-08-11 12:49:12 +08:00
want : [ ] * base . TaskMessage { t1 , t3 } ,
2020-06-20 21:29:58 +08:00
} ,
{
2020-08-10 20:37:49 +08:00
desc : "with empty in-progress queue" ,
deadlines : map [ string ] [ ] base . Z {
"default" : { } ,
"critical" : { } ,
} ,
qnames : [ ] string { "default" , "critical" } ,
t : time . Now ( ) ,
want : [ ] * base . TaskMessage { } ,
2020-06-20 21:29:58 +08:00
} ,
}
r := setup ( t )
for _ , tc := range tests {
h . FlushDB ( t , r . client )
2020-08-10 20:37:49 +08:00
h . SeedAllDeadlines ( t , r . client , tc . deadlines )
2020-06-20 21:29:58 +08:00
2020-08-10 20:37:49 +08:00
got , err := r . ListDeadlineExceeded ( tc . t , tc . qnames ... )
2020-06-20 21:29:58 +08:00
if err != nil {
t . Errorf ( "%s; ListDeadlineExceeded(%v) returned error: %v" , tc . desc , tc . t , err )
continue
}
if diff := cmp . Diff ( tc . want , got , h . SortMsgOpt ) ; diff != "" {
t . Errorf ( "%s; ListDeadlineExceeded(%v) returned %v, want %v;(-want,+got)\n%s" ,
tc . desc , tc . t , got , tc . want , diff )
}
}
}
2020-04-13 07:42:11 +08:00
func TestWriteServerState ( t * testing . T ) {
2020-01-31 22:48:58 +08:00
r := setup ( t )
2020-02-21 23:18:22 +08:00
2020-05-19 11:47:35 +08:00
var (
host = "localhost"
pid = 4242
serverID = "server123"
2020-02-21 23:18:22 +08:00
2020-05-19 11:47:35 +08:00
ttl = 5 * time . Second
)
2020-02-21 23:18:22 +08:00
2020-05-19 11:47:35 +08:00
info := base . ServerInfo {
Host : host ,
PID : pid ,
ServerID : serverID ,
Concurrency : 10 ,
Queues : map [ string ] int { "default" : 2 , "email" : 5 , "low" : 1 } ,
StrictPriority : false ,
Started : time . Now ( ) ,
Status : "running" ,
ActiveWorkerCount : 0 ,
}
err := r . WriteServerState ( & info , nil /* workers */ , ttl )
2020-02-21 23:18:22 +08:00
if err != nil {
2020-04-13 07:42:11 +08:00
t . Errorf ( "r.WriteServerState returned an error: %v" , err )
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
// Check ServerInfo was written correctly.
skey := base . ServerInfoKey ( host , pid , serverID )
2020-04-13 07:42:11 +08:00
data := r . client . Get ( skey ) . Val ( )
var got base . ServerInfo
2020-02-21 23:18:22 +08:00
err = json . Unmarshal ( [ ] byte ( data ) , & got )
if err != nil {
t . Fatalf ( "could not decode json: %v" , err )
}
2020-05-19 11:47:35 +08:00
if diff := cmp . Diff ( info , got ) ; diff != "" {
2020-04-13 07:42:11 +08:00
t . Errorf ( "persisted ServerInfo was %v, want %v; (-want,+got)\n%s" ,
2020-05-19 11:47:35 +08:00
got , info , diff )
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
// Check ServerInfo TTL was set correctly.
2020-04-13 07:42:11 +08:00
gotTTL := r . client . TTL ( skey ) . Val ( )
2020-03-18 21:49:39 +08:00
if ! cmp . Equal ( ttl . Seconds ( ) , gotTTL . Seconds ( ) , cmpopts . EquateApprox ( 0 , 1 ) ) {
2020-04-13 07:42:11 +08:00
t . Errorf ( "TTL of %q was %v, want %v" , skey , gotTTL , ttl )
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
// Check ServerInfo key was added to the set all server keys correctly.
gotServerKeys := r . client . ZRange ( base . AllServers , 0 , - 1 ) . Val ( )
wantServerKeys := [ ] string { skey }
if diff := cmp . Diff ( wantServerKeys , gotServerKeys ) ; diff != "" {
t . Errorf ( "%q contained %v, want %v" , base . AllServers , gotServerKeys , wantServerKeys )
2020-01-31 22:48:58 +08:00
}
2020-05-19 11:47:35 +08:00
// Check WorkersInfo was written correctly.
wkey := base . WorkersKey ( host , pid , serverID )
2020-02-21 23:18:22 +08:00
workerExist := r . client . Exists ( wkey ) . Val ( )
if workerExist != 0 {
t . Errorf ( "%q key exists" , wkey )
}
2020-05-19 11:47:35 +08:00
// Check WorkersInfo key was added to the set correctly.
2020-02-21 23:18:22 +08:00
gotWorkerKeys := r . client . ZRange ( base . AllWorkers , 0 , - 1 ) . Val ( )
wantWorkerKeys := [ ] string { wkey }
if diff := cmp . Diff ( wantWorkerKeys , gotWorkerKeys ) ; diff != "" {
t . Errorf ( "%q contained %v, want %v" , base . AllWorkers , gotWorkerKeys , wantWorkerKeys )
2020-01-31 22:48:58 +08:00
}
2020-02-21 23:18:22 +08:00
}
2020-01-31 22:48:58 +08:00
2020-04-13 07:42:11 +08:00
func TestWriteServerStateWithWorkers ( t * testing . T ) {
2020-02-21 23:18:22 +08:00
r := setup ( t )
2020-01-31 22:48:58 +08:00
2020-05-19 11:47:35 +08:00
var (
host = "127.0.0.1"
pid = 4242
serverID = "server123"
msg1 = h . NewTaskMessage ( "send_email" , map [ string ] interface { } { "user_id" : "123" } )
msg2 = h . NewTaskMessage ( "gen_thumbnail" , map [ string ] interface { } { "path" : "some/path/to/imgfile" } )
ttl = 5 * time . Second
)
workers := [ ] * base . WorkerInfo {
{
Host : host ,
PID : pid ,
ID : msg1 . ID . String ( ) ,
Type : msg1 . Type ,
Queue : msg1 . Queue ,
Payload : msg1 . Payload ,
Started : time . Now ( ) . Add ( - 10 * time . Second ) ,
} ,
{
Host : host ,
PID : pid ,
ID : msg2 . ID . String ( ) ,
Type : msg2 . Type ,
Queue : msg2 . Queue ,
Payload : msg2 . Payload ,
Started : time . Now ( ) . Add ( - 2 * time . Minute ) ,
} ,
}
serverInfo := base . ServerInfo {
Host : host ,
PID : pid ,
ServerID : serverID ,
Concurrency : 10 ,
Queues : map [ string ] int { "default" : 2 , "email" : 5 , "low" : 1 } ,
StrictPriority : false ,
Started : time . Now ( ) . Add ( - 10 * time . Minute ) ,
Status : "running" ,
ActiveWorkerCount : len ( workers ) ,
}
2020-01-31 22:48:58 +08:00
2020-05-19 11:47:35 +08:00
err := r . WriteServerState ( & serverInfo , workers , ttl )
2020-02-21 23:18:22 +08:00
if err != nil {
2020-05-19 11:47:35 +08:00
t . Fatalf ( "r.WriteServerState returned an error: %v" , err )
2020-02-21 23:18:22 +08:00
}
2020-01-31 22:48:58 +08:00
2020-05-19 11:47:35 +08:00
// Check ServerInfo was written correctly.
skey := base . ServerInfoKey ( host , pid , serverID )
2020-04-13 07:42:11 +08:00
data := r . client . Get ( skey ) . Val ( )
var got base . ServerInfo
2020-02-21 23:18:22 +08:00
err = json . Unmarshal ( [ ] byte ( data ) , & got )
if err != nil {
t . Fatalf ( "could not decode json: %v" , err )
}
2020-05-19 11:47:35 +08:00
if diff := cmp . Diff ( serverInfo , got ) ; diff != "" {
2020-04-13 07:42:11 +08:00
t . Errorf ( "persisted ServerInfo was %v, want %v; (-want,+got)\n%s" ,
2020-05-19 11:47:35 +08:00
got , serverInfo , diff )
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
// Check ServerInfo TTL was set correctly.
2020-04-13 07:42:11 +08:00
gotTTL := r . client . TTL ( skey ) . Val ( )
2020-03-18 21:49:39 +08:00
if ! cmp . Equal ( ttl . Seconds ( ) , gotTTL . Seconds ( ) , cmpopts . EquateApprox ( 0 , 1 ) ) {
2020-04-13 07:42:11 +08:00
t . Errorf ( "TTL of %q was %v, want %v" , skey , gotTTL , ttl )
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
// Check ServerInfo key was added to the set correctly.
gotServerKeys := r . client . ZRange ( base . AllServers , 0 , - 1 ) . Val ( )
wantServerKeys := [ ] string { skey }
if diff := cmp . Diff ( wantServerKeys , gotServerKeys ) ; diff != "" {
t . Errorf ( "%q contained %v, want %v" , base . AllServers , gotServerKeys , wantServerKeys )
2020-02-21 23:18:22 +08:00
}
2020-01-31 22:48:58 +08:00
2020-05-19 11:47:35 +08:00
// Check WorkersInfo was written correctly.
wkey := base . WorkersKey ( host , pid , serverID )
2020-02-21 23:18:22 +08:00
wdata := r . client . HGetAll ( wkey ) . Val ( )
if len ( wdata ) != 2 {
t . Fatalf ( "HGETALL %q returned a hash of size %d, want 2" , wkey , len ( wdata ) )
}
2020-05-19 11:47:35 +08:00
var gotWorkers [ ] * base . WorkerInfo
for _ , val := range wdata {
2020-02-21 23:18:22 +08:00
var w base . WorkerInfo
if err := json . Unmarshal ( [ ] byte ( val ) , & w ) ; err != nil {
t . Fatalf ( "could not unmarshal worker's data: %v" , err )
2020-01-31 22:48:58 +08:00
}
2020-05-19 11:47:35 +08:00
gotWorkers = append ( gotWorkers , & w )
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
if diff := cmp . Diff ( workers , gotWorkers , h . SortWorkerInfoOpt ) ; diff != "" {
2020-02-21 23:18:22 +08:00
t . Errorf ( "persisted workers info was %v, want %v; (-want,+got)\n%s" ,
2020-05-19 11:47:35 +08:00
gotWorkers , workers , diff )
2020-02-21 23:18:22 +08:00
}
2020-02-02 14:22:48 +08:00
2020-05-19 11:47:35 +08:00
// Check WorkersInfo TTL was set correctly.
2020-02-21 23:18:22 +08:00
gotTTL = r . client . TTL ( wkey ) . Val ( )
2020-05-19 11:47:35 +08:00
if ! cmp . Equal ( ttl . Seconds ( ) , gotTTL . Seconds ( ) , cmpopts . EquateApprox ( 0 , 1 ) ) {
2020-02-21 23:18:22 +08:00
t . Errorf ( "TTL of %q was %v, want %v" , wkey , gotTTL , ttl )
}
2020-05-19 11:47:35 +08:00
// Check WorkersInfo key was added to the set correctly.
2020-02-21 23:18:22 +08:00
gotWorkerKeys := r . client . ZRange ( base . AllWorkers , 0 , - 1 ) . Val ( )
wantWorkerKeys := [ ] string { wkey }
if diff := cmp . Diff ( wantWorkerKeys , gotWorkerKeys ) ; diff != "" {
t . Errorf ( "%q contained %v, want %v" , base . AllWorkers , gotWorkerKeys , wantWorkerKeys )
}
}
2020-02-02 14:22:48 +08:00
2020-04-13 07:42:11 +08:00
func TestClearServerState ( t * testing . T ) {
2020-02-21 23:18:22 +08:00
r := setup ( t )
2020-02-02 14:22:48 +08:00
2020-05-19 11:47:35 +08:00
var (
host = "127.0.0.1"
pid = 1234
serverID = "server123"
2020-02-02 14:22:48 +08:00
2020-05-19 11:47:35 +08:00
otherHost = "127.0.0.2"
otherPID = 9876
otherServerID = "server987"
msg1 = h . NewTaskMessage ( "send_email" , map [ string ] interface { } { "user_id" : "123" } )
msg2 = h . NewTaskMessage ( "gen_thumbnail" , map [ string ] interface { } { "path" : "some/path/to/imgfile" } )
ttl = 5 * time . Second
)
workers1 := [ ] * base . WorkerInfo {
{
Host : host ,
PID : pid ,
ID : msg1 . ID . String ( ) ,
Type : msg1 . Type ,
Queue : msg1 . Queue ,
Payload : msg1 . Payload ,
Started : time . Now ( ) . Add ( - 10 * time . Second ) ,
} ,
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
serverInfo1 := base . ServerInfo {
Host : host ,
PID : pid ,
ServerID : serverID ,
Concurrency : 10 ,
Queues : map [ string ] int { "default" : 2 , "email" : 5 , "low" : 1 } ,
StrictPriority : false ,
Started : time . Now ( ) . Add ( - 10 * time . Minute ) ,
Status : "running" ,
ActiveWorkerCount : len ( workers1 ) ,
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
workers2 := [ ] * base . WorkerInfo {
{
Host : otherHost ,
PID : otherPID ,
ID : msg2 . ID . String ( ) ,
Type : msg2 . Type ,
Queue : msg2 . Queue ,
Payload : msg2 . Payload ,
Started : time . Now ( ) . Add ( - 30 * time . Second ) ,
} ,
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
serverInfo2 := base . ServerInfo {
Host : otherHost ,
PID : otherPID ,
ServerID : otherServerID ,
Concurrency : 10 ,
Queues : map [ string ] int { "default" : 2 , "email" : 5 , "low" : 1 } ,
StrictPriority : false ,
Started : time . Now ( ) . Add ( - 15 * time . Minute ) ,
Status : "running" ,
ActiveWorkerCount : len ( workers2 ) ,
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
// Write server and workers data.
if err := r . WriteServerState ( & serverInfo1 , workers1 , ttl ) ; err != nil {
t . Fatalf ( "could not write server state: %v" , err )
2020-02-21 23:18:22 +08:00
}
2020-05-19 11:47:35 +08:00
if err := r . WriteServerState ( & serverInfo2 , workers2 , ttl ) ; err != nil {
t . Fatalf ( "could not write server state: %v" , err )
2020-02-21 23:18:22 +08:00
}
2020-02-02 14:22:48 +08:00
2020-05-19 11:47:35 +08:00
err := r . ClearServerState ( host , pid , serverID )
2020-02-21 23:18:22 +08:00
if err != nil {
2020-04-13 07:42:11 +08:00
t . Fatalf ( "(*RDB).ClearServerState failed: %v" , err )
2020-02-21 23:18:22 +08:00
}
2020-02-02 14:22:48 +08:00
2020-05-19 11:47:35 +08:00
skey := base . ServerInfoKey ( host , pid , serverID )
wkey := base . WorkersKey ( host , pid , serverID )
otherSKey := base . ServerInfoKey ( otherHost , otherPID , otherServerID )
otherWKey := base . WorkersKey ( otherHost , otherPID , otherServerID )
// Check all keys are cleared.
2020-04-13 07:42:11 +08:00
if r . client . Exists ( skey ) . Val ( ) != 0 {
t . Errorf ( "Redis key %q exists" , skey )
2020-02-21 23:18:22 +08:00
}
if r . client . Exists ( wkey ) . Val ( ) != 0 {
t . Errorf ( "Redis key %q exists" , wkey )
}
2020-05-19 11:47:35 +08:00
gotServerKeys := r . client . ZRange ( base . AllServers , 0 , - 1 ) . Val ( )
wantServerKeys := [ ] string { otherSKey }
if diff := cmp . Diff ( wantServerKeys , gotServerKeys ) ; diff != "" {
t . Errorf ( "%q contained %v, want %v" , base . AllServers , gotServerKeys , wantServerKeys )
2020-02-21 23:18:22 +08:00
}
gotWorkerKeys := r . client . ZRange ( base . AllWorkers , 0 , - 1 ) . Val ( )
wantWorkerKeys := [ ] string { otherWKey }
if diff := cmp . Diff ( wantWorkerKeys , gotWorkerKeys ) ; diff != "" {
t . Errorf ( "%q contained %v, want %v" , base . AllWorkers , gotWorkerKeys , wantWorkerKeys )
2020-01-31 22:48:58 +08:00
}
}
2020-02-23 06:30:24 +08:00
func TestCancelationPubSub ( t * testing . T ) {
r := setup ( t )
pubsub , err := r . CancelationPubSub ( )
if err != nil {
t . Fatalf ( "(*RDB).CancelationPubSub() returned an error: %v" , err )
}
cancelCh := pubsub . Channel ( )
var (
mu sync . Mutex
received [ ] string
)
go func ( ) {
for msg := range cancelCh {
mu . Lock ( )
received = append ( received , msg . Payload )
mu . Unlock ( )
}
} ( )
publish := [ ] string { "one" , "two" , "three" }
for _ , msg := range publish {
r . PublishCancelation ( msg )
}
// allow for message to reach subscribers.
time . Sleep ( time . Second )
pubsub . Close ( )
mu . Lock ( )
if diff := cmp . Diff ( publish , received , h . SortStringSliceOpt ) ; diff != "" {
t . Errorf ( "subscriber received %v, want %v; (-want,+got)\n%s" , received , publish , diff )
}
mu . Unlock ( )
}