CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-amazonaws--aws-java-sdk-lambda

AWS Java SDK client for communicating with AWS Lambda Service for function management, invocation, and resource control

Pending
Overview
Eval results
Files

waiters-polling.mddocs/

Waiters and Polling

Automated polling utilities for Lambda resources that wait until functions, versions, and configurations reach desired states, providing robust handling of asynchronous operations with configurable retry policies.

Capabilities

AWSLambdaWaiters

Central waiter class providing pre-configured polling strategies for common Lambda resource state transitions.

/**
 * Provides waiter objects for polling Lambda resources until desired state
 * Handles common asynchronous operations with automatic retry and backoff
 */
public class AWSLambdaWaiters {
    
    /**
     * Creates waiters instance for Lambda client
     * @param client AWSLambda client for resource polling
     * @return Configured waiters instance
     */
    public static AWSLambdaWaiters of(AWSLambda client);
    
    /**
     * Waits until Lambda function exists and is accessible
     * Polls GetFunction API until function is found or timeout reached
     * @return Waiter configured for function existence checking
     * Configuration: 20 attempts, 1 second delay between attempts
     */
    public Waiter<GetFunctionRequest> functionExists();
    
    /**
     * Waits until function configuration state is Active
     * Monitors function state transitions during updates and deployments
     * @return Waiter configured for function active state
     * Configuration: 60 attempts, 5 seconds delay between attempts (5 minutes total)
     */
    public Waiter<GetFunctionConfigurationRequest> functionActive();
    
    /**
     * Waits until function update operation completes successfully
     * Monitors LastUpdateStatus until Successful or Failed state reached
     * @return Waiter configured for function update completion
     * Configuration: 60 attempts, 5 seconds delay between attempts (5 minutes total)
     */
    public Waiter<GetFunctionConfigurationRequest> functionUpdated();
    
    /**
     * Waits until published function version becomes active and ready for invocation
     * Ensures version is fully deployed and available across all regions
     * @return Waiter configured for published version active state
     * Configuration: 312 attempts, 5 seconds delay between attempts (26 minutes total)
     */
    public Waiter<GetFunctionConfigurationRequest> publishedVersionActive();
}

Waiter Interface and Configuration

Core waiter functionality with customizable polling behavior and error handling.

/**
 * Generic waiter interface for polling AWS resources until desired state
 * @param <T> Request type for the polling operation
 */
public interface Waiter<T> {
    
    /**
     * Synchronously waits until resource reaches desired state
     * Blocks calling thread until condition met or timeout exceeded
     * @param request Request object for polling operation
     * @return WaiterResult containing final state and timing information
     * @throws WaiterTimedOutException if maximum attempts exceeded
     * @throws WaiterUnrecoverableException if unrecoverable error occurs
     */
    WaiterResult<T> run(T request) throws WaiterTimedOutException, WaiterUnrecoverableException;
    
    /**
     * Asynchronously waits until resource reaches desired state
     * Returns immediately with Future for non-blocking operation
     * @param request Request object for polling operation
     * @return Future containing WaiterResult when operation completes
     */
    Future<WaiterResult<T>> runAsync(T request);
    
    /**
     * Asynchronously waits with custom callback handling
     * @param request Request object for polling operation
     * @param callback Handler for waiter events and completion
     * @return Future for async operation control
     */
    Future<WaiterResult<T>> runAsync(T request, WaiterHandler<T> callback);
}

/**
 * Result of waiter operation containing state and metadata
 * @param <T> Request type that was polled
 */
public class WaiterResult<T> {
    
    /**
     * Gets number of polling attempts made
     * @return Total attempts executed before completion
     */
    public int getAttemptsExecuted();
    
    /**
     * Gets final state of waiter operation
     * @return WaiterState indicating success, failure, or timeout
     */
    public WaiterState getWaiterState();
}

