Best practices for working with Lambda SnapStart - AWS Lambda

Best practices for working with Lambda SnapStart

Network connections

The state of connections that your function establishes during the initialization phase isn't guaranteed when Lambda resumes your function from a snapshot. In most cases, network connections that an AWS SDK establishes automatically resume. For other connections, we recommend the following best practices.

Re-establish network connections

Always re-establish your network connections when your function resumes from a snapshot. We recommend that you re-establish network connections in the function handler. Alternatively, you can use an afterRestore runtime hook.

Don't use hostname as a unique execution environment identifier

We recommend against using hostname to identify your execution environment as a unique node or container in your applications. With SnapStart, a single snapshot is used as the initial state for multiple execution environments, and all execution environments return the same hostname value for InetAddress.getLocalHost(). For applications that require a unique execution environment identity or hostname value, we recommend that you generate a unique ID in the function handler. Or, use an afterRestore runtime hook to generate a unique ID, and then use the unique ID as the identifier for the execution environment.

Avoid binding connections to fixed source ports

We recommend that you avoid binding network connections to fixed source ports. Connections are re-established when a function resumes from a snapshot, and network connections that are bound to a fixed source port might fail.

Avoid using Java DNS cache

Lambda functions already cache DNS responses. If you use another DNS cache with SnapStart, then you might experience connection timeouts when the function resumes from a snapshot.

The java.util.logging.Logger class can indirectly enable the JVM DNS cache. To override the default settings, set networkaddress.cache.ttl to 0 before initializing logger. Example:

public class MyHandler { // first set TTL property static{ java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); } // then instantiate logger var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class); }

To prevent UnknownHostException failures, we recommend setting networkaddress.cache.negative.ttl to 0. You can set this property for a Lambda function with the AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL=0 environment variable.

Disabling the JVM DNS cache does not disable Lambda's managed DNS caching.

Performance tuning

Note

SnapStart works best when used with function invocations at scale. Functions that are invoked infrequently might not experience the same performance improvements.

To maximize the benefits of SnapStart, we recommend that you preload classes that contribute to startup latency in your initialization code instead of in the function handler. This moves the latency associated with heavy class loading out of the invocation path, optimizing startup performance with SnapStart.

If you can't preload classes during initialization, then we recommend that you preload classes with dummy invocations. To do this, update the function handler code, as shown in the following example from the pet store function on the AWS Labs GitHub repository.

private static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler; static { try { handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class); // Use the onStartup method of the handler to register the custom filter handler.onStartup(servletContext -> { FilterRegistration.Dynamic registration = servletContext.addFilter("CognitoIdentityFilter", CognitoIdentityFilter.class); registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*"); }); // Send a fake Amazon API Gateway request to the handler to load classes ahead of time ApiGatewayRequestIdentity identity = new ApiGatewayRequestIdentity(); identity.setApiKey("foo"); identity.setAccountId("foo"); identity.setAccessKey("foo"); AwsProxyRequestContext reqCtx = new AwsProxyRequestContext(); reqCtx.setPath("/pets"); reqCtx.setStage("default"); reqCtx.setAuthorizer(null); reqCtx.setIdentity(identity); AwsProxyRequest req = new AwsProxyRequest(); req.setHttpMethod("GET"); req.setPath("/pets"); req.setBody(""); req.setRequestContext(reqCtx); Context ctx = new TestContext(); handler.proxy(req, ctx); } catch (ContainerInitializationException e) { // if we fail here. We re-throw the exception to force another cold start e.printStackTrace(); throw new RuntimeException("Could not initialize Spring framework", e); } }