Measuring cold start time of AWS Lambda functions with Java and SnapStart

Aleksandr Filichkin
5 min readMar 26, 2023

--

I hope everyone already knows that Java is not a perfect language for AWS Lambda because of its huge cold start(first start) issue. You can read about it in my previous article.

What is SnapStart

At AWS re:Invent 2022, AWS announced SnapStart for AWS Lambda running on Java Corretto 11. AWS says that this feature will significantly (10x) reduce the cold start latency and this feature is free, with no additional costs.

SnapStart is a feature that improves the startup time of Lambda functions. When a customer releases a new version of their function, the Lambda service initializes its code and creates an encrypted snapshot of the execution environment. This snapshot is stored in a cache with multiple tiers for quick and easy access.

Later, when the function is called and scaled, Lambda retrieves the execution environment from the stored snapshot instead of starting from scratch. This process significantly reduces the time it takes for the function to start up, resulting in lower latency.

The lifecycle of a Lambda function

As always all code is available on my GitHub https://github.com/Aleksandr-Filichkin/aws-lambda-snapstart. The repo contains all required Terrafrom scripts and has GitLab Actions.

Test scenario

API-Gateway -> AWS Lambda->DynamoDb

We are going to compare the performance of plan Java implementation and Java + SnapStart.

Plain Java (without SnapStart)

  • Java 11
  • AWS SDK-V2 for Dynamodb
  • No DI (Spring, etc)
  • No special frameworks
  • Utilize CPU burst on startup (move everything to static, warm-up dynamo DB client by saving a dummy data into Dymanodb)
  • Reduce dependencies(exclude Netty)
  • Specify AWS Regions
  • Specify Credential Provider
public class BookHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

/**
* All clients should be static and initialized on init state, it dramatically reduces cold start, because use CPU burst of AWS Lambda.
*/
private static final EnhancedRequestDispatcher ENHANCED_REQUEST_DISPATCHER = initDispatcher();

static {
/**
* warm-up dynamo DB client by saving a dummy data into Dymanodb
*/
ENHANCED_REQUEST_DISPATCHER.warmUp();
}

private static EnhancedRequestDispatcher initDispatcher() {
DynamoDbEnhancedClient dynamoDbEnhancedClient = DynamoDbEnhancedClient.builder()
.dynamoDbClient(DynamoDbClient.builder()
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.EU_WEST_1)
.build())
.build();
DynamoDbTable<Book> dynamoDbTable = dynamoDbEnhancedClient.table(TABLE_NAME, TableSchema.fromBean(Book.class));
return new EnhancedRequestDispatcher(new EnhancedClientBookStorage(dynamoDbTable), new ObjectMapper());
}

@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) {
return ENHANCED_REQUEST_DISPATCHER.dispatch(apiGatewayProxyRequestEvent);
}
}

Java with SnapStart

AWS says that to use SnapStart we just need:

1)Enable SnapStart config

AWS Lambda configuration console

But I love Terraform and in Terraform we just need to add one parameter snap_start (check https://github.com/Aleksandr-Filichkin/aws-lambda-snapstart/blob/main/terraform/lambda.tf#L20)

2)Select the Versions tab and choose Publish new. It’s needed because SnapStart works only with a specific version of a function or alias.

So I try to enable to SnapStart on the existing Java code without any changes and it failed:

Error for SnapStart

The issue is in CredentialProvider. In the documentation, you can see this https://docs.aws.amazon.com/lambda/latest/dg/snapstart-activate.html#snapstart-credentials

So we need to update CredentialProvider and use ContainerCredentialsProvider or even you can just do not specify the provider and it will be resolved automatically.

private static EnhancedRequestDispatcher initDispatcher() {
DynamoDbEnhancedClient dynamoDbEnhancedClient = DynamoDbEnhancedClient.builder()
.dynamoDbClient(DynamoDbClient.builder()
.credentialsProvider(ContainerCredentialsProvider.builder().build())
.region(Region.EU_WEST_1)
.build())
.build();
DynamoDbTable<Book> dynamoDbTable = dynamoDbEnhancedClient.table(TABLE_NAME, TableSchema.fromBean(Book.class));
return new EnhancedRequestDispatcher(new EnhancedClientBookStorage(dynamoDbTable), new ObjectMapper());
}

After this fix we can see this message:

Once you’ve enabled SnapStart, Lambda publishes all subsequent versions with snapshots. The time to run your publish version depends on your init code. For my code, it took up to 2 mins

In 2 mins everything was completed:

Does SnapStart help if we remove the warm-up call to DynamoDB?

No, really. Without a warm-up, we have 2862 ms duration for 1024MB lambda versus 400 ms with a warm-up!

SnapStart Report without warm-up:

REPORT RequestId: 7f0f0297–47cf-472a-b8cf-aa3b8a79be53 Duration: 2687.89 ms Billed Duration: 2862 ms Memory Size: 1024 MB Max Memory Used: 128 MB Restore Duration: 258.84 ms Billed Restore Duration: 174 ms

SnapStart Report with warm-up:

REPORT RequestId: 7c579824–487d-4301–971a-fc5c5d488e1e Duration: 271.14 ms Billed Duration: 400 ms Memory Size: 1024 MB Max Memory Used: 127 MB Restore Duration: 241.03 ms Billed Restore Duration: 128 ms

Performance Result

128 MB Memory is still not enough, SnapStart doesn’t help with memory, only reduces cold start. If you want to reduce the memory, the only solution for Java and AWS Lambda is GraalVm native. You can read it here https://medium.com/p/2655eeee98c6

The result is great, the only strange thing with 256 MB. I ran it many times and had always Billed duration > 3 sec:

REPORT RequestId: a853d064–0db3–4514–9c56-fba9f06d9b46 Duration: 3536.34 ms Billed Duration: 3848 ms Memory Size: 256 MB Max Memory Used: 141 MB Restore Duration: 1105.99 ms Billed Restore Duration: 311 ms

REPORT RequestId: 5b3b0272–0c0e-4421–9cf9–9ba5fc913939 Duration: 3817.11 ms Billed Duration: 4274 ms Memory Size: 256 MB Max Memory Used: 141 MB Restore Duration: 621.28 ms Billed Restore Duration: 456 ms

Summary

  • As you can see SnapStart feature is really amazing, it makes Java runtime much better.
  • It requires almost zero code changes in your code if you already do warm-ups for your clients.
  • But if you have lazy clients like AWS SDK clients then you have to warm up it using some fake/dummy calls. SnapStart will initialize the client in the first stage.
  • It doesn’t solve Java high memory consumption. Use GraalVM for low memory usage.
  • SnapStart doesn’t support yet ARM architecture, which can be critical if the cost is more important than rare cold starts

--

--