mirror of
https://github.com/hibiken/asynq.git
synced 2024-12-26 15:52:18 +08:00
Add DeleteQueue method to Inspector
- Added ErrQueueNotFound and ErrQueueNotEmpty type to indicate the kind of an error returned from the method.
This commit is contained in:
parent
8f9d5a3352
commit
e9239260ae
39
inspector.go
39
inspector.go
@ -124,6 +124,45 @@ func (i *Inspector) History(qname string, n int) ([]*DailyStats, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrQueueNotFound indicates that the specified queue does not exist.
|
||||||
|
type ErrQueueNotFound struct {
|
||||||
|
qname string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrQueueNotFound) Error() string {
|
||||||
|
return fmt.Sprintf("queue %q does not exist", e.qname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrQueueNotEmpty indicates that the specified queue is not empty.
|
||||||
|
type ErrQueueNotEmpty struct {
|
||||||
|
qname string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrQueueNotEmpty) Error() string {
|
||||||
|
return fmt.Sprintf("queue %q is not empty", e.qname)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteQueue removes the specified queue.
|
||||||
|
//
|
||||||
|
// If force is set to true, DeleteQueue will remove the queue regardless of
|
||||||
|
// the queue size as long as no tasks are active in the queue.
|
||||||
|
// If force is set to false, DeleteQueue will remove the queue only if
|
||||||
|
// the queue is empty.
|
||||||
|
//
|
||||||
|
// If the specified queue does not exist, DeleteQueue returns ErrQueueNotFound.
|
||||||
|
// If force is set to false and the specified queue is not empty, DeleteQueue
|
||||||
|
// returns ErrQueueNotEmpty.
|
||||||
|
func (i *Inspector) DeleteQueue(qname string, force bool) error {
|
||||||
|
err := i.rdb.RemoveQueue(qname, force)
|
||||||
|
if _, ok := err.(*rdb.ErrQueueNotFound); ok {
|
||||||
|
return &ErrQueueNotFound{qname}
|
||||||
|
}
|
||||||
|
if _, ok := err.(*rdb.ErrQueueNotEmpty); ok {
|
||||||
|
return &ErrQueueNotEmpty{qname}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// PendingTask is a task in a queue and is ready to be processed.
|
// PendingTask is a task in a queue and is ready to be processed.
|
||||||
type PendingTask struct {
|
type PendingTask struct {
|
||||||
*Task
|
*Task
|
||||||
|
@ -50,6 +50,207 @@ func TestInspectorQueues(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInspectorDeleteQueue(t *testing.T) {
|
||||||
|
r := setup(t)
|
||||||
|
defer r.Close()
|
||||||
|
inspector := NewInspector(getRedisConnOpt(t))
|
||||||
|
defer inspector.Close()
|
||||||
|
m1 := h.NewTaskMessage("task1", nil)
|
||||||
|
m2 := h.NewTaskMessage("task2", nil)
|
||||||
|
m3 := h.NewTaskMessageWithQueue("task3", nil, "custom")
|
||||||
|
m4 := h.NewTaskMessageWithQueue("task4", nil, "custom")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
pending map[string][]*base.TaskMessage
|
||||||
|
active map[string][]*base.TaskMessage
|
||||||
|
scheduled map[string][]base.Z
|
||||||
|
retry map[string][]base.Z
|
||||||
|
dead map[string][]base.Z
|
||||||
|
qname string // queue to remove
|
||||||
|
force bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pending: map[string][]*base.TaskMessage{
|
||||||
|
"default": {m1, m2},
|
||||||
|
"custom": {},
|
||||||
|
},
|
||||||
|
active: map[string][]*base.TaskMessage{
|
||||||
|
"default": {},
|
||||||
|
"custom": {},
|
||||||
|
},
|
||||||
|
scheduled: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
"custom": {},
|
||||||
|
},
|
||||||
|
retry: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
"custom": {},
|
||||||
|
},
|
||||||
|
dead: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
"custom": {},
|
||||||
|
},
|
||||||
|
qname: "custom",
|
||||||
|
force: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pending: map[string][]*base.TaskMessage{
|
||||||
|
"default": {m1, m2},
|
||||||
|
"custom": {m3},
|
||||||
|
},
|
||||||
|
active: map[string][]*base.TaskMessage{
|
||||||
|
"default": {},
|
||||||
|
"custom": {},
|
||||||
|
},
|
||||||
|
scheduled: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
"custom": {{Message: m4, Score: time.Now().Unix()}},
|
||||||
|
},
|
||||||
|
retry: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
"custom": {},
|
||||||
|
},
|
||||||
|
dead: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
"custom": {},
|
||||||
|
},
|
||||||
|
qname: "custom",
|
||||||
|
force: true, // allow removing non-empty queue
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
h.FlushDB(t, r)
|
||||||
|
h.SeedAllPendingQueues(t, r, tc.pending)
|
||||||
|
h.SeedAllActiveQueues(t, r, tc.active)
|
||||||
|
h.SeedAllScheduledQueues(t, r, tc.scheduled)
|
||||||
|
h.SeedAllRetryQueues(t, r, tc.retry)
|
||||||
|
h.SeedAllDeadQueues(t, r, tc.dead)
|
||||||
|
|
||||||
|
err := inspector.DeleteQueue(tc.qname, tc.force)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("DeleteQueue(%q, %t) = %v, want nil",
|
||||||
|
tc.qname, tc.force, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if r.SIsMember(base.AllQueues, tc.qname).Val() {
|
||||||
|
t.Errorf("%q is a member of %q", tc.qname, base.AllQueues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInspectorDeleteQueueErrorQueueNotEmpty(t *testing.T) {
|
||||||
|
r := setup(t)
|
||||||
|
defer r.Close()
|
||||||
|
inspector := NewInspector(getRedisConnOpt(t))
|
||||||
|
defer inspector.Close()
|
||||||
|
m1 := h.NewTaskMessage("task1", nil)
|
||||||
|
m2 := h.NewTaskMessage("task2", nil)
|
||||||
|
m3 := h.NewTaskMessageWithQueue("task3", nil, "custom")
|
||||||
|
m4 := h.NewTaskMessageWithQueue("task4", nil, "custom")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
pending map[string][]*base.TaskMessage
|
||||||
|
active map[string][]*base.TaskMessage
|
||||||
|
scheduled map[string][]base.Z
|
||||||
|
retry map[string][]base.Z
|
||||||
|
dead map[string][]base.Z
|
||||||
|
qname string // queue to remove
|
||||||
|
force bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pending: map[string][]*base.TaskMessage{
|
||||||
|
"default": {m1, m2},
|
||||||
|
},
|
||||||
|
active: map[string][]*base.TaskMessage{
|
||||||
|
"default": {m3, m4},
|
||||||
|
},
|
||||||
|
scheduled: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
},
|
||||||
|
retry: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
},
|
||||||
|
dead: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
},
|
||||||
|
qname: "default",
|
||||||
|
force: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
h.FlushDB(t, r)
|
||||||
|
h.SeedAllPendingQueues(t, r, tc.pending)
|
||||||
|
h.SeedAllActiveQueues(t, r, tc.active)
|
||||||
|
h.SeedAllScheduledQueues(t, r, tc.scheduled)
|
||||||
|
h.SeedAllRetryQueues(t, r, tc.retry)
|
||||||
|
h.SeedAllDeadQueues(t, r, tc.dead)
|
||||||
|
|
||||||
|
err := inspector.DeleteQueue(tc.qname, tc.force)
|
||||||
|
if _, ok := err.(*ErrQueueNotEmpty); !ok {
|
||||||
|
t.Errorf("DeleteQueue(%v, %t) did not return ErrQueueNotEmpty",
|
||||||
|
tc.qname, tc.force)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInspectorDeleteQueueErrorQueueNotFound(t *testing.T) {
|
||||||
|
r := setup(t)
|
||||||
|
defer r.Close()
|
||||||
|
inspector := NewInspector(getRedisConnOpt(t))
|
||||||
|
defer inspector.Close()
|
||||||
|
m1 := h.NewTaskMessage("task1", nil)
|
||||||
|
m2 := h.NewTaskMessage("task2", nil)
|
||||||
|
m3 := h.NewTaskMessageWithQueue("task3", nil, "custom")
|
||||||
|
m4 := h.NewTaskMessageWithQueue("task4", nil, "custom")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
pending map[string][]*base.TaskMessage
|
||||||
|
active map[string][]*base.TaskMessage
|
||||||
|
scheduled map[string][]base.Z
|
||||||
|
retry map[string][]base.Z
|
||||||
|
dead map[string][]base.Z
|
||||||
|
qname string // queue to remove
|
||||||
|
force bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pending: map[string][]*base.TaskMessage{
|
||||||
|
"default": {m1, m2},
|
||||||
|
},
|
||||||
|
active: map[string][]*base.TaskMessage{
|
||||||
|
"default": {m3, m4},
|
||||||
|
},
|
||||||
|
scheduled: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
},
|
||||||
|
retry: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
},
|
||||||
|
dead: map[string][]base.Z{
|
||||||
|
"default": {},
|
||||||
|
},
|
||||||
|
qname: "nonexistent",
|
||||||
|
force: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
h.FlushDB(t, r)
|
||||||
|
h.SeedAllPendingQueues(t, r, tc.pending)
|
||||||
|
h.SeedAllActiveQueues(t, r, tc.active)
|
||||||
|
h.SeedAllScheduledQueues(t, r, tc.scheduled)
|
||||||
|
h.SeedAllRetryQueues(t, r, tc.retry)
|
||||||
|
h.SeedAllDeadQueues(t, r, tc.dead)
|
||||||
|
|
||||||
|
err := inspector.DeleteQueue(tc.qname, tc.force)
|
||||||
|
if _, ok := err.(*ErrQueueNotFound); !ok {
|
||||||
|
t.Errorf("DeleteQueue(%v, %t) did not return ErrQueueNotFound",
|
||||||
|
tc.qname, tc.force)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestInspectorCurrentStats(t *testing.T) {
|
func TestInspectorCurrentStats(t *testing.T) {
|
||||||
r := setup(t)
|
r := setup(t)
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
@ -66,7 +267,7 @@ func TestInspectorCurrentStats(t *testing.T) {
|
|||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pending map[string][]*base.TaskMessage
|
pending map[string][]*base.TaskMessage
|
||||||
inProgress map[string][]*base.TaskMessage
|
active map[string][]*base.TaskMessage
|
||||||
scheduled map[string][]base.Z
|
scheduled map[string][]base.Z
|
||||||
retry map[string][]base.Z
|
retry map[string][]base.Z
|
||||||
dead map[string][]base.Z
|
dead map[string][]base.Z
|
||||||
@ -81,7 +282,7 @@ func TestInspectorCurrentStats(t *testing.T) {
|
|||||||
"critical": {m5},
|
"critical": {m5},
|
||||||
"low": {m6},
|
"low": {m6},
|
||||||
},
|
},
|
||||||
inProgress: map[string][]*base.TaskMessage{
|
active: map[string][]*base.TaskMessage{
|
||||||
"default": {m2},
|
"default": {m2},
|
||||||
"critical": {},
|
"critical": {},
|
||||||
"low": {},
|
"low": {},
|
||||||
@ -134,7 +335,7 @@ func TestInspectorCurrentStats(t *testing.T) {
|
|||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
asynqtest.FlushDB(t, r)
|
asynqtest.FlushDB(t, r)
|
||||||
asynqtest.SeedAllPendingQueues(t, r, tc.pending)
|
asynqtest.SeedAllPendingQueues(t, r, tc.pending)
|
||||||
asynqtest.SeedAllActiveQueues(t, r, tc.inProgress)
|
asynqtest.SeedAllActiveQueues(t, r, tc.active)
|
||||||
asynqtest.SeedAllScheduledQueues(t, r, tc.scheduled)
|
asynqtest.SeedAllScheduledQueues(t, r, tc.scheduled)
|
||||||
asynqtest.SeedAllRetryQueues(t, r, tc.retry)
|
asynqtest.SeedAllRetryQueues(t, r, tc.retry)
|
||||||
asynqtest.SeedAllDeadQueues(t, r, tc.dead)
|
asynqtest.SeedAllDeadQueues(t, r, tc.dead)
|
||||||
@ -314,13 +515,13 @@ func TestInspectorListActiveTasks(t *testing.T) {
|
|||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
inProgress map[string][]*base.TaskMessage
|
active map[string][]*base.TaskMessage
|
||||||
qname string
|
qname string
|
||||||
want []*ActiveTask
|
want []*ActiveTask
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "with a few active tasks",
|
desc: "with a few active tasks",
|
||||||
inProgress: map[string][]*base.TaskMessage{
|
active: map[string][]*base.TaskMessage{
|
||||||
"default": {m1, m2},
|
"default": {m1, m2},
|
||||||
"custom": {m3, m4},
|
"custom": {m3, m4},
|
||||||
},
|
},
|
||||||
@ -334,7 +535,7 @@ func TestInspectorListActiveTasks(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
asynqtest.FlushDB(t, r)
|
asynqtest.FlushDB(t, r)
|
||||||
asynqtest.SeedAllActiveQueues(t, r, tc.inProgress)
|
asynqtest.SeedAllActiveQueues(t, r, tc.active)
|
||||||
|
|
||||||
got, err := inspector.ListActiveTasks(tc.qname)
|
got, err := inspector.ListActiveTasks(tc.qname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -745,7 +745,6 @@ func (r *RDB) RemoveQueue(qname string, force bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.client.SRem(base.AllQueues, qname).Err()
|
return r.client.SRem(base.AllQueues, qname).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2707,10 +2707,10 @@ func TestRemoveQueue(t *testing.T) {
|
|||||||
|
|
||||||
err := r.RemoveQueue(tc.qname, tc.force)
|
err := r.RemoveQueue(tc.qname, tc.force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("(*RDB).RemoveQueue(%q) = %v, want nil", tc.qname, err)
|
t.Errorf("(*RDB).RemoveQueue(%q, %t) = %v, want nil",
|
||||||
|
tc.qname, tc.force, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.client.SIsMember(base.AllQueues, tc.qname).Val() {
|
if r.client.SIsMember(base.AllQueues, tc.qname).Val() {
|
||||||
t.Errorf("%q is a member of %q", tc.qname, base.AllQueues)
|
t.Errorf("%q is a member of %q", tc.qname, base.AllQueues)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user