RetryExample.java

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.lambda.durable.examples.step;

import java.time.Duration;
import java.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;
import software.amazon.lambda.durable.config.StepConfig;
import software.amazon.lambda.durable.retry.RetryStrategies;

/**
 * Simple example demonstrating retry strategies with a flaky API.
 *
 * <p>This example shows:
 *
 * <ul>
 *   <li>A step that never retries (fails immediately)
 *   <li>A step that retries with default exponential backoff
 *   <li>Time-based failure simulation for realistic retry behavior
 * </ul>
 */
public class RetryExample extends DurableHandler<Object, String> {

    private static final Logger logger = LoggerFactory.getLogger(RetryExample.class);

    private Instant startTime;

    @Override
    public String handleRequest(Object input, DurableContext context) {
        // Step 1: Record start time
        startTime = context.step("record-start-time", Instant.class, stepCtx -> Instant.now());
        logger.info("Recorded start time: {}", startTime);

        // Step 2: Call that never retries (fails immediately)
        try {
            context.step(
                    "no-retry-call",
                    Void.class,
                    stepCtx -> {
                        throw new RuntimeException("This operation never retries");
                    },
                    StepConfig.builder()
                            .retryStrategy(RetryStrategies.Presets.NO_RETRY)
                            .build());
        } catch (Exception e) {
            logger.info("No-retry step failed as expected: {}", e.getMessage());
        }

        // Step 3: Flaky API call that succeeds after retries
        var result = context.step(
                "flaky-api-call",
                String.class,
                stepCtx -> {
                    // Fail for first 8 seconds, then succeed
                    var failForMillis = 8000;
                    var elapsed = Duration.between(startTime, Instant.now());

                    if (elapsed.toMillis() < failForMillis) {
                        var message = String.format(
                                "Flaky API failing - elapsed time (%.1fs) < %.1fs",
                                elapsed.toMillis() / 1000.0, failForMillis / 1000.0);
                        logger.warn(message);
                        throw new RuntimeException(message);
                    } else {
                        var message = String.format(
                                "Flaky API succeeded - elapsed time (%.1fs) >= %.1fs",
                                elapsed.toMillis() / 1000.0, failForMillis / 1000.0);
                        logger.info(message);
                        return message;
                    }
                },
                StepConfig.builder()
                        .retryStrategy(RetryStrategies.Presets.DEFAULT)
                        .build());

        logger.info("Flaky API result: {}", result);

        // Step 4: Wait a bit before finishing
        context.wait(null, Duration.ofSeconds(2));

        logger.info("Retry example completed successfully");
        return "Retry example completed: " + result;
    }
}