github.com/wfusion/gofusion@v1.1.14/common/infra/asynq/inspector.go (about) 1 // Copyright 2020 Kentaro Hibino. All rights reserved. 2 // Use of this source code is governed by a MIT license 3 // that can be found in the LICENSE file. 4 5 package asynq 6 7 import ( 8 "fmt" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/redis/go-redis/v9" 14 "github.com/wfusion/gofusion/common/infra/asynq/pkg/base" 15 "github.com/wfusion/gofusion/common/infra/asynq/pkg/errors" 16 "github.com/wfusion/gofusion/common/infra/asynq/pkg/rdb" 17 ) 18 19 // Inspector is a client interface to inspect and mutate the state of 20 // queues and tasks. 21 type Inspector struct { 22 rdb *rdb.RDB 23 } 24 25 // NewInspector returns a new instance of Inspector. 26 func NewInspector(r RedisConnOpt) *Inspector { 27 c, ok := r.MakeRedisClient().(redis.UniversalClient) 28 if !ok { 29 panic(fmt.Sprintf("inspeq: unsupported RedisConnOpt type %T", r)) 30 } 31 return &Inspector{ 32 rdb: rdb.NewRDB(c), 33 } 34 } 35 36 // Close closes the connection with redis. 37 func (i *Inspector) Close() error { 38 return i.rdb.Close() 39 } 40 41 // Queues returns a list of all queue names. 42 func (i *Inspector) Queues() ([]string, error) { 43 return i.rdb.AllQueues() 44 } 45 46 // Groups returns a list of all groups within the given queue. 47 func (i *Inspector) Groups(queue string) ([]*GroupInfo, error) { 48 stats, err := i.rdb.GroupStats(queue) 49 if err != nil { 50 return nil, err 51 } 52 var res []*GroupInfo 53 for _, s := range stats { 54 res = append(res, &GroupInfo{ 55 Group: s.Group, 56 Size: s.Size, 57 }) 58 } 59 return res, nil 60 } 61 62 // GroupInfo represents a state of a group at a certain time. 63 type GroupInfo struct { 64 // Name of the group. 65 Group string 66 67 // Size is the total number of tasks in the group. 68 Size int 69 } 70 71 // QueueInfo represents a state of a queue at a certain time. 72 type QueueInfo struct { 73 // Name of the queue. 74 Queue string 75 76 // Total number of bytes that the queue and its tasks require to be stored in redis. 77 // It is an approximate memory usage value in bytes since the value is computed by sampling. 78 MemoryUsage int64 79 80 // Latency of the queue, measured by the oldest pending task in the queue. 81 Latency time.Duration 82 83 // Size is the total number of tasks in the queue. 84 // The value is the sum of Pending, Active, Scheduled, Retry, Aggregating and Archived. 85 Size int 86 87 // Groups is the total number of groups in the queue. 88 Groups int 89 90 // Number of pending tasks. 91 Pending int 92 // Number of active tasks. 93 Active int 94 // Number of scheduled tasks. 95 Scheduled int 96 // Number of retry tasks. 97 Retry int 98 // Number of archived tasks. 99 Archived int 100 // Number of stored completed tasks. 101 Completed int 102 // Number of aggregating tasks. 103 Aggregating int 104 105 // Total number of tasks being processed within the given date (counter resets daily). 106 // The number includes both succeeded and failed tasks. 107 Processed int 108 // Total number of tasks failed to be processed within the given date (counter resets daily). 109 Failed int 110 111 // Total number of tasks processed (cumulative). 112 ProcessedTotal int 113 // Total number of tasks failed (cumulative). 114 FailedTotal int 115 116 // Paused indicates whether the queue is paused. 117 // If true, tasks in the queue will not be processed. 118 Paused bool 119 120 // Time when this queue info snapshot was taken. 121 Timestamp time.Time 122 } 123 124 // GetQueueInfo returns current information of the given queue. 125 func (i *Inspector) GetQueueInfo(queue string) (*QueueInfo, error) { 126 if err := base.ValidateQueueName(queue); err != nil { 127 return nil, err 128 } 129 stats, err := i.rdb.CurrentStats(queue) 130 if err != nil { 131 return nil, err 132 } 133 return &QueueInfo{ 134 Queue: stats.Queue, 135 MemoryUsage: stats.MemoryUsage, 136 Latency: stats.Latency, 137 Size: stats.Size, 138 Groups: stats.Groups, 139 Pending: stats.Pending, 140 Active: stats.Active, 141 Scheduled: stats.Scheduled, 142 Retry: stats.Retry, 143 Archived: stats.Archived, 144 Completed: stats.Completed, 145 Aggregating: stats.Aggregating, 146 Processed: stats.Processed, 147 Failed: stats.Failed, 148 ProcessedTotal: stats.ProcessedTotal, 149 FailedTotal: stats.FailedTotal, 150 Paused: stats.Paused, 151 Timestamp: stats.Timestamp, 152 }, nil 153 } 154 155 // DailyStats holds aggregate data for a given day for a given queue. 156 type DailyStats struct { 157 // Name of the queue. 158 Queue string 159 // Total number of tasks being processed during the given date. 160 // The number includes both succeeded and failed tasks. 161 Processed int 162 // Total number of tasks failed to be processed during the given date. 163 Failed int 164 // Date this stats was taken. 165 Date time.Time 166 } 167 168 // History returns a list of stats from the last n days. 169 func (i *Inspector) History(queue string, n int) ([]*DailyStats, error) { 170 if err := base.ValidateQueueName(queue); err != nil { 171 return nil, err 172 } 173 stats, err := i.rdb.HistoricalStats(queue, n) 174 if err != nil { 175 return nil, err 176 } 177 var res []*DailyStats 178 for _, s := range stats { 179 res = append(res, &DailyStats{ 180 Queue: s.Queue, 181 Processed: s.Processed, 182 Failed: s.Failed, 183 Date: s.Time, 184 }) 185 } 186 return res, nil 187 } 188 189 var ( 190 // ErrQueueNotFound indicates that the specified queue does not exist. 191 ErrQueueNotFound = errors.New("queue not found") 192 193 // ErrQueueNotEmpty indicates that the specified queue is not empty. 194 ErrQueueNotEmpty = errors.New("queue is not empty") 195 196 // ErrTaskNotFound indicates that the specified task cannot be found in the queue. 197 ErrTaskNotFound = errors.New("task not found") 198 ) 199 200 // DeleteQueue removes the specified queue. 201 // 202 // If force is set to true, DeleteQueue will remove the queue regardless of 203 // the queue size as long as no tasks are active in the queue. 204 // If force is set to false, DeleteQueue will remove the queue only if 205 // the queue is empty. 206 // 207 // If the specified queue does not exist, DeleteQueue returns ErrQueueNotFound. 208 // If force is set to false and the specified queue is not empty, DeleteQueue 209 // returns ErrQueueNotEmpty. 210 func (i *Inspector) DeleteQueue(queue string, force bool) error { 211 err := i.rdb.RemoveQueue(queue, force) 212 if errors.IsQueueNotFound(err) { 213 return fmt.Errorf("%w: queue=%q", ErrQueueNotFound, queue) 214 } 215 if errors.IsQueueNotEmpty(err) { 216 return fmt.Errorf("%w: queue=%q", ErrQueueNotEmpty, queue) 217 } 218 return err 219 } 220 221 // GetTaskInfo retrieves task information given a task id and queue name. 222 // 223 // Returns an error wrapping ErrQueueNotFound if a queue with the given name doesn't exist. 224 // Returns an error wrapping ErrTaskNotFound if a task with the given id doesn't exist in the queue. 225 func (i *Inspector) GetTaskInfo(queue, id string) (*TaskInfo, error) { 226 info, err := i.rdb.GetTaskInfo(queue, id) 227 switch { 228 case errors.IsQueueNotFound(err): 229 return nil, fmt.Errorf("asynq: %w", ErrQueueNotFound) 230 case errors.IsTaskNotFound(err): 231 return nil, fmt.Errorf("asynq: %w", ErrTaskNotFound) 232 case err != nil: 233 return nil, fmt.Errorf("asynq: %v", err) 234 } 235 return newTaskInfo(info.Message, info.State, info.NextProcessAt, info.Result), nil 236 } 237 238 // ListOption specifies behavior of list operation. 239 type ListOption any 240 241 // Internal list option representations. 242 type ( 243 pageSizeOpt int 244 pageNumOpt int 245 ) 246 247 type listOption struct { 248 pageSize int 249 pageNum int 250 } 251 252 const ( 253 // Page size used by default in list operation. 254 defaultPageSize = 30 255 256 // Page number used by default in list operation. 257 defaultPageNum = 1 258 ) 259 260 func composeListOptions(opts ...ListOption) listOption { 261 res := listOption{ 262 pageSize: defaultPageSize, 263 pageNum: defaultPageNum, 264 } 265 for _, opt := range opts { 266 switch opt := opt.(type) { 267 case pageSizeOpt: 268 res.pageSize = int(opt) 269 case pageNumOpt: 270 res.pageNum = int(opt) 271 default: 272 // ignore unexpected option 273 } 274 } 275 return res 276 } 277 278 // PageSize returns an option to specify the page size for list operation. 279 // 280 // Negative page size is treated as zero. 281 func PageSize(n int) ListOption { 282 if n < 0 { 283 n = 0 284 } 285 return pageSizeOpt(n) 286 } 287 288 // Page returns an option to specify the page number for list operation. 289 // The value 1 fetches the first page. 290 // 291 // Negative page number is treated as one. 292 func Page(n int) ListOption { 293 if n < 0 { 294 n = 1 295 } 296 return pageNumOpt(n) 297 } 298 299 // ListPendingTasks retrieves pending tasks from the specified queue. 300 // 301 // By default, it retrieves the first 30 tasks. 302 func (i *Inspector) ListPendingTasks(queue string, opts ...ListOption) ([]*TaskInfo, error) { 303 if err := base.ValidateQueueName(queue); err != nil { 304 return nil, fmt.Errorf("asynq: %v", err) 305 } 306 opt := composeListOptions(opts...) 307 pgn := rdb.Pagination{Size: opt.pageSize, Page: opt.pageNum - 1} 308 infos, err := i.rdb.ListPending(queue, pgn) 309 switch { 310 case errors.IsQueueNotFound(err): 311 return nil, fmt.Errorf("asynq: %w", ErrQueueNotFound) 312 case err != nil: 313 return nil, fmt.Errorf("asynq: %v", err) 314 } 315 var tasks []*TaskInfo 316 for _, i := range infos { 317 tasks = append(tasks, newTaskInfo( 318 i.Message, 319 i.State, 320 i.NextProcessAt, 321 i.Result, 322 )) 323 } 324 return tasks, err 325 } 326 327 // ListActiveTasks retrieves active tasks from the specified queue. 328 // 329 // By default, it retrieves the first 30 tasks. 330 func (i *Inspector) ListActiveTasks(queue string, opts ...ListOption) ([]*TaskInfo, error) { 331 if err := base.ValidateQueueName(queue); err != nil { 332 return nil, fmt.Errorf("asynq: %v", err) 333 } 334 opt := composeListOptions(opts...) 335 pgn := rdb.Pagination{Size: opt.pageSize, Page: opt.pageNum - 1} 336 infos, err := i.rdb.ListActive(queue, pgn) 337 switch { 338 case errors.IsQueueNotFound(err): 339 return nil, fmt.Errorf("asynq: %w", ErrQueueNotFound) 340 case err != nil: 341 return nil, fmt.Errorf("asynq: %v", err) 342 } 343 expired, err := i.rdb.ListLeaseExpired(time.Now(), queue) 344 if err != nil { 345 return nil, fmt.Errorf("asynq: %v", err) 346 } 347 expiredSet := make(map[string]struct{}) // set of expired message IDs 348 for _, msg := range expired { 349 expiredSet[msg.ID] = struct{}{} 350 } 351 var tasks []*TaskInfo 352 for _, i := range infos { 353 t := newTaskInfo( 354 i.Message, 355 i.State, 356 i.NextProcessAt, 357 i.Result, 358 ) 359 if _, ok := expiredSet[i.Message.ID]; ok { 360 t.IsOrphaned = true 361 } 362 tasks = append(tasks, t) 363 } 364 return tasks, nil 365 } 366 367 // ListAggregatingTasks retrieves scheduled tasks from the specified group. 368 // 369 // By default, it retrieves the first 30 tasks. 370 func (i *Inspector) ListAggregatingTasks(queue, group string, opts ...ListOption) ([]*TaskInfo, error) { 371 if err := base.ValidateQueueName(queue); err != nil { 372 return nil, fmt.Errorf("asynq: %v", err) 373 } 374 opt := composeListOptions(opts...) 375 pgn := rdb.Pagination{Size: opt.pageSize, Page: opt.pageNum - 1} 376 infos, err := i.rdb.ListAggregating(queue, group, pgn) 377 switch { 378 case errors.IsQueueNotFound(err): 379 return nil, fmt.Errorf("asynq: %w", ErrQueueNotFound) 380 case err != nil: 381 return nil, fmt.Errorf("asynq: %v", err) 382 } 383 var tasks []*TaskInfo 384 for _, i := range infos { 385 tasks = append(tasks, newTaskInfo( 386 i.Message, 387 i.State, 388 i.NextProcessAt, 389 i.Result, 390 )) 391 } 392 return tasks, nil 393 } 394 395 // ListScheduledTasks retrieves scheduled tasks from the specified queue. 396 // Tasks are sorted by NextProcessAt in ascending order. 397 // 398 // By default, it retrieves the first 30 tasks. 399 func (i *Inspector) ListScheduledTasks(queue string, opts ...ListOption) ([]*TaskInfo, error) { 400 if err := base.ValidateQueueName(queue); err != nil { 401 return nil, fmt.Errorf("asynq: %v", err) 402 } 403 opt := composeListOptions(opts...) 404 pgn := rdb.Pagination{Size: opt.pageSize, Page: opt.pageNum - 1} 405 infos, err := i.rdb.ListScheduled(queue, pgn) 406 switch { 407 case errors.IsQueueNotFound(err): 408 return nil, fmt.Errorf("asynq: %w", ErrQueueNotFound) 409 case err != nil: 410 return nil, fmt.Errorf("asynq: %v", err) 411 } 412 var tasks []*TaskInfo 413 for _, i := range infos { 414 tasks = append(tasks, newTaskInfo( 415 i.Message, 416 i.State, 417 i.NextProcessAt, 418 i.Result, 419 )) 420 } 421 return tasks, nil 422 } 423 424 // ListRetryTasks retrieves retry tasks from the specified queue. 425 // Tasks are sorted by NextProcessAt in ascending order. 426 // 427 // By default, it retrieves the first 30 tasks. 428 func (i *Inspector) ListRetryTasks(queue string, opts ...ListOption) ([]*TaskInfo, error) { 429 if err := base.ValidateQueueName(queue); err != nil { 430 return nil, fmt.Errorf("asynq: %v", err) 431 } 432 opt := composeListOptions(opts...) 433 pgn := rdb.Pagination{Size: opt.pageSize, Page: opt.pageNum - 1} 434 infos, err := i.rdb.ListRetry(queue, pgn) 435 switch { 436 case errors.IsQueueNotFound(err): 437 return nil, fmt.Errorf("asynq: %w", ErrQueueNotFound) 438 case err != nil: 439 return nil, fmt.Errorf("asynq: %v", err) 440 } 441 var tasks []*TaskInfo 442 for _, i := range infos { 443 tasks = append(tasks, newTaskInfo( 444 i.Message, 445 i.State, 446 i.NextProcessAt, 447 i.Result, 448 )) 449 } 450 return tasks, nil 451 } 452 453 // ListArchivedTasks retrieves archived tasks from the specified queue. 454 // Tasks are sorted by LastFailedAt in descending order. 455 // 456 // By default, it retrieves the first 30 tasks. 457 func (i *Inspector) ListArchivedTasks(queue string, opts ...ListOption) ([]*TaskInfo, error) { 458 if err := base.ValidateQueueName(queue); err != nil { 459 return nil, fmt.Errorf("asynq: %v", err) 460 } 461 opt := composeListOptions(opts...) 462 pgn := rdb.Pagination{Size: opt.pageSize, Page: opt.pageNum - 1} 463 infos, err := i.rdb.ListArchived(queue, pgn) 464 switch { 465 case errors.IsQueueNotFound(err): 466 return nil, fmt.Errorf("asynq: %w", ErrQueueNotFound) 467 case err != nil: 468 return nil, fmt.Errorf("asynq: %v", err) 469 } 470 var tasks []*TaskInfo 471 for _, i := range infos { 472 tasks = append(tasks, newTaskInfo( 473 i.Message, 474 i.State, 475 i.NextProcessAt, 476 i.Result, 477 )) 478 } 479 return tasks, nil 480 } 481 482 // ListCompletedTasks retrieves completed tasks from the specified queue. 483 // Tasks are sorted by expiration time (i.e. CompletedAt + Retention) in descending order. 484 // 485 // By default, it retrieves the first 30 tasks. 486 func (i *Inspector) ListCompletedTasks(queue string, opts ...ListOption) ([]*TaskInfo, error) { 487 if err := base.ValidateQueueName(queue); err != nil { 488 return nil, fmt.Errorf("asynq: %v", err) 489 } 490 opt := composeListOptions(opts...) 491 pgn := rdb.Pagination{Size: opt.pageSize, Page: opt.pageNum - 1} 492 infos, err := i.rdb.ListCompleted(queue, pgn) 493 switch { 494 case errors.IsQueueNotFound(err): 495 return nil, fmt.Errorf("asynq: %w", ErrQueueNotFound) 496 case err != nil: 497 return nil, fmt.Errorf("asynq: %v", err) 498 } 499 var tasks []*TaskInfo 500 for _, i := range infos { 501 tasks = append(tasks, newTaskInfo( 502 i.Message, 503 i.State, 504 i.NextProcessAt, 505 i.Result, 506 )) 507 } 508 return tasks, nil 509 } 510 511 // DeleteAllPendingTasks deletes all pending tasks from the specified queue, 512 // and reports the number tasks deleted. 513 func (i *Inspector) DeleteAllPendingTasks(queue string) (int, error) { 514 if err := base.ValidateQueueName(queue); err != nil { 515 return 0, err 516 } 517 n, err := i.rdb.DeleteAllPendingTasks(queue) 518 return int(n), err 519 } 520 521 // DeleteAllScheduledTasks deletes all scheduled tasks from the specified queue, 522 // and reports the number tasks deleted. 523 func (i *Inspector) DeleteAllScheduledTasks(queue string) (int, error) { 524 if err := base.ValidateQueueName(queue); err != nil { 525 return 0, err 526 } 527 n, err := i.rdb.DeleteAllScheduledTasks(queue) 528 return int(n), err 529 } 530 531 // DeleteAllRetryTasks deletes all retry tasks from the specified queue, 532 // and reports the number tasks deleted. 533 func (i *Inspector) DeleteAllRetryTasks(queue string) (int, error) { 534 if err := base.ValidateQueueName(queue); err != nil { 535 return 0, err 536 } 537 n, err := i.rdb.DeleteAllRetryTasks(queue) 538 return int(n), err 539 } 540 541 // DeleteAllArchivedTasks deletes all archived tasks from the specified queue, 542 // and reports the number tasks deleted. 543 func (i *Inspector) DeleteAllArchivedTasks(queue string) (int, error) { 544 if err := base.ValidateQueueName(queue); err != nil { 545 return 0, err 546 } 547 n, err := i.rdb.DeleteAllArchivedTasks(queue) 548 return int(n), err 549 } 550 551 // DeleteAllCompletedTasks deletes all completed tasks from the specified queue, 552 // and reports the number tasks deleted. 553 func (i *Inspector) DeleteAllCompletedTasks(queue string) (int, error) { 554 if err := base.ValidateQueueName(queue); err != nil { 555 return 0, err 556 } 557 n, err := i.rdb.DeleteAllCompletedTasks(queue) 558 return int(n), err 559 } 560 561 // DeleteAllAggregatingTasks deletes all tasks from the specified group, 562 // and reports the number of tasks deleted. 563 func (i *Inspector) DeleteAllAggregatingTasks(queue, group string) (int, error) { 564 if err := base.ValidateQueueName(queue); err != nil { 565 return 0, err 566 } 567 n, err := i.rdb.DeleteAllAggregatingTasks(queue, group) 568 return int(n), err 569 } 570 571 // DeleteTask deletes a task with the given id from the given queue. 572 // The task needs to be in pending, scheduled, retry, or archived state, 573 // otherwise DeleteTask will return an error. 574 // 575 // If a queue with the given name doesn't exist, it returns an error wrapping ErrQueueNotFound. 576 // If a task with the given id doesn't exist in the queue, it returns an error wrapping ErrTaskNotFound. 577 // If the task is in active state, it returns a non-nil error. 578 func (i *Inspector) DeleteTask(queue, id string) error { 579 if err := base.ValidateQueueName(queue); err != nil { 580 return fmt.Errorf("asynq: %v", err) 581 } 582 err := i.rdb.DeleteTask(queue, id) 583 switch { 584 case errors.IsQueueNotFound(err): 585 return fmt.Errorf("asynq: %w", ErrQueueNotFound) 586 case errors.IsTaskNotFound(err): 587 return fmt.Errorf("asynq: %w", ErrTaskNotFound) 588 case err != nil: 589 return fmt.Errorf("asynq: %v", err) 590 } 591 return nil 592 593 } 594 595 // RunAllScheduledTasks schedules all scheduled tasks from the given queue to run, 596 // and reports the number of tasks scheduled to run. 597 func (i *Inspector) RunAllScheduledTasks(queue string) (int, error) { 598 if err := base.ValidateQueueName(queue); err != nil { 599 return 0, err 600 } 601 n, err := i.rdb.RunAllScheduledTasks(queue) 602 return int(n), err 603 } 604 605 // RunAllRetryTasks schedules all retry tasks from the given queue to run, 606 // and reports the number of tasks scheduled to run. 607 func (i *Inspector) RunAllRetryTasks(queue string) (int, error) { 608 if err := base.ValidateQueueName(queue); err != nil { 609 return 0, err 610 } 611 n, err := i.rdb.RunAllRetryTasks(queue) 612 return int(n), err 613 } 614 615 // RunAllArchivedTasks schedules all archived tasks from the given queue to run, 616 // and reports the number of tasks scheduled to run. 617 func (i *Inspector) RunAllArchivedTasks(queue string) (int, error) { 618 if err := base.ValidateQueueName(queue); err != nil { 619 return 0, err 620 } 621 n, err := i.rdb.RunAllArchivedTasks(queue) 622 return int(n), err 623 } 624 625 // RunAllAggregatingTasks schedules all tasks from the given grou to run. 626 // and reports the number of tasks scheduled to run. 627 func (i *Inspector) RunAllAggregatingTasks(queue, group string) (int, error) { 628 if err := base.ValidateQueueName(queue); err != nil { 629 return 0, err 630 } 631 n, err := i.rdb.RunAllAggregatingTasks(queue, group) 632 return int(n), err 633 } 634 635 // RunTask updates the task to pending state given a queue name and task id. 636 // The task needs to be in scheduled, retry, or archived state, otherwise RunTask 637 // will return an error. 638 // 639 // If a queue with the given name doesn't exist, it returns an error wrapping ErrQueueNotFound. 640 // If a task with the given id doesn't exist in the queue, it returns an error wrapping ErrTaskNotFound. 641 // If the task is in pending or active state, it returns a non-nil error. 642 func (i *Inspector) RunTask(queue, id string) error { 643 if err := base.ValidateQueueName(queue); err != nil { 644 return fmt.Errorf("asynq: %v", err) 645 } 646 err := i.rdb.RunTask(queue, id) 647 switch { 648 case errors.IsQueueNotFound(err): 649 return fmt.Errorf("asynq: %w", ErrQueueNotFound) 650 case errors.IsTaskNotFound(err): 651 return fmt.Errorf("asynq: %w", ErrTaskNotFound) 652 case err != nil: 653 return fmt.Errorf("asynq: %v", err) 654 } 655 return nil 656 } 657 658 // ArchiveAllPendingTasks archives all pending tasks from the given queue, 659 // and reports the number of tasks archived. 660 func (i *Inspector) ArchiveAllPendingTasks(queue string) (int, error) { 661 if err := base.ValidateQueueName(queue); err != nil { 662 return 0, err 663 } 664 n, err := i.rdb.ArchiveAllPendingTasks(queue) 665 return int(n), err 666 } 667 668 // ArchiveAllScheduledTasks archives all scheduled tasks from the given queue, 669 // and reports the number of tasks archiveed. 670 func (i *Inspector) ArchiveAllScheduledTasks(queue string) (int, error) { 671 if err := base.ValidateQueueName(queue); err != nil { 672 return 0, err 673 } 674 n, err := i.rdb.ArchiveAllScheduledTasks(queue) 675 return int(n), err 676 } 677 678 // ArchiveAllRetryTasks archives all retry tasks from the given queue, 679 // and reports the number of tasks archiveed. 680 func (i *Inspector) ArchiveAllRetryTasks(queue string) (int, error) { 681 if err := base.ValidateQueueName(queue); err != nil { 682 return 0, err 683 } 684 n, err := i.rdb.ArchiveAllRetryTasks(queue) 685 return int(n), err 686 } 687 688 // ArchiveAllAggregatingTasks archives all tasks from the given group, 689 // and reports the number of tasks archived. 690 func (i *Inspector) ArchiveAllAggregatingTasks(queue, group string) (int, error) { 691 if err := base.ValidateQueueName(queue); err != nil { 692 return 0, err 693 } 694 n, err := i.rdb.ArchiveAllAggregatingTasks(queue, group) 695 return int(n), err 696 } 697 698 // ArchiveTask archives a task with the given id in the given queue. 699 // The task needs to be in pending, scheduled, or retry state, otherwise ArchiveTask 700 // will return an error. 701 // 702 // If a queue with the given name doesn't exist, it returns an error wrapping ErrQueueNotFound. 703 // If a task with the given id doesn't exist in the queue, it returns an error wrapping ErrTaskNotFound. 704 // If the task is in already archived, it returns a non-nil error. 705 func (i *Inspector) ArchiveTask(queue, id string) error { 706 if err := base.ValidateQueueName(queue); err != nil { 707 return fmt.Errorf("asynq: err") 708 } 709 err := i.rdb.ArchiveTask(queue, id) 710 switch { 711 case errors.IsQueueNotFound(err): 712 return fmt.Errorf("asynq: %w", ErrQueueNotFound) 713 case errors.IsTaskNotFound(err): 714 return fmt.Errorf("asynq: %w", ErrTaskNotFound) 715 case err != nil: 716 return fmt.Errorf("asynq: %v", err) 717 } 718 return nil 719 } 720 721 // CancelProcessing sends a signal to cancel processing of the task 722 // given a task id. CancelProcessing is best-effort, which means that it does not 723 // guarantee that the task with the given id will be canceled. The return 724 // value only indicates whether the cancelation signal has been sent. 725 func (i *Inspector) CancelProcessing(id string) error { 726 return i.rdb.PublishCancelation(id) 727 } 728 729 // PauseQueue pauses task processing on the specified queue. 730 // If the queue is already paused, it will return a non-nil error. 731 func (i *Inspector) PauseQueue(queue string) error { 732 if err := base.ValidateQueueName(queue); err != nil { 733 return err 734 } 735 return i.rdb.Pause(queue) 736 } 737 738 // UnpauseQueue resumes task processing on the specified queue. 739 // If the queue is not paused, it will return a non-nil error. 740 func (i *Inspector) UnpauseQueue(queue string) error { 741 if err := base.ValidateQueueName(queue); err != nil { 742 return err 743 } 744 return i.rdb.Unpause(queue) 745 } 746 747 // Servers return a list of running servers' information. 748 func (i *Inspector) Servers() ([]*ServerInfo, error) { 749 servers, err := i.rdb.ListServers() 750 if err != nil { 751 return nil, err 752 } 753 workers, err := i.rdb.ListWorkers() 754 if err != nil { 755 return nil, err 756 } 757 m := make(map[string]*ServerInfo) // ServerInfo keyed by serverID 758 for _, s := range servers { 759 m[s.ServerID] = &ServerInfo{ 760 ID: s.ServerID, 761 Host: s.Host, 762 PID: s.PID, 763 Concurrency: s.Concurrency, 764 Queues: s.Queues, 765 StrictPriority: s.StrictPriority, 766 Started: s.Started, 767 Status: s.Status, 768 ActiveWorkers: make([]*WorkerInfo, 0), 769 } 770 } 771 for _, w := range workers { 772 srvInfo, ok := m[w.ServerID] 773 if !ok { 774 continue 775 } 776 wrkInfo := &WorkerInfo{ 777 TaskID: w.ID, 778 TaskType: w.Type, 779 TaskPayload: w.Payload, 780 Queue: w.Queue, 781 Started: w.Started, 782 Deadline: w.Deadline, 783 } 784 srvInfo.ActiveWorkers = append(srvInfo.ActiveWorkers, wrkInfo) 785 } 786 var out []*ServerInfo 787 for _, srvInfo := range m { 788 out = append(out, srvInfo) 789 } 790 return out, nil 791 } 792 793 // ServerInfo describes a running Server instance. 794 type ServerInfo struct { 795 // Unique Identifier for the server. 796 ID string 797 // Host machine on which the server is running. 798 Host string 799 // PID of the process in which the server is running. 800 PID int 801 802 // Server configuration details. 803 // See Config doc for field descriptions. 804 Concurrency int 805 Queues map[string]int 806 StrictPriority bool 807 808 // Time the server started. 809 Started time.Time 810 // Status indicates the status of the server. 811 // TODO: Update comment with more details. 812 Status string 813 // A List of active workers currently processing tasks. 814 ActiveWorkers []*WorkerInfo 815 } 816 817 // WorkerInfo describes a running worker processing a task. 818 type WorkerInfo struct { 819 // ID of the task the worker is processing. 820 TaskID string 821 // Type of the task the worker is processing. 822 TaskType string 823 // Payload of the task the worker is processing. 824 TaskPayload []byte 825 // Queue from which the worker got its task. 826 Queue string 827 // Time the worker started processing the task. 828 Started time.Time 829 // Time the worker needs to finish processing the task by. 830 Deadline time.Time 831 } 832 833 // ClusterKeySlot returns an integer identifying the hash slot the given queue hashes to. 834 func (i *Inspector) ClusterKeySlot(queue string) (int64, error) { 835 return i.rdb.ClusterKeySlot(queue) 836 } 837 838 // ClusterNode describes a node in redis cluster. 839 type ClusterNode struct { 840 // Node ID in the cluster. 841 ID string 842 843 // Address of the node. 844 Addr string 845 } 846 847 // ClusterNodes returns a list of nodes the given queue belongs to. 848 // 849 // Only relevant if task queues are stored in redis cluster. 850 func (i *Inspector) ClusterNodes(queue string) ([]*ClusterNode, error) { 851 nodes, err := i.rdb.ClusterNodes(queue) 852 if err != nil { 853 return nil, err 854 } 855 var res []*ClusterNode 856 for _, node := range nodes { 857 res = append(res, &ClusterNode{ID: node.ID, Addr: node.Addr}) 858 } 859 return res, nil 860 } 861 862 // SchedulerEntry holds information about a periodic task registered with a scheduler. 863 type SchedulerEntry struct { 864 // Identifier of this entry. 865 ID string 866 867 // Spec describes the schedule of this entry. 868 Spec string 869 870 // Periodic Task registered for this entry. 871 Task *Task 872 873 // Opts is the options for the periodic task. 874 Opts []Option 875 876 // Next shows the next time the task will be enqueued. 877 Next time.Time 878 879 // Prev shows the last time the task was enqueued. 880 // Zero time if task was never enqueued. 881 Prev time.Time 882 } 883 884 // SchedulerEntries returns a list of all entries registered with 885 // currently running schedulers. 886 func (i *Inspector) SchedulerEntries() ([]*SchedulerEntry, error) { 887 var entries []*SchedulerEntry 888 res, err := i.rdb.ListSchedulerEntries() 889 if err != nil { 890 return nil, err 891 } 892 for _, e := range res { 893 task := NewTask(e.Type, e.Payload) 894 var opts []Option 895 for _, s := range e.Opts { 896 if o, err := parseOption(s); err == nil { 897 // ignore bad data 898 opts = append(opts, o) 899 } 900 } 901 entries = append(entries, &SchedulerEntry{ 902 ID: e.ID, 903 Spec: e.Spec, 904 Task: task, 905 Opts: opts, 906 Next: e.Next, 907 Prev: e.Prev, 908 }) 909 } 910 return entries, nil 911 } 912 913 // parseOption interprets a string s as an Option and returns the Option if parsing is successful, 914 // otherwise returns non-nil error. 915 func parseOption(s string) (Option, error) { 916 fn, arg := parseOptionFunc(s), parseOptionArg(s) 917 switch fn { 918 case "Queue": 919 queue, err := strconv.Unquote(arg) 920 if err != nil { 921 return nil, err 922 } 923 return Queue(queue), nil 924 case "MaxRetry": 925 n, err := strconv.Atoi(arg) 926 if err != nil { 927 return nil, err 928 } 929 return MaxRetry(n), nil 930 case "Timeout": 931 d, err := time.ParseDuration(arg) 932 if err != nil { 933 return nil, err 934 } 935 return Timeout(d), nil 936 case "Deadline": 937 t, err := time.Parse(time.UnixDate, arg) 938 if err != nil { 939 return nil, err 940 } 941 return Deadline(t), nil 942 case "Unique": 943 d, err := time.ParseDuration(arg) 944 if err != nil { 945 return nil, err 946 } 947 return Unique(d), nil 948 case "ProcessAt": 949 t, err := time.Parse(time.UnixDate, arg) 950 if err != nil { 951 return nil, err 952 } 953 return ProcessAt(t), nil 954 case "ProcessIn": 955 d, err := time.ParseDuration(arg) 956 if err != nil { 957 return nil, err 958 } 959 return ProcessIn(d), nil 960 case "Retention": 961 d, err := time.ParseDuration(arg) 962 if err != nil { 963 return nil, err 964 } 965 return Retention(d), nil 966 default: 967 return nil, fmt.Errorf("cannot not parse option string %q", s) 968 } 969 } 970 971 func parseOptionFunc(s string) string { 972 i := strings.Index(s, "(") 973 return s[:i] 974 } 975 976 func parseOptionArg(s string) string { 977 i := strings.Index(s, "(") 978 if i >= 0 { 979 j := strings.Index(s, ")") 980 if j > i { 981 return s[i+1 : j] 982 } 983 } 984 return "" 985 } 986 987 // SchedulerEnqueueEvent holds information about an enqueue event by a scheduler. 988 type SchedulerEnqueueEvent struct { 989 // ID of the task that was enqueued. 990 TaskID string 991 992 // Time the task was enqueued. 993 EnqueuedAt time.Time 994 } 995 996 // ListSchedulerEnqueueEvents retrieves a list of enqueue events from the specified scheduler entry. 997 // 998 // By default, it retrieves the first 30 tasks. 999 func (i *Inspector) ListSchedulerEnqueueEvents(entryID string, opts ...ListOption) ([]*SchedulerEnqueueEvent, error) { 1000 opt := composeListOptions(opts...) 1001 pgn := rdb.Pagination{Size: opt.pageSize, Page: opt.pageNum - 1} 1002 data, err := i.rdb.ListSchedulerEnqueueEvents(entryID, pgn) 1003 if err != nil { 1004 return nil, err 1005 } 1006 var events []*SchedulerEnqueueEvent 1007 for _, e := range data { 1008 events = append(events, &SchedulerEnqueueEvent{TaskID: e.TaskID, EnqueuedAt: e.EnqueuedAt}) 1009 } 1010 return events, nil 1011 }