AWS Java SDK client for communicating with AWS Lambda Service for function management, invocation, and resource control
—
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.
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();
}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
}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();
}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();
}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());
}// 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());
}// 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);
}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;
}
}
}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);
}
}
}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");
}
}Common exceptions when using waiters:
Install with Tessl CLI
npx tessl i tessl/maven-com-amazonaws--aws-java-sdk-lambdadocs