AWS

AWS Lambda SnapStart - Turbocharge Java Application Startup by up to 10x at no extra cost

· 4 min read
AWS Lambda SnapStart - Turbocharge Java Application Startup by up to 10x at no extra cost

Spring Framework 5.0 introduced Spring Cloud Function to aid the implementation of business logic as a function and to enable Spring Boot features on different serverless cloud providers (AWS Lambda, Azure, Google Cloud Platform, Oracle Fn platform and Apache OpenWhisk). Its "functional" style of bean declarations helps in the fast startup. Now, the AWS Lambda SnapStart feature will turbocharge the startup of your Java application by up to 10x at no additional cost.

In this post, we'll learn more about AWS Lambda SnapStart and run one Spring Cloud Function (Github: SpringBoot2AwsLambda) on AWS Lambda's Java 11 (Corretto) runtime to verify the improvement for cold start requests.

Video

Lifecycle of Lambda Execution Environment

There are mainly three phases in the lifecycle of Lambda execution environment.

⏲️
Init: Creates execution environment, download function's code and layers, bootstrap the runtime and runs function's initialization code.
🚀
Invoke: Executes Lambda function's handler method.
🛑
Shutdown: Removes the environment, if no invocation happens for a period of time.

Lambda maintains the execution environment for some time for the next invocation to reuse (warm start).

In general, Java based frameworks take extra time to bootstrap because following activities.

  • Different Class-Loaders load classes, libraries, extensions etc. into Java Virtual Machine (JVM), then performs linking and initialization.
  • Frameworks like Spring Framework heavily uses reflection for runtime dependency injection (DI), component scanning, auto configuration etc.
  • Finally JIT (Just in time) compilation to covert bytecodes to native machine code at run time.

Because of that, Init phase (function's initialization code) is slower for Java/Spring/Spring Boot applications. And this results cold start.

My sample Spring Cloud Function (with bare minimum dependencies/libraries) is just one liner to return a greeting message . Still it took ≈4.8 seconds in initialization phase for cold start request.

package com.awsjunkie.demo;

import java.util.function.Function;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import com.awsjunkie.demo.model.User;

@SpringBootApplication
public class SpringBoot2AwsLambdaApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBoot2AwsLambdaApplication.class, args);
	}
	
	// org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest
	@Bean
	public Function<User, String> uppercase() {
		return user -> {
				return String.format("Hello %s!", user.getName());
		};
	}

}

ℹ️
For Lambda handler field in "Runtime settings", use org.springframework.cloud.function.adapter.aws.FunctionInvoker provided by AWS adapter for Spring Cloud Function.

What Lambda SnapStart deos?

When Lambda SnapStart is enabled,  Init phase happens when a Lambda function version is published.

"Creating version # of function SpringBoot2AwsLambdaDemo. SnapStart adds a few minutes to the version creation process."

Lambda takes Firecracker microVM snapshot of the initialized execution environment, encrypts the same and cache it for future use. For a cold start request, instead of initializing the execution environment, Lambda simply restores the persisted snapshot. This technique significantly reduces the cold start duration.

What is Firecracker?
Firecracker is an open source virtualization technology that is purpose-built for creating and managing secure, multi-tenant container and function-based services that provide serverless operational models. Firecracker runs workloads in lightweight virtual machines, called microVMs, which combine the security and isolation properties provided by hardware virtualization technology with the speed and flexibility of containers.
- https://github.com/firecracker-microvm/firecracker

To see the improvement (if any), I invoked the SnapStart enabled lambda function version.

Now, there is no Init duration. Init phase already happened when we published the lambda function. We'll see the Restoration duration for all cold start requests. So we realized 5610.83 / 1084.97 ≈ 5x improvement for cold start requests just by enabling SnapStart. For a heavier application, we'll see more improvement.

Without SnapStart:
==================
Init duration (4821.37 ms) + Duration (789.46 ms) = Total (5610.83 ms)

With SnapStart:
===============
Restore duration (356.80 ms) + Duration (728.17 ms) = Total (1084.97 ms)

Performance Boost:
==================
(5610.83 / 1084.97) = 5.17x

Billed Restore Duration is lesser than Restoration duration because  AWS does not charge for restoring the snapshot.

ℹ️
Restore Duration: The time it takes for Lambda to restore a snapshot, load the runtime (JVM), and run any afterRestore runtime hooks.
ℹ️
Billed Restore Duration: The time it takes for Lambda to load the runtime (JVM) and run any afterRestore hooks. You are not charged for the time it takes to restore a snapshot.

How to enable Lambda SnapStart ?

SnapStart supports Java managed runtime Java 11 (Corretto)  onwards. It can be enabled only for published lambda function versions and aliases pointing to a version.

You can activate and manage Lambda SnapStart in many ways (e.g. AWS console, AWS CLI, AWS SAM, AWS CDK, CloudFormation etc.).  Let's see how we can enable it using the AWS console.

Configuration --> General configuration --> Edit
Edit basic settings --> Published versions
🚮
Lambda deletes snapshots if the function or function version gets deleted and function version is not invoked for 14 days.

Whenever we deal with snapshot/restore of the memory and disk state of execution environment, we must consider few other aspects to avoid undesired outcome.  Please refer all the Best practices for working with Lambda SnapStart.

References