RefreshLoop re-registers to mint a fresh JWT before the current one expires, updating the credentials returned by Current/Provider so the NATS connection adopts them on its next reconnect. It returns nil when ctx is cancelled or when the current credential has no expiry (nothing to refresh), and a n
(ctx context.Context)
| 159 | // exit the worker so it restarts and re-acquires (or surfaces the outage) |
| 160 | // rather than silently drifting toward an expired, unrenewable JWT. |
| 161 | func (m *NATSCredentialManager) RefreshLoop(ctx context.Context) error { |
| 162 | failures := 0 |
| 163 | for { |
| 164 | jwt, _ := m.Current() |
| 165 | exp, ok := m.expiryOf(jwt) |
| 166 | if !ok { |
| 167 | xlog.Debug("NATS credential has no expiry; refresh loop exiting") |
| 168 | return nil |
| 169 | } |
| 170 | wait := max(time.Duration(float64(time.Until(exp))*m.refreshLead), 0) |
| 171 | select { |
| 172 | case <-ctx.Done(): |
| 173 | return nil |
| 174 | case <-time.After(wait): |
| 175 | } |
| 176 | |
| 177 | res, err := m.register(ctx) |
| 178 | if err == nil && res.NatsJWT != "" && res.NatsUserSeed != "" { |
| 179 | m.store(res) |
| 180 | failures = 0 |
| 181 | xlog.Info("Refreshed NATS credentials", "node", res.ID) |
| 182 | continue |
| 183 | } |
| 184 | failures++ |
| 185 | if err != nil { |
| 186 | xlog.Warn("NATS credential refresh failed; will retry", "attempt", failures, "error", err) |
| 187 | } else { |
| 188 | xlog.Warn("NATS credential refresh returned no credentials; will retry", "attempt", failures) |
| 189 | } |
| 190 | if m.maxAttempts > 0 && failures >= m.maxAttempts { |
| 191 | return fmt.Errorf("NATS credential refresh failed %d times in a row", failures) |
| 192 | } |
| 193 | // Back off before retrying so a persistent failure near expiry does not spin. |
| 194 | select { |
| 195 | case <-ctx.Done(): |
| 196 | return nil |
| 197 | case <-time.After(m.refreshRetry): |
| 198 | } |
| 199 | } |
| 200 | } |