tryRestart loops with backoff until reconnect succeeds, ctx is cancelled, or the budget is exhausted. Returns true on success (state → Ready), false otherwise (state → Failed or Stopped).
(ctx context.Context)
| 589 | // or the budget is exhausted. Returns true on success (state → Ready), |
| 590 | // false otherwise (state → Failed or Stopped). |
| 591 | func (s *Supervisor) tryRestart(ctx context.Context) bool { |
| 592 | maxAttempts := s.policy.maxAttempts() |
| 593 | log := s.policy.logger() |
| 594 | |
| 595 | for attempt := 0; ; attempt++ { |
| 596 | if maxAttempts > 0 && attempt >= maxAttempts { |
| 597 | lastErr := s.tracker.LastError() |
| 598 | log.Error("supervisor: giving up after max attempts", "name", s.name, "attempts", attempt) |
| 599 | s.tracker.Fail(StateFailed, lastErr) |
| 600 | if cb := s.policy.OnFailed; cb != nil { |
| 601 | cb(lastErr) |
| 602 | } |
| 603 | s.signalDone() |
| 604 | return false |
| 605 | } |
| 606 | |
| 607 | delay := s.policy.Backoff.delay(attempt, s.randFloat) |
| 608 | log.Debug("supervisor: restart attempt", "name", s.name, "attempt", attempt+1, "backoff", delay) |
| 609 | |
| 610 | timer := time.NewTimer(delay) |
| 611 | select { |
| 612 | case <-timer.C: |
| 613 | case <-ctx.Done(): |
| 614 | timer.Stop() |
| 615 | return false |
| 616 | } |
| 617 | |
| 618 | s.mu.Lock() |
| 619 | if s.stopping { |
| 620 | s.mu.Unlock() |
| 621 | return false |
| 622 | } |
| 623 | s.mu.Unlock() |
| 624 | |
| 625 | sess, err := s.connector.Connect(withBackgroundReconnect(ctx)) |
| 626 | if err != nil { |
| 627 | // A permanent error on reconnect (e.g. ErrAuthRequired from a |
| 628 | // server-side invalid_token) must not be retried: doing so would |
| 629 | // burn through the budget and mask the real failure. Symmetric |
| 630 | // with the shouldRestart check on the Wait() path. |
| 631 | if IsPermanent(err) { |
| 632 | log.Warn("supervisor: permanent error on reconnect; not retrying", "name", s.name, "error", err) |
| 633 | s.tracker.Fail(StateFailed, err) |
| 634 | if cb := s.policy.OnFailed; cb != nil { |
| 635 | cb(err) |
| 636 | } |
| 637 | s.signalDone() |
| 638 | return false |
| 639 | } |
| 640 | s.tracker.Fail(StateRestarting, err) |
| 641 | s.tracker.IncRestarts() |
| 642 | log.Warn("supervisor: restart failed", "name", s.name, "attempt", attempt+1, "error", err) |
| 643 | continue |
| 644 | } |
| 645 | |
| 646 | s.mu.Lock() |
| 647 | if s.stopping { |
| 648 | s.mu.Unlock() |
no test coverage detected