(ctx context.Context)
| 754 | } |
| 755 | |
| 756 | func (q *TaskQueue) Start(ctx context.Context) { |
| 757 | if q.started.Load() { |
| 758 | return |
| 759 | } |
| 760 | |
| 761 | if q.Handler == nil { |
| 762 | log.Error("should set handler before start in queue", slog.String(pkg.LogKeyName, q.Name)) |
| 763 | q.SetStatus(QueueStatusNoHandlerSet) |
| 764 | return |
| 765 | } |
| 766 | |
| 767 | // Start metrics monitoring goroutine |
| 768 | q.startMetricsMonitoring() |
| 769 | |
| 770 | go func() { |
| 771 | // Outer recover is a last-resort safety net: it prevents the whole worker |
| 772 | // goroutine from being torn down if a panic escapes processOne (e.g. from |
| 773 | // q.compaction or q.waitForTask). The main per-task recovery happens |
| 774 | // inside processOne so that the for-loop keeps running after a panic. |
| 775 | defer func() { |
| 776 | if r := recover(); r != nil { |
| 777 | q.logger.Error("panic escaped TaskQueue worker, queue stopped", |
| 778 | slog.String(pkg.LogKeyQueue, q.Name), |
| 779 | slog.Any(pkg.LogKeyError, r), |
| 780 | slog.String(pkg.LogKeyStack, string(debug.Stack())), |
| 781 | ) |
| 782 | q.SetStatus(QueueStatusStop) |
| 783 | } |
| 784 | }() |
| 785 | |
| 786 | q.SetStatus(QueueStatusIdle) |
| 787 | var sleepDelay time.Duration |
| 788 | for { |
| 789 | q.logger.Debug("queue: wait for task", slog.String(pkg.LogKeyQueue, q.Name), slog.Duration(pkg.LogKeySleepDelay, sleepDelay)) |
| 790 | t := q.waitForTask(sleepDelay) |
| 791 | if t == nil { |
| 792 | q.SetStatus(QueueStatusStop) |
| 793 | q.logger.Info("queue stopped", slog.String(pkg.LogKeyName, q.Name)) |
| 794 | return |
| 795 | } |
| 796 | |
| 797 | nextSleepDelay, stop := q.processOne(ctx, t) |
| 798 | if stop { |
| 799 | return |
| 800 | } |
| 801 | sleepDelay = nextSleepDelay |
| 802 | } |
| 803 | }() |
| 804 | |
| 805 | q.started.Store(true) |
| 806 | } |
| 807 | |
| 808 | // processOne processes a single task fetched from the queue. |
| 809 | // It contains its own recover() so a panic inside q.Handler does NOT kill |
nothing calls this directly
no test coverage detected