public enum WaiterState {
    SUCCESS,    // Desired condition met
    RETRY,      // Condition not met, will retry
    FAILURE     // Unrecoverable failure occurred
}

Waiter Exception Handling

Specialized exceptions for different waiter failure scenarios.

/**
 * Exception thrown when waiter times out before reaching desired state
 * Indicates maximum attempts were exceeded without success
 */
public class WaiterTimedOutException extends Exception {
    
    /**
     * Gets waiter result containing attempt information
     * @return WaiterResult with details about failed attempts
     */
    public WaiterResult<?> getWaiterResult();
}

/**
 * Exception thrown when waiter encounters unrecoverable error
 * Indicates polling cannot continue due to permanent failure condition
 */
public class WaiterUnrecoverableException extends Exception {
    
    /**
     * Creates exception with cause and waiter result
     * @param message Error description
     * @param cause Underlying exception that caused failure
     * @param waiterResult Result containing attempt information
     */
    public WaiterUnrecoverableException(String message, Throwable cause, WaiterResult<?> waiterResult);
    
    /**
     * Gets waiter result containing attempt information
     * @return WaiterResult with details about failed attempts
     */
    public WaiterResult<?> getWaiterResult();
}

Waiter Event Handling

Callback interface for monitoring waiter progress and handling events.

/**
 * Handler interface for waiter events and state transitions
 * Enables custom logic during polling operations
 * @param <T> Request type being polled
 */
public interface WaiterHandler<T> {
    
    /**
     * Called before each polling attempt
     * @param context Current waiter execution context
     */
    void onWaiterStart(WaiterExecutionContext<T> context);
    
    /**
     * Called after each polling attempt
     * @param context Current waiter execution context
     */
    void onWaiterSuccess(WaiterExecutionContext<T> context);
    
    /**
     * Called when waiter operation fails
     * @param context Current waiter execution context
     * @param exception Exception that caused failure
     */
    void onWaiterFailure(WaiterExecutionContext<T> context, Throwable exception);
}

/**
 * Execution context providing waiter state and metadata
 * @param <T> Request type being polled
 */
public class WaiterExecutionContext<T> {
    
    /**
     * Gets current polling request
     * @return Request object for this polling attempt
     */
    public T getRequest();
    
    /**
     * Gets current attempt number (0-based)
     * @return Number of attempts completed so far
     */
    public int getAttemptNumber();
    
    /**
     * Gets total time elapsed since waiter start
     * @return Duration in milliseconds
     */
    public long getElapsedTimeMillis();
}

Usage Examples

Basic Function Existence Waiting

AWSLambda lambdaClient = AWSLambdaClientBuilder.defaultClient();
AWSLambdaWaiters waiters = AWSLambdaWaiters.of(lambdaClient);

// Wait for function to exist after creation
GetFunctionRequest request = new GetFunctionRequest()
    .withFunctionName("newly-created-function");

try {
    WaiterResult<GetFunctionRequest> result = waiters.functionExists().run(request);
    System.out.println("Function exists after " + result.getAttemptsExecuted() + " attempts");
} catch (WaiterTimedOutException e) {
    System.err.println("Function did not appear within expected time");
} catch (WaiterUnrecoverableException e) {
    System.err.println("Unrecoverable error while waiting: " + e.getMessage());
}

Waiting for Function Updates

// Update function code
UpdateFunctionCodeRequest updateRequest = new UpdateFunctionCodeRequest()
    .withFunctionName("my-function")
    .withS3Bucket("deployment-bucket")
    .withS3Key("my-function-v2.zip");

lambdaClient.updateFunctionCode(updateRequest);

// Wait for update to complete
GetFunctionConfigurationRequest configRequest = new GetFunctionConfigurationRequest()
    .withFunctionName("my-function");

