runIterationWithRetry wraps runIteration with retry logic for crash recovery.
(ctx context.Context)
| 241 | |
| 242 | // runIterationWithRetry wraps runIteration with retry logic for crash recovery. |
| 243 | func (l *Loop) runIterationWithRetry(ctx context.Context) error { |
| 244 | l.mu.Lock() |
| 245 | config := l.retryConfig |
| 246 | l.mu.Unlock() |
| 247 | |
| 248 | var lastErr error |
| 249 | for attempt := 0; attempt <= config.MaxRetries; attempt++ { |
| 250 | // Check if retry is enabled (except for first attempt) |
| 251 | if attempt > 0 { |
| 252 | if !config.Enabled { |
| 253 | return lastErr |
| 254 | } |
| 255 | |
| 256 | // Get delay for this retry |
| 257 | delayIdx := attempt - 1 |
| 258 | if delayIdx >= len(config.RetryDelays) { |
| 259 | delayIdx = len(config.RetryDelays) - 1 |
| 260 | } |
| 261 | delay := config.RetryDelays[delayIdx] |
| 262 | |
| 263 | // Emit retry event |
| 264 | l.mu.Lock() |
| 265 | iter := l.iteration |
| 266 | l.mu.Unlock() |
| 267 | l.events <- Event{ |
| 268 | Type: EventRetrying, |
| 269 | Iteration: iter, |
| 270 | RetryCount: attempt, |
| 271 | RetryMax: config.MaxRetries, |
| 272 | Text: fmt.Sprintf("%s crashed, retrying (%d/%d)...", l.provider.Name(), attempt, config.MaxRetries), |
| 273 | } |
| 274 | |
| 275 | // Wait before retry |
| 276 | if delay > 0 { |
| 277 | select { |
| 278 | case <-time.After(delay): |
| 279 | case <-ctx.Done(): |
| 280 | return ctx.Err() |
| 281 | } |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | // Check if stopped during delay |
| 286 | l.mu.Lock() |
| 287 | if l.stopped { |
| 288 | l.mu.Unlock() |
| 289 | return nil |
| 290 | } |
| 291 | l.mu.Unlock() |
| 292 | |
| 293 | // Run the iteration |
| 294 | err := l.runIteration(ctx) |
| 295 | if err == nil { |
| 296 | return nil // Success |
| 297 | } |
| 298 | |
| 299 | // Check if this is a context cancellation (don't retry) |
| 300 | if ctx.Err() != nil { |
no test coverage detected