MCPcopy Create free account
hub / github.com/devrandom-se/heimdall / RetryUtil

Class RetryUtil

src/main/java/se/devrandom/heimdall/util/RetryUtil.java:33–172  ·  view source on GitHub ↗

Utility class for executing operations with retry logic and exponential backoff. Only retries transient errors (network issues, 5xx errors, S3 throttling). Does not retry client errors (4xx) or application-level failures.

Source from the content-addressed store, hash-verified

31 * Does not retry client errors (4xx) or application-level failures.
32 */
33public class RetryUtil {
34 private static final Logger log = LoggerFactory.getLogger(RetryUtil.class);
35
36 /**
37 * Executes the given operation with retry logic and exponential backoff.
38 *
39 * @param operation The operation to execute
40 * @param maxAttempts Maximum number of attempts (e.g., 3)
41 * @param initialDelayMs Initial delay in milliseconds (e.g., 1000 for 1 second)
42 * @param operationName Name of the operation for logging purposes
43 * @param <T> Return type of the operation
44 * @return The result from the operation
45 * @throws Exception if all retry attempts are exhausted or a non-retryable error occurs
46 */
47 public static <T> T executeWithRetry(
48 Callable<T> operation,
49 int maxAttempts,
50 long initialDelayMs,
51 String operationName) throws Exception {
52
53 Exception lastException = null;
54
55 for (int attempt = 1; attempt <= maxAttempts; attempt++) {
56 try {
57 return operation.call();
58 } catch (Exception e) {
59 lastException = e;
60
61 // Check if we've exhausted all attempts
62 if (attempt == maxAttempts) {
63 log.error("{} failed after {} attempts", operationName, maxAttempts, e);
64 throw e;
65 }
66
67 // Check if error is retryable
68 if (!isRetryable(e)) {
69 log.error("{} failed with non-retryable error: {}",
70 operationName, e.getClass().getSimpleName(), e);
71 throw e;
72 }
73
74 // Calculate exponential backoff delay: 1s, 2s, 4s, 8s, ...
75 long delayMs = initialDelayMs * (1L << (attempt - 1));
76
77 log.warn("{} attempt {}/{} failed, retrying in {}ms: {}",
78 operationName, attempt, maxAttempts, delayMs, e.getMessage());
79
80 // Wait before retrying
81 try {
82 Thread.sleep(delayMs);
83 } catch (InterruptedException ie) {
84 Thread.currentThread().interrupt();
85 throw new RuntimeException("Retry interrupted", ie);
86 }
87 }
88 }
89
90 // Should not reach here, but throw last exception just in case

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected