Skip to content

Commit 32f4aad

Browse files
authored
use 1-based attempt number (#272)
1 parent 24bc5ab commit 32f4aad

File tree

14 files changed

+93
-88
lines changed

14 files changed

+93
-88
lines changed

sdk-integration-tests/src/test/java/software/amazon/lambda/durable/WaitForConditionIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ void propertyWaitStrategyReceivesCorrectStateAndAttempt() {
269269
// Check function returns state + 1, starting from 0
270270
// So after check i, state = i + 1, and strategy receives that value
271271
assertEquals(i + 1, observedStates.get(i), "State at strategy call " + (i + 1));
272-
assertEquals(i, observedAttempts.get(i), "Attempt at strategy call " + (i + 1));
272+
assertEquals(i + 1, observedAttempts.get(i), "Attempt at strategy call " + (i + 1));
273273
}
274274
}
275275
}

sdk-testing/src/main/java/software/amazon/lambda/durable/testing/TestOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,6 @@ public ErrorObject getError() {
126126
/** Returns the current retry attempt number (0-based), defaulting to 0 if not available. */
127127
public int getAttempt() {
128128
var details = operation.stepDetails();
129-
return details != null && details.attempt() != null ? details.attempt() : 0;
129+
return details != null && details.attempt() != null ? details.attempt() : 1;
130130
}
131131
}

sdk/src/main/java/software/amazon/lambda/durable/execution/CheckpointManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
class CheckpointManager {
3131
private static final int MAX_BATCH_SIZE_BYTES = 750 * 1024; // 750KB
3232
private static final int MAX_ITEM_COUNT = 200; // max updates in one batch
33+
private static final int FIRST_ATTEMPT = 1;
3334
private static final Logger logger = LoggerFactory.getLogger(CheckpointManager.class);
3435

3536
private final Consumer<List<Operation>> callback;
@@ -96,7 +97,7 @@ CompletableFuture<Operation> pollForUpdate(String operationId, PollingStrategy p
9697
.computeIfAbsent(operationId, k -> Collections.synchronizedList(new ArrayList<>()))
9798
.add(future);
9899
}
99-
pollForUpdateInternal(future, 0, Instant.now(), pollingStrategy);
100+
pollForUpdateInternal(future, FIRST_ATTEMPT, Instant.now(), pollingStrategy);
100101
return future;
101102
}
102103