try {
    WaiterResult<GetFunctionConfigurationRequest> result = 
        waiters.functionUpdated().run(configRequest);
    
    System.out.println("Function update completed successfully");
    System.out.println("Attempts: " + result.getAttemptsExecuted());
    
} catch (WaiterTimedOutException e) {
    System.err.println("Function update timed out after " + 
        e.getWaiterResult().getAttemptsExecuted() + " attempts");
} catch (WaiterUnrecoverableException e) {
    System.err.println("Function update failed: " + e.getMessage());
}

Asynchronous Waiting with Callbacks

// Implement custom waiter handler
WaiterHandler<GetFunctionConfigurationRequest> handler = new WaiterHandler<GetFunctionConfigurationRequest>() {
    
    @Override
    public void onWaiterStart(WaiterExecutionContext<GetFunctionConfigurationRequest> context) {
        System.out.println("Starting wait attempt " + (context.getAttemptNumber() + 1));
    }
    
    @Override
    public void onWaiterSuccess(WaiterExecutionContext<GetFunctionConfigurationRequest> context) {
        System.out.println("Function is now active after " + 
            context.getElapsedTimeMillis() + "ms");
    }
    
    @Override
    public void onWaiterFailure(WaiterExecutionContext<GetFunctionConfigurationRequest> context, 
                               Throwable exception) {
        System.err.println("Wait failed at attempt " + context.getAttemptNumber() + 
            ": " + exception.getMessage());
    }
};

// Wait asynchronously for function to become active
GetFunctionConfigurationRequest configRequest = new GetFunctionConfigurationRequest()
    .withFunctionName("deployment-target");

Future<WaiterResult<GetFunctionConfigurationRequest>> future = 
    waiters.functionActive().runAsync(configRequest, handler);

// Continue with other work while waiting
performOtherOperations();

// Get result when needed
try {
    WaiterResult<GetFunctionConfigurationRequest> result = future.get(10, TimeUnit.MINUTES);
    System.out.println("Function is active and ready for use");
} catch (TimeoutException e) {
    System.err.println("Async wait timed out");
    future.cancel(true);
}

Version Publication Workflow

public class VersionDeploymentManager {
    private final AWSLambda lambdaClient;
    private final AWSLambdaWaiters waiters;
    
    public VersionDeploymentManager(AWSLambda lambdaClient) {
        this.lambdaClient = lambdaClient;
        this.waiters = AWSLambdaWaiters.of(lambdaClient);
    }
    
    /**
     * Deploy new version and wait for it to be fully active
     */
    public String deployNewVersion(String functionName, String description) {
        try {
            // Publish new version
            PublishVersionRequest publishRequest = new PublishVersionRequest()
                .withFunctionName(functionName)
                .withDescription(description);
            
            PublishVersionResult publishResult = lambdaClient.publishVersion(publishRequest);
            String newVersion = publishResult.getVersion();
            
            System.out.println("Published version " + newVersion + ", waiting for activation...");
            
            // Wait for version to become active
            GetFunctionConfigurationRequest configRequest = new GetFunctionConfigurationRequest()
                .withFunctionName(functionName)
                .withQualifier(newVersion);
            
            WaiterResult<GetFunctionConfigurationRequest> waiterResult = 
                waiters.publishedVersionActive().run(configRequest);
            
            System.out.println("Version " + newVersion + " is now active after " + 
                waiterResult.getAttemptsExecuted() + " polling attempts");
            
            return newVersion;
            
        } catch (WaiterTimedOutException e) {
            throw new RuntimeException("Version activation timed out after " + 
                e.getWaiterResult().getAttemptsExecuted() + " attempts");
        } catch (WaiterUnrecoverableException e) {
            throw new RuntimeException("Version activation failed permanently", e);
        }
    }
    
