doRequestWithRetry performs an HTTP request with retry logic.
(ctx context.Context, method, path string, data interface{}, result *map[string]interface{}, maxRetries int)
| 73 | |
| 74 | // doRequestWithRetry performs an HTTP request with retry logic. |
| 75 | func (hc *HTTPClient) doRequestWithRetry(ctx context.Context, method, path string, data interface{}, result *map[string]interface{}, maxRetries int) error { |
| 76 | var lastErr error |
| 77 | |
| 78 | for attempt := 1; attempt <= maxRetries; attempt++ { |
| 79 | if attempt > 1 { |
| 80 | // Exponential backoff |
| 81 | backoff := time.Duration(attempt-1) * 500 * time.Millisecond |
| 82 | hc.logger.Debug("Retrying request after %v (attempt %d/%d)", backoff, attempt, maxRetries) |
| 83 | |
| 84 | select { |
| 85 | case <-time.After(backoff): |
| 86 | case <-ctx.Done(): |
| 87 | return ctx.Err() |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | err := hc.executeRequest(ctx, method, path, data, result) |
| 92 | if err == nil { |
| 93 | return nil |
| 94 | } |
| 95 | |
| 96 | lastErr = err |
| 97 | |
| 98 | // Check if we should retry |
| 99 | if !hc.shouldRetry(err, attempt, maxRetries) { |
| 100 | break |
| 101 | } |
| 102 | |
| 103 | hc.logger.Debug("Request failed, will retry: %v", err) |
| 104 | } |
| 105 | |
| 106 | return fmt.Errorf("request failed after %d attempts: %w", maxRetries, lastErr) |
| 107 | } |
| 108 | |
| 109 | // executeRequest performs a single HTTP request. |
| 110 | func (hc *HTTPClient) executeRequest(ctx context.Context, method, path string, data interface{}, result *map[string]interface{}) error { |
no test coverage detected