sdk/src/main/java/software/amazon/lambda/durable/operation/StepOperation.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
* @param <T> the result type of the step function
3434
*/
3535
public class StepOperation<T> extends SerializableDurableOperation<T> {
36-
private static final Integer FIRST_ATTEMPT = 0;
36+
private static final Integer FIRST_ATTEMPT = 1;
3737

3838
private final Function<StepContext, T> function;
3939
private final StepConfig config;
@@ -60,7 +60,7 @@ protected void start() {
6060
@Override
6161
protected void replay(Operation existing) {
6262
var attempt = existing.stepDetails() != null && existing.stepDetails().attempt() != null
63-
? existing.stepDetails().attempt()
63+
? existing.stepDetails().attempt() + 1
6464
: FIRST_ATTEMPT;
6565
switch (existing.status()) {
6666
case SUCCEEDED, FAILED -> markAlreadyCompleted();

sdk/src/main/java/software/amazon/lambda/durable/operation/WaitForConditionOperation.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* @param <T> the type of state being polled
3737
*/
3838
public class WaitForConditionOperation<T> extends SerializableDurableOperation<T> {
39+
private static final Integer FIRST_ATTEMPT = 1;
3940

4041
private final BiFunction<T, StepContext, WaitForConditionResult<T>> checkFunc;
4142
private final WaitForConditionConfig<T> config;
@@ -61,7 +62,7 @@ public WaitForConditionOperation(
6162

6263
@Override
6364
protected void start() {
64-
executeCheckLogic(config.initialState(), 0);
65+
executeCheckLogic(config.initialState(), FIRST_ATTEMPT);
6566
}
6667

6768
@Override
@@ -99,7 +100,8 @@ public T get() {
99100

100101
private void resumeCheckLoop(Operation existing) {
101102
var stepDetails = existing.stepDetails();
102-
int attempt = (stepDetails != null && stepDetails.attempt() != null) ? stepDetails.attempt() : 0;
103+
int attempt =
104+
(stepDetails != null && stepDetails.attempt() != null) ? stepDetails.attempt() + 1 : FIRST_ATTEMPT;
103105
var checkpointData = stepDetails != null ? stepDetails.result() : null;
104106
T currentState; // Get current state
105107
if (checkpointData != null) {

sdk/src/main/java/software/amazon/lambda/durable/retry/PollingStrategies.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public static class Presets {
2323
/**
2424
* Creates an exponential backoff polling strategy.
2525
*
26-
* <p>The delay calculation follows the formula: delay = jitter(baseInterval × backoffRate^attempt)
26+
* <p>The delay calculation follows the formula: delay = jitter(baseInterval × backoffRate^(attempt-1))
2727
*
2828
* @param baseInterval Base delay before first poll
2929
* @param backoffRate Multiplier for exponential backoff (must be positive)
@@ -49,7 +49,8 @@ public static PollingStrategy exponentialBackoff(
4949
}
5050

5151
return (attempt) -> {
52-
double delayMs = baseInterval.toMillis() * Math.pow(backoffRate, attempt);
52+
// attempt is 1-based
53+
double delayMs = baseInterval.toMillis() * Math.pow(backoffRate, attempt - 1);
5354
delayMs = Math.min(jitter.apply(delayMs), maxInterval.toMillis());
5455
return Duration.ofMillis(Math.round(delayMs));
5556
};

sdk/src/main/java/software/amazon/lambda/durable/retry/PollingStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public interface PollingStrategy {
1111
/**
1212
* Computes the delay before the next polling attempt.
1313
*
14-
* @param attempt The current attempt number (0-based)
14+
* @param attempt The current attempt number (1-based)
1515
* @return Duration to wait before the next poll
1616
*/
1717
Duration computeDelay(int attempt);

sdk/src/main/java/software/amazon/lambda/durable/retry/RetryStrategies.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ public static class Presets {
2929
);
3030

3131
/** No retry strategy - fails immediately on first error. Use this for operations that should not be retried. */
32-
public static final RetryStrategy NO_RETRY = (error, attemptNumber) -> RetryDecision.fail();
32+
public static final RetryStrategy NO_RETRY = (error, attempt) -> RetryDecision.fail();
3333
}
3434

3535
/**
3636
* Creates an exponential backoff retry strategy.
3737
*
38-
* <p>The delay calculation follows the formula: baseDelay = min(initialDelay × backoffRate^attemptNumber, maxDelay)
38+
* <p>The delay calculation follows the formula: baseDelay = min(initialDelay × backoffRate^(attempt-1), maxDelay)
3939
*
4040
* @param maxAttempts Maximum number of attempts (including initial attempt)
4141
* @param initialDelay Initial delay before first retry
@@ -56,17 +56,17 @@ public static RetryStrategy exponentialBackoff(
5656
throw new IllegalArgumentException("backoffRate must be positive");
5757
}
5858

59-
return (error, attemptNumber) -> {
60-
// Check if we've exceeded max attempts (attemptNumber is 0-based)
61-
if (attemptNumber + 1 >= maxAttempts) {
59+
return (error, attempt) -> {
60+
// Check if we've exceeded max attempts (attemptNumber is 1-based)
61+
if (attempt >= maxAttempts) {
6262
return RetryDecision.fail();
6363
}
6464

6565
// Calculate delay with exponential backoff
6666
double initialDelaySeconds = initialDelay.toSeconds();
6767
double maxDelaySeconds = maxDelay.toSeconds();
6868

69-
double baseDelay = Math.min(initialDelaySeconds * Math.pow(backoffRate, attemptNumber), maxDelaySeconds);
69+
double baseDelay = Math.min(initialDelaySeconds * Math.pow(backoffRate, attempt - 1), maxDelaySeconds);
7070

7171
// Apply jitter
7272
double delayWithJitter = jitter.apply(baseDelay);
@@ -92,8 +92,8 @@ public static RetryStrategy fixedDelay(int maxAttempts, Duration fixedDelay) {
9292
}
9393
ParameterValidator.validateDuration(fixedDelay, "fixedDelay");
9494

95-
return (error, attemptNumber) -> {
96-
if (attemptNumber + 1 >= maxAttempts) {
95+
return (error, attempt) -> {
96+
if (attempt >= maxAttempts) {
9797
return RetryDecision.fail();
9898
}
9999
return RetryDecision.retry(fixedDelay);

sdk/src/main/java/software/amazon/lambda/durable/retry/RetryStrategy.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public interface RetryStrategy {
1515
* Determines whether to retry a failed operation and calculates the retry delay.
1616
*
1717
* @param error The error that occurred during the operation
18-
* @param attemptNumber The current attempt number (0-based, so first attempt is 0)
18+
* @param attempt The current attempt number (1-based, so first attempt is 1)
1919
* @return RetryDecision indicating whether to retry and the delay before next attempt
2020
*/
21-
RetryDecision makeRetryDecision(Throwable error, int attemptNumber);
21+
RetryDecision makeRetryDecision(Throwable error, int attempt);
2222
}

sdk/src/main/java/software/amazon/lambda/durable/retry/WaitForConditionWaitStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public interface WaitForConditionWaitStrategy<T> {
2323
* Computes the delay before the next polling attempt based on the current state and attempt number.
2424
*
2525
* @param state the current state returned by the check function
26-
* @param attempt the attempt number
26+
* @param attempt the attempt number (1-based)
2727
* @return a {@link Duration} representing the delay before the next polling attempt
2828
* @throws WaitForConditionFailedException if the maximum number of attempts has been exceeded
2929
*/

0 commit comments

Comments
 (0)