    /**
     * Deploy with alias update and traffic shifting
     */
    public void deployToAlias(String functionName, String aliasName, String description) {
        try {
            // Deploy new version
            String newVersion = deployNewVersion(functionName, description);
            
            // Update alias to point to new version
            UpdateAliasRequest aliasRequest = new UpdateAliasRequest()
                .withFunctionName(functionName)
                .withName(aliasName)
                .withFunctionVersion(newVersion);
            
            lambdaClient.updateAlias(aliasRequest);
            System.out.println("Alias " + aliasName + " updated to version " + newVersion);
            
        } catch (Exception e) {
            System.err.println("Deployment failed: " + e.getMessage());
            throw e;
        }
    }
}

Batch Operations with Waiters

public class BatchFunctionManager {
    private final AWSLambda lambdaClient;
    private final AWSLambdaWaiters waiters;
    private final ExecutorService executorService;
    
    public BatchFunctionManager(AWSLambda lambdaClient) {
        this.lambdaClient = lambdaClient;
        this.waiters = AWSLambdaWaiters.of(lambdaClient);
        this.executorService = Executors.newFixedThreadPool(10);
    }
    
    /**
     * Update multiple functions concurrently and wait for all to complete
     */
    public void updateFunctionsConcurrently(List<String> functionNames, 
                                           Map<String, FunctionCode> codeUpdates) {
        List<Future<String>> futures = new ArrayList<>();
        
        // Start all updates concurrently
        for (String functionName : functionNames) {
            Future<String> future = executorService.submit(() -> {
                return updateSingleFunction(functionName, codeUpdates.get(functionName));
            });
            futures.add(future);
        }
        
        // Wait for all updates to complete
        List<String> results = new ArrayList<>();
        for (Future<String> future : futures) {
            try {
                String result = future.get(10, TimeUnit.MINUTES);
                results.add(result);
            } catch (Exception e) {
                System.err.println("Function update failed: " + e.getMessage());
            }
        }
        
        System.out.println("Updated " + results.size() + " functions successfully");
    }
    
    private String updateSingleFunction(String functionName, FunctionCode code) {
        try {
            // Update function code
            UpdateFunctionCodeRequest updateRequest = new UpdateFunctionCodeRequest()
                .withFunctionName(functionName)
                .withS3Bucket(code.getS3Bucket())
                .withS3Key(code.getS3Key());
            
            lambdaClient.updateFunctionCode(updateRequest);
            
            // Wait for update completion
            GetFunctionConfigurationRequest configRequest = new GetFunctionConfigurationRequest()
                .withFunctionName(functionName);
            
            WaiterResult<GetFunctionConfigurationRequest> result = 
                waiters.functionUpdated().run(configRequest);
            
            return "Function " + functionName + " updated in " + 
                result.getAttemptsExecuted() + " attempts";
                
        } catch (Exception e) {
            throw new RuntimeException("Failed to update function " + functionName, e);
        }
    }
}

Custom Waiter Implementation

public class CustomLambdaWaiter {
    private final AWSLambda lambdaClient;
    
    public CustomLambdaWaiter(AWSLambda lambdaClient) {
        this.lambdaClient = lambdaClient;
    }
    
    /**
     * Wait for provisioned concurrency to be ready with custom polling
     */
    public void waitForProvisionedConcurrency(String functionName, String qualifier, 
                                            int maxAttempts, long delayMs) {
        
        GetProvisionedConcurrencyConfigRequest request = new GetProvisionedConcurrencyConfigRequest()
            .withFunctionName(functionName)
            .withQualifier(qualifier);
        
        int attempt = 0;
        while (attempt < maxAttempts) {
            try {
                GetProvisionedConcurrencyConfigResult result = 
                    lambdaClient.getProvisionedConcurrencyConfig(request);
                
                ProvisionedConcurrencyStatus status = result.getStatus();
                System.out.println("Attempt " + (attempt + 1) + ": Status is " + status);
                
                if (ProvisionedConcurrencyStatus.READY.equals(status)) {
                    System.out.println("Provisioned concurrency is ready!");
                    return;
                } else if (ProvisionedConcurrencyStatus.FAILED.equals(status)) {
                    throw new RuntimeException("Provisioned concurrency failed: " + 
                        result.getStatusReason());
                }
                
                // Wait before next attempt
                Thread.sleep(delayMs);
                attempt++;
                
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Interrupted while waiting", e);
            }
        }
        
        throw new RuntimeException("Provisioned concurrency did not become ready after " + 
            maxAttempts + " attempts");
    }
    
