Skip to content

Commit d901827

Browse files
committed
chore: added WithRetryAsync functions
1 parent b102747 commit d901827

2 files changed

Lines changed: 289 additions & 19 deletions

File tree

sdk/src/main/java/software/amazon/lambda/durable/util/WithRetryHelper.java

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.time.Duration;
66
import java.util.Objects;
77
import software.amazon.lambda.durable.DurableContext;
8+
import software.amazon.lambda.durable.DurableFuture;
89
import software.amazon.lambda.durable.TypeToken;
910
import software.amazon.lambda.durable.config.WithRetryConfig;
1011
import software.amazon.lambda.durable.exception.UnrecoverableDurableExecutionException;
@@ -52,6 +53,24 @@
5253
* .build()
5354
* );
5455
* }</pre>
56+
*
57+
* <h2>Usage — async form returning DurableFuture</h2>
58+
*
59+
* <pre>{@code
60+
* DurableFuture<String> future = WithRetryHelper.withRetryAsync(
61+
* context,
62+
* "approval",
63+
* (ctx, attempt) -> ctx.waitForCallback(
64+
* "approval-" + attempt, String.class,
65+
* (callbackId, stepCtx) -> sendApprovalEmail(approverEmail, callbackId)
66+
* ),
67+
* WithRetryConfig.builder()
68+
* .retryStrategy(RetryStrategies.fixedDelay(3, Duration.ofSeconds(2)))
69+
* .build()
70+
* );
71+
* // ... do other work ...
72+
* var result = future.get();
73+
* }</pre>
5574
*/
5675
public final class WithRetryHelper {
5776

@@ -64,48 +83,88 @@ private WithRetryHelper() {
6483
}
6584

6685
/**
67-
* Named form — wraps the retry loop in {@code runInChildContext} by default so all attempts are grouped under a
68-
* single named operation in execution history.
86+
* Named async form — wraps the retry loop in {@code runInChildContextAsync} by default so all attempts are grouped
87+
* under a single named operation in execution history, and returns a {@link DurableFuture} that can be composed or
88+
* blocked on.
6989
*
7090
* <p>The child-context wrapping can be disabled via {@link WithRetryConfig.Builder#wrapInChildContext(boolean)}.
91+
* When disabled, the retry loop executes immediately and the returned future is already completed.
7192
*
7293
* @param <T> the result type
7394
* @param context the durable context
7495
* @param name operation name (used for child context and backoff wait names)
7596
* @param operation the retryable operation — receives the context and 1-based attempt number
7697
* @param config retry configuration including the retry strategy
77-
* @return the operation result
98+
* @return a future representing the operation result
7899
*/
79100
@SuppressWarnings("unchecked")
80-
public static <T> T withRetry(DurableContext context, String name, WithRetry<T> operation, WithRetryConfig config) {
101+
public static <T> DurableFuture<T> withRetryAsync(
102+
DurableContext context, String name, WithRetry<T> operation, WithRetryConfig config) {
81103
Objects.requireNonNull(context, "context cannot be null");
82104
Objects.requireNonNull(name, "name cannot be null");
83105
Objects.requireNonNull(operation, "operation cannot be null");
84106
Objects.requireNonNull(config, "config cannot be null");
85107

86108
if (config.wrapInChildContext()) {
87-
return (T) context.runInChildContext(
109+
return (DurableFuture<T>) context.runInChildContextAsync(
88110
name, new TypeToken<Object>() {}, childCtx -> executeRetryLoop(childCtx, name, operation, config));
89111
}
90-
return executeRetryLoop(context, name, operation, config);
112+
return new CompletedDurableFuture<>(executeRetryLoop(context, name, operation, config));
91113
}
92114

93115
/**
94-
* Anonymous form — runs the retry loop directly in the caller's context. No child-context wrapping is applied
95-
* regardless of the {@code wrapInChildContext} config setting.
116+
* Named sync form — wraps the retry loop in {@code runInChildContext} by default so all attempts are grouped under
117+
* a single named operation in execution history, and blocks until the result is available.
118+
*
119+
* <p>Equivalent to {@code withRetryAsync(context, name, operation, config).get()}.
96120
*
97121
* @param <T> the result type
98122
* @param context the durable context
123+
* @param name operation name (used for child context and backoff wait names)
99124
* @param operation the retryable operation — receives the context and 1-based attempt number
100125
* @param config retry configuration including the retry strategy
101126
* @return the operation result
102127
*/
103-
public static <T> T withRetry(DurableContext context, WithRetry<T> operation, WithRetryConfig config) {
128+
public static <T> T withRetry(DurableContext context, String name, WithRetry<T> operation, WithRetryConfig config) {
129+
return withRetryAsync(context, name, operation, config).get();
130+
}
131+
132+
/**
133+
* Anonymous async form — runs the retry loop directly in the caller's context and returns a {@link DurableFuture}.
134+
* No child-context wrapping is applied regardless of the {@code wrapInChildContext} config setting.
135+
*
136+
* <p>Because the anonymous form executes the retry loop inline (no child context), the returned future is always
137+
* already completed.
138+
*
139+
* @param <T> the result type
140+
* @param context the durable context
141+
* @param operation the retryable operation — receives the context and 1-based attempt number
142+
* @param config retry configuration including the retry strategy
143+
* @return a future representing the operation result
144+
*/
145+
public static <T> DurableFuture<T> withRetryAsync(
146+
DurableContext context, WithRetry<T> operation, WithRetryConfig config) {
104147
Objects.requireNonNull(context, "context cannot be null");
105148
Objects.requireNonNull(operation, "operation cannot be null");
106149
Objects.requireNonNull(config, "config cannot be null");
107150

108-
return executeRetryLoop(context, null, operation, config);
151+
return new CompletedDurableFuture<>(executeRetryLoop(context, null, operation, config));
152+
}
153+
154+
/**
155+
* Anonymous sync form — runs the retry loop directly in the caller's context. No child-context wrapping is applied
156+
* regardless of the {@code wrapInChildContext} config setting.
157+
*
158+
* <p>Equivalent to {@code withRetryAsync(context, operation, config).get()}.
159+
*
160+
* @param <T> the result type
161+
* @param context the durable context
162+
* @param operation the retryable operation — receives the context and 1-based attempt number
163+
* @param config retry configuration including the retry strategy
164+
* @return the operation result
165+
*/
166+
public static <T> T withRetry(DurableContext context, WithRetry<T> operation, WithRetryConfig config) {
167+
return withRetryAsync(context, operation, config).get();
109168
}
110169

111170
/**

0 commit comments

Comments
 (0)