    /**
     * Wait for multiple functions to be active with progress reporting
     */
    public void waitForMultipleFunctions(List<String> functionNames) {
        Set<String> pendingFunctions = new HashSet<>(functionNames);
        int totalAttempts = 0;
        final int maxAttempts = 60;
        
        while (!pendingFunctions.isEmpty() && totalAttempts < maxAttempts) {
            Iterator<String> iterator = pendingFunctions.iterator();
            
            while (iterator.hasNext()) {
                String functionName = iterator.next();
                
                try {
                    GetFunctionConfigurationRequest request = new GetFunctionConfigurationRequest()
                        .withFunctionName(functionName);
                    
                    GetFunctionConfigurationResult result = 
                        lambdaClient.getFunctionConfiguration(request);
                    
                    if (State.Active.equals(result.getState()) && 
                        LastUpdateStatus.Successful.equals(result.getLastUpdateStatus())) {
                        
                        System.out.println("Function " + functionName + " is now active");
                        iterator.remove();
                    }
                    
                } catch (ResourceNotFoundException e) {
                    // Function doesn't exist yet, keep waiting
                }
            }
            
            if (!pendingFunctions.isEmpty()) {
                System.out.println("Still waiting for " + pendingFunctions.size() + 
                    " functions: " + pendingFunctions);
                
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Interrupted while waiting", e);
                }
            }
            
            totalAttempts++;
        }
        
        if (!pendingFunctions.isEmpty()) {
            throw new RuntimeException("Functions not ready after maximum attempts: " + 
                pendingFunctions);
        }
        
        System.out.println("All functions are now active and ready");
    }
}

Exception Handling

Common exceptions when using waiters:

  • WaiterTimedOutException: Maximum polling attempts exceeded without reaching desired state
  • WaiterUnrecoverableException: Permanent failure condition prevents continued polling
  • ResourceNotFoundException: Target resource does not exist and cannot be polled
  • InterruptedException: Waiter thread was interrupted during polling delay
  • ExecutionException: Async waiter operation failed with underlying exception

Best Practices

Waiter Configuration

  • Choose appropriate timeout values based on expected operation duration
  • Use shorter delays for fast operations, longer delays for deployment operations
  • Consider resource limits and API throttling when setting polling frequency
  • Implement exponential backoff for operations prone to transient failures

Error Handling

  • Distinguish between temporary failures (retry) and permanent failures (abort)
  • Log waiter progress for debugging and monitoring purposes
  • Implement fallback strategies for waiter timeouts
  • Use circuit breaker patterns to prevent cascading failures

Performance Optimization

  • Use asynchronous waiters for non-blocking operations
  • Batch multiple waiter operations when possible
  • Monitor API usage to avoid throttling limits
  • Cache waiter instances to reduce initialization overhead

Monitoring and Observability

  • Track waiter success rates and average completion times
  • Set up alerts for frequent waiter timeouts or failures
  • Log waiter events for troubleshooting deployment issues
  • Use structured logging for correlation with other operations

Install with Tessl CLI

npx tessl i tessl/maven-com-amazonaws--aws-java-sdk-lambda

docs

account-settings.md

alias-version-management.md

client-management.md

concurrency-performance.md

event-source-mapping.md

function-invocation.md

function-management.md

function-url-management.md

high-level-invocation.md

index.md

layer-management.md

permissions-policies.md

runtime-management.md

security-code-signing.md

tagging.md

waiters-polling.md

tile.json