Vadym Kazulkin | @VKazulkin |ip.labs GmbH
How to develop, run and optimize
Spring Boot 3 application
on AWS Lambda
Vadym Kazulkin, ip.labs, JAX, May 6, 2025
How to develop,
run and optimize
Spring Boot 3
1
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Vadym Kazulkin
ip.labs GmbH Bonn, Germany
Co-Organizer of the Java User Group Bonn
v.kazulkin@gmail.com
@VKazulkin
https://dev.to/vkazulkin
https://github.com/Vadym79/
https://de.slideshare.net/VadymKazulkin/
https://www.linkedin.com/in/vadymkazulkin
https://www.iplabs.de/
Contact
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
About ip.labs
4
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Your organization has profound Java and Spring Boot
development skills
▪ You have an existing Spring Boot 3 (web) application (in AWS), or
you want to develop a new one
Current State
5
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ You want to use AWS Lambda for hosting your web-based
Applications written in Java
▪ To benefit from the advantages of the Serverless architectures
▪ Scalability
▪ Less focus on operational tasks
▪ API Gateway and Lambda offer fully managed approach
▪ Less focus on the code maintenance
▪ Requires securing the application only
Desired State
6
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
„AWS Lambda SnapStart „ series
7
https://dev.to/vkazulkin/measuring-java-11-lambda-cold-starts-with-snapstart-part-1-first-impressions-30a4
Article series covers they why and what
behind Lambda SnapStart and priming
techniques including measurements for cold
and warm starts with different settings for
▪ Java 11
▪ Java 17
▪ Java 21
▪ Micronaut
▪ Quarkus
▪ Spring Boot 2.7
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ I can at best achieve acceptable performance by using pure Java
on AWS Lambda
▪ Spring (Boot) performance on AWS Lambda would be bad
▪ Too much runtime annotation processing, reflection, dynamic
class loading used
▪ As a result, high cold (application start up) and warm start times
Most frequent replies and assumptions
8
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ I use NoSQL Amazon DynamoDB for my examples
▪ You can make it work with the same introduced approaches by
managing your own database or using Amazon RDS (i.e.
PostgreSQL)
▪ Challenges around managing database connections. Possible
solutions are:
▪ Using Amazon RDS Proxy and putting Lambda functions into VPC
▪ Using (PostgreSQL) Aurora Serverless v2 Data API
▪ Try out the newly introduced Aurora DSQL (in preview)
▪ You need to re-measure performance though
Database usage
9
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Demo Application
11
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot
▪ PUT
https://{YOUR_API_GATEWAY_URL}/prod
/products/
▪ GET
https://{YOUR_API_GATEWAY_URL}/prod
/products/ {productId}
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Spring Boot 3 Application class
13
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
public record Product(String id, String name, BigDecimal price) { }
Product Entity class
14
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@Repository
public class DynamoProductDao implements ProductDao {
private static final String PRODUCT_TABLE_NAME =
System.getenv("PRODUCT_TABLE_NAME");
private static final DynamoDbClient dynamoDbClient =
DynamoDbClient.builder().build();
@Override
public void putProduct(Product product) {
dynamoDbClient.putItem(PutItemRequest.builder()
.tableName(PRODUCT_TABLE_NAME)
.item(ProductMapper.productToDynamoDb(product))
.build());
}
Product Repository DAO class
15
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@Override
public Optional<Product> getProduct(String id) {
GetItemResponse getItemResponse = dynamoDbClient.getItem
(GetItemRequest.builder().key(Map.of("PK",
AttributeValue.builder().s(id).build())).tableName(PRODUCT_TABLE_NAME).build());
if (getItemResponse.hasItem()) {
return
Optional.of(ProductMapper.productFromDynamoDB(getItemResponse.item()));
} else {
return Optional.empty();
}
}
..
}
Product Repository DAO class
16
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
public class GetProductByIdHandler implements
RequestHandler<APIGatewayProxyRequestEvent, Optional<Product>> {
private static final ProductDao productDao = new DynamoProductDao();
@Override
public Optional<Product> handleRequest(APIGatewayProxyRequestEvent event,
Context context) {
String id = event.getPathParameters().get("id");
return productDao.getProduct(id);
}}
AWS Lambda Function with Java runtime
How to develop, run and optimize Spring Boot 3 application on AWS Lambda
17
Invocation of the
handeRequest method is
the warm start
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ You can use Lambda function (implement RequestHandler)
interface and inject any types of the Spring beans like services
or repositories directly there
▪ I’ll focus more on using more portable solutions using Spring
Boot controllers
▪ Portable in the sense of more easily switching between Serverless,
Containers (ECS, EKS, Fargate) or pure servers (EC2) on AWS
▪ Only adjust pom.xml, IaC and a small chunk of the source code
▪ Amazon DynamoDB locks you in into AWS
Business Logic
18
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ AWS Serverless Java Container
▪ AWS Lambda Web Adapter
▪ Spring Cloud Function with AWS Adapter
▪ (Docker) Container Image on AWS Lambda
▪ Optimization techniques
▪ AWS (Lambda) SnapStart
▪ GraalVM Native Image
▪ Cold and warm start measurements for all approaches and optimizations
Agenda
19
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS Serverless Java Container
20
https://github.com/aws/serverless-java-container/
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-aws-serverless-java-container
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ The AWS Serverless Java Container makes it
easier to run Java applications written with
frameworks such as Struts, Spring, Spring Boot
2 and 3, or JAX-RS/Jersey in Lambda.
▪ The container provides adapter logic to
minimize code changes. Incoming events are
translated to the Jakarta EE Servlet specification
so that frameworks work as before
AWS Serverless Java Container
21
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
pom.xml
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-springboot3</artifactId>
</dependency>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
AWS Serverless Java Container
22
exclude Tomcat, as we use
Amazon API Gateway
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@RestController @EnableWebMvc
public class ProductController {
@Autowired
private DynamoProductDao productDao;
@RequestMapping(path = “/products/{id}”, method = RequestMethod.GET, produces =
MediaType.APPLICATION_JSON_VALUE)
public Optional<Product> getProductById(@PathVariable(“id”) String id) {
return productDao.getProduct(id);
}
@RequestMapping(path = “/products”, method = RequestMethod.PUT, consumes =
MediaType.APPLICATION_JSON_VALUE)
public void createProduct(@RequestBody Product product) {
productDao.putProduct(product);
}
Spring Boot 3 Product (Rest)Controller class
23
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@RequestMapping(path = “/products/{id}”, method = RequestMethod.GET, produces =
MediaType.APPLICATION_JSON_VALUE)
public Optional<Product> getProductById(@PathVariable(“id”) String id) {
return productDao.getProduct(id);
}
Mapps to the Lambda function in IaC (AWS SAM template)
GetProductByIdFunction:
Type: AWS::Serverless::Function
....
Events:
GetRequestById:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /products/{id}
Method: get
Spring Boot 3 Rest Controller method mapping to
Lambda Function in IaC
24
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Automatic proxying through AWS Serverless Java Container
AWS SAM Template (template.yaml)
GetProductByIdFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler
CodeUri: target/ aws-lambda-spring-boot-3.4-aws-serverless-java-container… .jar
Runtime: java21
MemorySize: 1024
Environment:
Variables:
MAIN_CLASS: com.amazonaws.Application
AWS Serverless Java Container
26
Use generic AWS Serverless Container
Spring Boot 3 proxy Lambda function
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Self-written Lambda function
AWS SAM Template (template.yaml)
GetProductByIdFunction:
Type: AWS::Serverless::Function
Properties:
Handler: software.amazonaws.example.product.handler.StreamLambdaHandler::handleRequest
CodeUri: target/aws-lambda-spring-boot-3.4-aws-serverless-java-container… .jar
Runtime: java21
MemorySize: 1024
AWS Serverless Java Container
27
Or implement our own Lambda function
as an alternative
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
public class StreamLambdaHandler implements RequestStreamHandler {
SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler =
SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context
context) throws IOException {
handler.proxyStream(inputStream, outputStream, context);
}
}
AWS Serverless Java Container
28
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS Lambda Web Adapter
30
https://github.com/awslabs/aws-lambda-web-adapter
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-lambda-web-adapter
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ AWS Lambda Web Adapter is a tool written in Rust programming language
to run web applications on AWS Lambda.
▪ It allows developers to build web apps (HTTP API) with familiar frameworks
(e.g. Express.js, Next.js, Flask, Spring Boot, ASP.NET and Laravel, anything
that speaks HTTP 1.1/1.0) and run it on AWS Lambda.
▪ The same Docker image can run also on Amazon EC2, AWS Fargate, and
local computers
AWS Lambda Web Adapter
31
https://github.com/awslabs/aws-lambda-web-adapter
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Run web applications on AWS Lambda
▪ Supports Amazon API Gateway Rest API and HTTP
API endpoints, Lambda Function URLs, and
Application Load Balancer
▪ Supports all non-HTTP event triggers, such as SQS,
SNS, S3, DynamoDB, Kinesis, Kafka, EventBridge,
and Bedrock Agents
AWS Lambda Web Adapter
32
https://github.com/awslabs/aws-lambda-web-adapter
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Can be deployed in several ways:
▪ Lambda functions packaged as Docker Images
▪ Lambda functions packaged as OCI Images
▪ Lambda functions packaged as Zip package for
AWS managed runtimes and attached as Lambda
layer to your Lambda function
AWS Lambda Web Adapter
33
https://github.com/awslabs/aws-lambda-web-adapter
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@RestController @EnableWebMvc
public class ProductController {
@Autowired
private DynamoProductDao productDao;
@RequestMapping(path = “/products/{id}”, method = RequestMethod.GET, produces =
MediaType.APPLICATION_JSON_VALUE)
public Optional<Product> getProductById(@PathVariable(“id”) String id) {
return productDao.getProduct(id);
}
@RequestMapping(path = “/products”, method = RequestMethod.PUT, consumes =
MediaType.APPLICATION_JSON_VALUE)
public void createProduct(@RequestBody Product product) {
productDao.putProduct(product);
}
Spring Boot 3 Product (Rest)Controller class
34
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@RequestMapping(path = “/products/{id}”, method = RequestMethod.GET, produces =
MediaType.APPLICATION_JSON_VALUE)
public Optional<Product> getProductById(@PathVariable(“id”) String id) {
return productDao.getProduct(id);
}
Mapps to the Lambda function in IaC (AWS SAM template)
GetProductByIdFunction:
Type: AWS::Serverless::Function
....
Events:
GetRequestById:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /products/{id}
Method: get
Spring Boot 3 Rest Controller method mapping to
Lambda Function in IaC
35
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@RequestMapping(path = “/products”, method = RequestMethod.PUT, consumes =
MediaType.APPLICATION_JSON_VALUE)
public void createProduct( @RequestBody Product product) {
productDao.putProduct(product);
}
Mapps to the Lambda function in IaC (AWS SAM template)
PutProductFunction:
Type: AWS::Serverless::Function
...
Events:
PutRequest:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /products
Method: put
Spring Boot 3 Rest Controller method mapping to
Lambda Function in IaC
36
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS SAM Template (template.yaml)
GetProductByIdFunction:
Type: AWS::Serverless::Function
Properties:
Handler: run.sh
CodeUri: target/aws-lambda-spring-boot-3.4-lambda-web-adapter… .jar
Runtime: java21
MemorySize: 1024
Layers:
- !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:23
Environment:
Variables:
AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap
AWS Lambda Web Adapter
37
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
run.sh
#!/bin/sh
exec java -cp "./:lib/*" "software.amazonaws.Application"
AWS Lambda Web Adapter
38
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Spring Cloud Function with AWS Adapter
41
https://spring.io/projects/spring-cloud-function https://spring.io/projects/spring-cloud-aws
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-spring-cloud-function
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Promote the implementation of business logic via functions.
▪ A simple function application (in context or Spring) is an application that
contains beans of type Supplier, Java 8 Function interface or Consumer.
Spring Cloud Function for AWS Lambda
42
https://spring.io/projects/spring-cloud-function https://spring.io/projects/spring-cloud-aws
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Decouple the development lifecycle of business logic from any specific
runtime target so that the same code can run as a web endpoint, a stream
processor, or a task. One of these specific runtime targets can be AWS
Lambda.
▪ Enable Spring Boot features (auto-configuration, dependency injection,
metrics) on serverless providers.
Spring Cloud Function for AWS Lambda
43
https://spring.io/projects/spring-cloud-function https://spring.io/projects/spring-cloud-aws
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ AWS Request Adapter converts the JSON coming
from Lambda function to the HttpServletRequest
which then invokes the Spring Dispatcher Servlet
which then interacts with our Spring Boot
application on API level without starting web server
▪ Then response flows back and AWS Response
Adapter converts HttpServletResponse to JSON
which Lambda function sends back to API Gateway.
Spring Cloud Function for AWS Lambda
45
https://spring.io/projects/spring-cloud-function https://spring.io/projects/spring-cloud-aws
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
In AWS Serverless Java Container we saw in pom.xml
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>
aws-serverless-java-container-springboot3
</artifactId>
<version>2.0.0</version>
</dependency>
Spring Cloud Function for AWS Lambda
46
▪ If we look into the whole
dependency tree, we'll discover
another dependency spring-
cloud-function-serverless-web
that artifact aws-serverless-java-
container-springboot3 requires
▪ This is the collaboration effort
between Spring and AWS
Serverless developers
▪ Spring Boot 3 Application with
AWS Serverless Java container is
Spring Cloud function application
as well
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-web</artifactId>
</dependency>
Spring Cloud Function for AWS Lambda
47
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
pom.xml
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
Spring Cloud Function for AWS Lambda
48
exclude Tomcat, as we use
Amazon API Gateway
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS SAM Template (template.yaml)
GetProductByIdFunction:
Type: AWS::Serverless::Function
Properties:
Handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest
CodeUri: target/aws-lambda-spring-boot-3.4-spring-cloud-function-aws… .jar
Runtime: java21
MemorySize: 1024
Environment:
Variables:
MAIN_CLASS: software.amazonaws.Application
SPRING_CLOUD_FUNCTION_DEFINITION: getProductByIdHandler
Spring Cloud Function for AWS Lambda
49
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@Component
public class GetProductByIdHandler implements
Function<APIGatewayProxyRequestEvent, Product> {
@Autowired
private DynamoProductDao productDao;
...
public APIGatewayProxyResponseEvent apply(APIGatewayProxyRequestEvent requestEvent) {
String id = requestEvent.getPathParameters().get("id");
return productDao.getProduct(id);
}
Spring Cloud Function for AWS Lambda
50
SPRING_CLOUD_FUNCTION_DEFINITION:
getProductByIdHandler
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Docker Image with Spring Cloud Function
for AWS Lambda
51
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-aws-serverless-java-container-as-lambda-docker-image
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Re-use AWS Serverless Java Container
▪ Use Base Java 21 Docker Image from Amazon ECR
public.ecr.aws/lambda/java:21
▪ Docker build image
▪ Docker tag image
▪ Create Amazon ECR repository (if not exists)
▪ Docker push image to Amazon ECR repository
Docker Container Image with Spring Cloud Function
52
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Dockerfile
FROM public.ecr.aws/lambda/java:21
# Copy function code and runtime dependencies from Maven layout
COPY target/classes ${LAMBDA_TASK_ROOT}
COPY target/classes/lib/* ${LAMBDA_TASK_ROOT}/lib/
Docker Container Image with Spring Cloud Function
53
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS SAM Template (template.yaml)
GetProductByIdFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.eu-central-1.amazonaws.com/spring-boot-3.4-
with-aws-serverless-java-container-as-lambda-docker-image:v1
ImageConfig:
Command:
["software.amazonaws.example.product.handler.StreamLambdaHandler::handleRequest"]
MemorySize: 1024
…..
Docker Container Image with Spring Cloud Function
54
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Lambda function lifecycle – a full cold start
55
Sources: Ajay Nair „Become a Serverless Black Belt” https://www.youtube.com/watch?v=oQFORsso2go
Tomasz Łakomy "Notes from Optimizing Lambda Performance for Your Serverless Applications“ https://tlakomy.com/optimizing-lambda-performance-for-serverless-applications
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Start Firecracker VM (execution environment)
▪ AWS Lambda starts the Java runtime
▪ Java runtime loads and initializes Lambda function code
(Lambda handler Java class)
▪ Class loading
▪ Static initializer block of the handler class is executed (i.e. AWS
service client creation)
▪ Runtime dependency injection
▪ Just-in-Time (JIT) compilation kicks in
▪ Lambda invokes the handler method
56
Lambda function lifecycle
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Demo Application
57
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot
▪ Used Spring 3.4 with Amazon Corretto
Java 21
▪ Lambda has 1024 MB memory setting
▪ Lambda uses x86 architecture
▪ Used „-XX:+TieredCompilation -
XX:TieredStopAtLevel=1” Java
compilation option
▪ Info about the experiments:
▪ Approx. 1 hour duration
▪ Approx. first* 100 cold starts
▪ Approx. first 100.000 warm starts
*after Lambda function being re-deployed
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
1000
2000
3000
4000
5000
6000
7000
8000
9000
AWS Serverless Java
Container
AWS Lambda Web AdapterSpring Cloud Function with
AWS Adapter
Docker Image based on
AWS Serverless Java
Container
p50 p75 p90 p99 p99.9 max
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
1000
2000
3000
4000
5000
6000
7000
8000
9000
AWS Serverless Java
Container
AWS Lambda Web
Adapter
Spring Cloud
Function with AWS
Adapter
Docker Image based
on AWS Serverless
Java Container
Pure Java w/o
framework usage
p50 p75 p90 p99 p99.9 max
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0,00
5,00
10,00
15,00
20,00
25,00
AWS Serverless Java
Container
AWS Lambda Web AdapterSpring Cloud Function with
AWS Adapter
Docker Image based on
AWS Serverless Java
Container
p50 p75 p90 p99
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0,00
5,00
10,00
15,00
20,00
25,00
AWS Serverless Java
Container
AWS Lambda Web
Adapter
Spring Cloud
Function with AWS
Adapter
Docker Image based
on AWS Serverless
Java Container
Pure Java w/o
framework usage
p50 p75 p90 p99
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
200
400
600
800
1000
1200
1400
1600
1800
2000
AWS Serverless Java
Container
AWS Lambda Web AdapterSpring Cloud Function with
AWS Adapter
Docker Image based on
AWS Serverless Java
Container
p99.9 max
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
200
400
600
800
1000
1200
1400
1600
1800
2000
AWS Serverless Java
Container
AWS Lambda Web
Adapter
Spring Cloud
Function with AWS
Adapter
Docker Image based
on AWS Serverless
Java Container
Pure Java w/o
framework usage
p99.9 max
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS Lambda
SnapStart
66
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪Lambda SnapStart can improve startup performance for latency-
sensitive applications
▪SnapStart is fully managed
AWS Lambda SnapStart
69
https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS SnapStart Deployment & Invocation
70
https://dev.to/vkazulkin/measuring-java-11-lambda-cold-starts-with-snapstart-part-1-first-impressions-30a4
https://aws.amazon.com/de/blogs/compute/using-aws-lambda-snapstart-with-infrastructure-as-code-and-ci-cd-pipelines/
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Currently available for Lambda managed Java Runtimes (Java 11, 17 and 21),
Python and .NET
▪ Not available for all other Lambda runtimes:
▪ Docker Container Image
▪ Custom (Lambda) Runtime (a way to ship GraalVM Native Image)
AWS Lambda SnapStart
71
https://github.com/Vadym79/AWSLambdaJavaDockerImage/
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
available for:
▪ AWS Serverless Java Container
▪ AWS Lambda Web Adapter
▪ Spring Cloud Function (on AWS Lambda)
▪ (Docker) Container Image on AWS Lambda
AWS Lambda SnapStart
72
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS SnapStart Deployment & Invocation
74
https://aws.amazon.com/de/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/
Vadym Kazulkin @VKazulkin , ip.labs GmbH
C
Create
Snapshot
Firecracker microVM
create & restore
snapshot
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Linux CRIU available since 2012 allows a running application to be paused and restarted at some
point later in time, potentially on a different machine.
▪ The overall goal of the project is to support the migration of containers.
▪ When performing a checkpoint, essentially, the full context of the process is saved: program
counter, registers, stacks, memory-mapped and shared memory
▪ To restore the application, all this data can be reloaded and (theoretically) it continues from the
same point.
▪ Challenges
▪ open files
▪ network connections
▪ sudden change in the value of the system clock
▪ time-based caches
CRIU (Checkpoint/Restore in Userspace)
76
https://criu.org/Main_Page
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ CRaC is a JDK project that allows you to start Java programs with a
shorter time to first transaction, combined with less time and resources
to achieve full code speed.
▪ CRaC effectively takes a snapshot of the Java process (checkpoint) when
it is fully warmed up, then uses that snapshot to launch any number of
JVMs from this captured state.
▪ CRaC is based on CRIU or recently released Warp engine
Ideas behind CRaC (Coordinated Restore at
Checkpoint)
78
https://www.azul.com/blog/superfast-application-startup-java-on-crac/
https://github.com/CRaC/docs
https://foojay.io/today/warp-the-new-crac-engine/
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Comparing Firecracker and CRIU snapshotting:
▪ Firecracker snapshotting saves a whole running OS
▪ CRIU snapshotting saves a single process or container
▪ Advantages of the Firecracker snapshot : we don't have to care about
file handles because they will be still valid after resume.
▪ Drawbacks the Firecracker snapshot : the need to reseed /dev/random
and to sync the system clock.
Snapshot /checkpointing and restore
Firecracker microVM vs CRIU
79
https://mail.openjdk.org/pipermail/discuss/2021-July/005864.html
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS Lambda SnapStart with Priming
82
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Pre-load as many Java classes as possible before the SnapStart takes the
snapshot
▪ Java loads classes on demand (lazy-loading)
▪ Pre-initialize as much as possible before the SnapStart takes the snapshot
▪ Http Clients (Apache, UrlConnection) and JSON Marshallers (Jackson) require
expensive one-time initialization per (Lambda) lifecycle. They both are used when
creating Amazon DynamoDbClient
▪ Expensive static (cached) one-time mappings
Lambda API Gateway Request Event (received as JSON) to Spring Boot
programming model (@RestController) through AWS Serverless Java Container and
Spring Cloud Function (with AWS Adapter) frameworks
Ideas behind priming
83
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS SnapStart Deployment & Invocation
84
https://aws.amazon.com/de/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/
Vadym Kazulkin @VKazulkin , ip.labs GmbH
Lambda uses the
CRaC APIs for
runtime hooks
for Priming
C
Create
Snapshot
Firecracker microVM
create & restore snapshot
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS Lambda SnapStart with Priming
Approach 1 Priming of DynamoDB request
86
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Prime dependencies during initialization phase (when it worth doing)
▪ „Fake“ the calls to pre-initialize „some other expensive stuff“ (this technique is called
Priming)
▪ In case of DynamoDB client put the following code outside of the handler method to
pre-initialize the HTTP Client and Jackson Marshaller:
DynamoDbClient client = DynamoDbClientBuilder.builder().region(Regions.US_WEST_2).build();
GetItemResponse getItemResponse = client.getItem(GetItemRequest.builder()
.key(Map.of("PK", AttributeValue.builder().s(id).build()))
.tableName(PRODUCT_TABLE_NAME).build());
……
Priming
87
invocation forces HTTP Client and Jackson
Marshallers to pre-initialize
getProductById (int id)
method
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@Configuration
public class Priming implements Resource {
@Autowired
private DynamoProductDao productDao;
public Priming() {
Core.getGlobalContext().register(this);
}
@Override
public void beforeCheckpoint(Context<? extends Resource> context) throws Exception {
productDao.getProduct("0");
}
@Override
public void afterRestore(Context<? extends Resource> context) throws Exception { }
}
Priming of DynamoDB request
88
• Uses CRaC API: Resource interface with
beforeCheckpoint and afterRestore
methods
• Applies priming to all Lambda functions in
the deployment artifact
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS Lambda SnapStart with Priming
Approach 2 Priming/Proxying the whole web
(API Gateway) request without going via
network
95
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
public class StreamLambdaHandler implements RequestStreamHandler, Resource {
SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler =
SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
@Autowired
private DynamoProductDao productDao;
public StreamLambdaHandler() {
Core.getGlobalContext().register(this);
}
@Override
public void afterRestore(Context<? extends Resource> context) throws Exception { }
96
AWS Serverless Java Container (Priming of API Gateway request)
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
handler.proxyStream(inputStream, outputStream, context);
}
@Override
public void beforeCheckpoint(Context<? extends Resource> context) throws Exception {
handler.proxy(getAwsProxyRequest(), new MockLambdaContext());
}
97
AWS Serverless Java Container (Priming of API Gateway request)
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
private static AwsProxyRequest getAwsProxyRequest () {
final AwsProxyRequest awsProxyRequest = new AwsProxyRequest ();
awsProxyRequest.setHttpMethod("GET");
awsProxyRequest.setPath("/products/0");
awsProxyRequest.setResource("/products/{id}");
awsProxyRequest.setPathParameters(Map.of("id","0"));
final AwsProxyRequestContext awsProxyRequestContext = new AwsProxyRequestContext();
final ApiGatewayRequestIdentity apiGatewayRequestIdentity= new ApiGatewayRequestIdentity();
apiGatewayRequestIdentity.setApiKey("blabla");
awsProxyRequestContext.setIdentity(apiGatewayRequestIdentity);
awsProxyRequest.setRequestContext(awsProxyRequestContext);
return awsProxyRequest;
}
}
98
AWS Serverless Java Container (Priming of API Gateway request)
Simulate
ApiGatewayProxyRequest
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
public class GetProductByIdHandler
implements RequestHandler<APIGatewayProxyRequestEvent, Optional<Product> {
private static final ProductDao productDao = new DynamoProductDao();
@Override
public Optional<Product> handleRequest(APIGatewayProxyRequestEvent requestEvent,
Context context) {
String id = requestEvent.getPathParameters().get("id");
return productDao.getProduct(id);
}
}
AWS Lambda with pure Java
99
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
This type of priming doesn’t work for AWS Web Adapter
as doesn’t offer low-level API to stream/proxy the API
Gateway web request
100
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
@Component
public class GetProductByIdHandler implements
Function<APIGatewayProxyRequestEvent, Product>, Resource {
@Autowired
private DynamoProductDao productDao;
public GetProductByIdWithDynamoDBRequestPrimingHandler () {
Core.getGlobalContext().register(this);
}
...
public Product apply(APIGatewayProxyRequestEvent requestEvent) {
String id = requestEvent.getPathParameters().get("id");
return productDao.getProduct(id);
}
101
Spring Cloud Function for AWS (Priming of API Gateway request)
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
1000
2000
3000
4000
5000
6000
7000
8000
AWS Serverless Java Container AWS Lambda Web Adapter Spring Cloud Function with AWS
Adapter
w/o SnapStart w SnapStart no Priming
w SnapStart & DynamoDB request priming w SnapStart and API Gateway request priming
p90
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
1000
2000
3000
4000
5000
6000
7000
8000
AWS Serverless Java
Container
AWS Lambda Web AdapterSpring Cloud Function with
AWS Adapter
Pure Java w/o framework
usage
w/o SnapStart w SnapStart no Priming
w SnapStart & DynamoDB request priming w SnapStart & API Gateway request priming
p90
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
500
1000
1500
2000
2500
3000
3500
AWS Serverless Java Container AWS Lambda Web Adapter Spring Cloud Function with AWS
Adapter
w/o SnapStart w SnapStart no Priming
w SnapStart & DynamoDB request priming w SnapStart & API Gateway request priming
max value
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Lambda SnapStart Priming Guide
108
guide aims to explain techniques
for priming Java applications.
It assumes a base understanding
of AWS Lambda, Lambda
SnapStart, and CRaC.
https://github.com/marksailes/snapstart-priming-guide
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS Lambda Profiler Extension for Java
High performance Serverless Java on AWS
109
https://github.com/aws/aws-lambda-java-libs/tree/main/experimental/aws-lambda-java-profiler
https://dev.to/aws-heroes/aws-lambda-profiler-extension-for-java-part-2-improving-lambda-performance-with-lambda-snapstart-4p06
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Demo Application
110
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot
▪ Used Spring 3.4 with Amazon Corretto
Java 21
▪ Lambda has 1024 MB memory setting
▪ Lambda uses x86 architecture
▪ Used „-XX:+TieredCompilation -
XX:TieredStopAtLevel=1” Java
compilation option
▪ Info about the experiments:
▪ Approx. 1 hour duration
▪ Approx. first* 100 cold starts
▪ Approx. first 100.000 warm starts
*after Lambda function being re-deployed
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS SnapStart Deployment & Invocation
111
https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html
https://aws.amazon.com/de/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/
Firecracker microVM
create & restore
snapshot
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS SnapStart tiered cache
112
https://dev.to/aws-builders/aws-snapstart-part-17-impact-of-the-snapshot-tiered-cache-on-the-cold-starts-with-java-21-52ef
• Due to the effect of
snapshot tiered cache, cold
start times reduces with the
number of invocations
• After certain number of
invocations reached the
cold start times becomes
stable
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
AWS Lambda under the Hood
113
https://www.infoq.com/articles/aws-lambda-under-the-hood/
https://www.infoq.com/presentations/aws-lambda-arch/
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Demo Application
114
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot
▪ Used Spring 3.4 with Amazon Corretto
Java 21
▪ Lambda has 1024 MB memory setting
▪ Lambda uses x86 architecture
▪ Used „-XX:+TieredCompilation -
XX:TieredStopAtLevel=1” Java
compilation option
▪ Info about the experiments:
▪ Approx. 1 hour duration
▪ Approx. last 70 from 100 cold starts
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
500
1000
1500
2000
2500
3000
3500
4000
AWS Serverless
Java Container all
100
AWS Serverless
Java Container
last 70
AWS Lambda
Web Adapter all
100
AWS Lambda
Web Adapter last
70
Spring Cloud
Function with
AWS Adapter all
100
Spring Cloud
Function with
AWS Adapter last
70
w SnapStart no Priming w SnapStart & DynamoDB request priming
w SnapStart and API Gateway request priming
p90
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Other optimizations
116
• Measure the cold and warm start of Lambda
with SnapStart enabled (and with priming) for
arm64 architecture and compare it to x86
• Measure the impact of the different Java
compilation options and memory settings
• Measure the impact of setting the different
synchronous HTTP clients
DynamoDbClient client = DynamoDbClient.builder().region(Region.EU_CENTRAL_1)
. httpClient(ApacheHttpClient.create())
//.httpClient(UrlConnectionHttpClient.create())
//.httpClient(AwsCrtHttpClient.create())
.build();
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Lambda x86_64 vs arm64 architecture
118
https://aws.amazon.com/lambda/pricing/?nc1=h_ls
For the same memory setting Lambda and
Lambda function execution duration the choice of
arm64 over x86_64 architecture is approx. 25%
cheaper
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
500
1000
1500
2000
2500
3000
3500
4000
4500
w/o AWS SnapStart with AWS SnapStart w/o priming with AWS SnapStart with priming
Cold starts of the pure Lambda function (no framework in use)
with Java 21runtime with
1024 MB memory setting, Apache Http Client, compilation
-XX:+TieredCompilation -XX:TieredStopAtLevel=1 for p90
x86_64 arm64
ms
https://dev.to/aws-builders/aws-snapstart-part-16-measuring-cold-and-warm-starts-with-java-21-using-different-asynchronous-http-clients-4n2
+8,6%
+23,9%
+24,8%
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
6,40
6,60
6,80
7,00
7,20
7,40
7,60
7,80
8,00
w/o AWS SnapStart with AWS SnapStart w/o priming with AWS SnapStart with priming
Warm starts of pure Lambda function (no framework in use)
with Java 21runtime with
1024 MB memory setting, Apache Http Client, compilation
-XX:+TieredCompilation -XX:TieredStopAtLevel=1 for p90
x86_64 arm64
ms
https://dev.to/aws-builders/aws-snapstart-part-16-measuring-cold-and-warm-starts-with-java-21-using-different-asynchronous-http-clients-4n2
+9,1%
+10,1%
+4,8%
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ SnapStart supports the Java 11, 17 and 21 (Corretto), Python and .NET
managed runtime only
▪ Deployment with SnapStart enabled takes more than 2-2,5 minutes
additionally
▪ Snapshot is deleted from cache if Lambda function is not invoked for 14
days
▪ SnapStart currently does not support :
▪ Provisioned concurrency
▪ Amazon Elastic File System (Amazon EFS)
▪ Ephemeral storage greater than 512 MB
AWS SnapStart Challenges & Limitations
121
https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Perform Priming out of the box without writing the logic on our own
▪ If snapshot not found do regular cold start and create snapshot under
the hood
▪ Snapshot creation on first Lambda function invocation instead of during
the deployment phase
▪ Regular cold start as long as the snapshot hasn’t been fully taken
▪ Trade off between duration of the deployment phase and several regular
bigger cold starts until the snapshot is taken and SnapStart becomes
effective (and therefore much shorter cold starts)
AWS SnapStart Possible Next Steps
122
https://dev.to/aws-builders/reducing-cold-starts-on-aws-lambda-with-java-runtime-future-ideas-about-snapstart-graalvm-and-co-3a1b
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ AWS Serverless Java Container and AWS Lambda Web Adapter offer similar
functionality
▪ AWS Serverless Java Container offers a bit lower warm start times
▪ AWS Serverless Java Container offers more flexibility to apply priming techniques to the
individual Lambda functions in the deployment artifact through its API
▪ AWS Lambda Web Adapter offers lower cold start times (with SnapStart and priming)
▪ AWS Lambda Web Adapter offers more event triggers (non HTTP events)
▪ Spring Cloud Function makes sense if already in use or you have a non-HTTP
application (i.e. messaging)
▪ AWS Serverless Java Container (since 2.0.2) and Spring Cloud Function both
support GraalVM Native Image
▪ Using Docker Image leads to the highest cold start.
▪ You can use the recent Java version though and use jlink to reduce the size of the JDK
Framework Comparison
123
https://www.azul.com/blog/superfast-application-startup-java-on-crac/ https://github.com/CRaC/docs
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
124
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
GraalVM Architecture
126
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
GraalVM Ahead-of-Time Compilation
127
Source: Oleg Šelajev, Thomas Wuerthinger, Oracle: “Deep dive into using GraalVM for Java and JavaScript”
https://www.youtube.com/watch?v=a-XEZobXspo
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Promise: Java Function compiled into a native executable
using GraalVM Native Image significantly reduces
▪ “cold start” times
▪ memory footprint
GraalVM Native Image
129
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ AWS doesn’t provide GraalVM (Native Image) as Java Runtime out of the box
▪ AWS provides Custom Runtime Option
Current Challenges with Native Executable using
GraalVM
130
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Custom Lambda Runtimes
131
https://github.com/Vadym79/AWSLambdaGraalVMNativeImage
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪Spring Boot 3+ provides official support for
compiling Spring Boot applications into the GraalVM
Native Image
▪Spring AOT (Ahead-of-Time Processing) is a process
that analyzes your application at build-time and
generate an optimized version of it. It is a mandatory
step to run a Spring ApplicationContext in a native
image
Spring Supprt for GraalVM Native Image
134
https://docs.spring.io/spring-boot/maven-plugin/aot.html
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
pom.xml
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
....
</profile>
</profiles>
135
mvn clean package -Pnative
https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-aws-serverless-java-container-as-graalvm-native-image
GraalVM Native Image with AWS Serverless Java Container
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
pom.xml
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>process-aot</id>
<goals>
<goal>process-aot</goal>
</goals>
</execution>
</executions>
</plugin>
136
GraalVM Native Image with AWS Serverless Java Container
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
pom.xml
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<mainClass>software.amazonaws.Application</mainClass>
</configuration>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
137
<phase>package</phase>
</execution>
</executions>
</plugin>
GraalVM Native Image with AWS Serverless Java Container
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import
org.springframework.aot.hint.annotation.RegisterReflection
ForBinding;
@Configuration
@RegisterReflectionForBinding({DateTime.class,
APIGatewayProxyRequestEvent.class, HashSet.class,
APIGatewayProxyRequestEvent.ProxyRequestContext.class,
APIGatewayProxyRequestEvent.RequestIdentity.class,
Product.class, Products.class})
@ImportRuntimeHints(ApplicationConfiguration.Application
RuntimeHintsRegistrar.class)
GraalVM Native Image with AWS Serverless Java Container
139
public class ApplicationConfiguration {
public static class ApplicationRuntimeHintsRegistrar
implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints,
ClassLoader classLoader) {
hints.reflection() .registerType(Product.class,
PUBLIC_FIELDS, INVOKE_PUBLIC_METHODS,
INVOKE_PUBLIC_CONSTRUCTORS
;
}
}
}
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
850
860
870
880
890
900
910
920
930
940
950
GraalVM 23 Native Image with AWS Serverless Java Container
p50 p75 p90 p99 p99.9 max
1024 MB
memory
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
20
40
60
80
100
120
140
160
GraalVM 23 Native Image with AWS Serverless Java Container
p50 p75 p90 p99 p99.9 max
1024 MB
memory
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
500
1000
1500
2000
2500
AWS Serverless Java
Container last 70
AWS Lambda Web Adapter
last 70
Spring Cloud Function with
AWS Adapter last 70
GraalVM 23 Native Image
with AWS Serverless Java
Container
w SnapStart no Priming w SnapStart & DynamoDB priming
w SnapStart & API Gateway request priming
p90
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
500
1000
1500
2000
2500
3000
3500
AWS Serverless Java
Container
AWS Lambda Web AdapterSpring Cloud Function with
AWS Adapter
GraalVM 23 Native Image
with AWS Serverless Java
Container
w/o SnapStart w SnapStart no Priming
w SnapStart & DynamoDB priming w SnapStart & API Gateway request priming
max value
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
100
200
300
400
500
600
700
800
900
1000
GraalVM 23 Native Image with AWS Serverless Java
Container
GraalVM 23 Native Image with Pure Java w/o
framework usage
p50 p75 p90 p99 p99.9 max
1024 MB
memory
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
0
20
40
60
80
100
120
140
160
GraalVM 23 Native Image with AWS Serverless Java
Container
GraalVM 23 Native Image with Pure Java w/o
framework usage
p50 p75 p90 p99 p99.9 max
1024 MB
memory
ms
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Other optimizations
147
AWS CRT Client supports GraalVM Native Image
DynamoDbClient client = DynamoDbClient.builder().
region(Region.EU_CENTRAL_1)
..httpClient(AwsCrtHttpClient.create())
.build();
https://aws.amazon.com/de/blogs/developer/aws-crt-client-for-java-adds-graalvm-native-image-support/
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ GraalVM is really powerful and has a lot of potential
▪ GraalVM Native Image improves cold starts and memory footprint
significantly
▪ GraalVM Native Image is currently not without challenges
▪ AWS Lambda Custom Runtime requires Linux executable only
▪ Building Custom Runtime requires some additional effort
▪ e.g. you need a scalable CI/CD pipeline to build memory-intensive native
image
▪ Build time is a factor
▪ You need to carefully test to avoid runtime errors
GraalVM Conclusion
148
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Frameworks and libraries Ready for GraalVM Native Image
149
https://www.graalvm.org/native-image/libraries-and-frameworks/
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
“Spring Boot 3 application on AWS Lambda” series
150
Article series covers different
ways to write Spring Boot 3
application on AWS Lambda
▪ AWS Serverless Java Container
▪ AWS Lambda Web Adapter
▪ Spring Cloud Functions
▪ Custom Docker Image
▪ GraalVM Native Image
Cold and warm start time
measurements are also provided
https://dev.to/aws-builders/spring-boot-3-application-on-aws-lambda-part-1-introduction-to-the-series-2m5g
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
“Data API for Amazon Aurora Serverless v2
with AWS SDK for Java” series
151
Article series also covers
cold and warm start time
measurements and
optimization techniques
https://dev.to/aws-builders/data-api-for-amazon-aurora-serverless-v2-with-aws-sdk-for-java-part-1-introduction-and-set-up-of-the-sample-application-3g71
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Word of caution
152
Re-measure for your use case!
Even with my examples measurements might
already produce different results due to:
▪ Lambda Amazon Corretto Java 21 managed
runtime minor version changes
▪ Spring Boot 3 (minor) version updates
▪ Lambda SnapStart snapshot create and
restore improvements
▪ Firecracker VM improvements
▪ GraalVM and Native Image improvements
and (minor) version updates
▪ There are still servers behind Lambda
▪ Java Memory Model impact (L or RAM
caches hits and misses)
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ Spring Boot natively supports CRaC
▪ For Checkpoint: -Dspring.context.checkpoint=onRefresh -XX:CRaCCheckpointTo={path}
▪ For Restore: -XX:CRaCRestoreFrom={path}
▪ Amazon Corretto doesn’t support CRaC
▪ Azul Zulu OpenJDK supports CRaC
▪ Can be deployed on Lambda with Docker Container Image
▪ Currently very complicated and only partially possible with hacks and many trade-offs
▪ Mounting EFS for CRaC snapshot. Requires Lambda being in VPC and managing EFS
throughput
Spring Boot Native CRaC Support
153
https://github.com/CRaC/example-lambda/issues/7
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
▪ With AWS SnapStart and GraalVM Native Image you can reduce cold
start times of the Spring Boot 3 application on AWS Lambda to the
acceptable values
▪ If you’re willing to accept slightly higher cold and warm start times for
certain the Lambda function(s) and solid priming is applicable -> use fully
managed AWS SnapStart with priming
▪ If a very high performance for certain the Lambda function(s) is really
crucial for your business -> go for GraalVM Native Image
Wrap up and personal suggestions
154
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
Project Leyden
155
The primary goal of
this Project is to
improve the startup
time, time to peak
performance, and
footprint of Java
programs.
https://www.youtube.com/watch?v=teXijm79vno
https://openjdk.org/projects/leyden/
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
FAQ Ask me Anything
156
Vadym Kazulkin | @VKazulkin |ip.labs GmbH
157
Thank you

How to develop, run and optimize Spring Boot 3 application on AWS Lambda JAX 2025

  • 1.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH How to develop, run and optimize Spring Boot 3 application on AWS Lambda Vadym Kazulkin, ip.labs, JAX, May 6, 2025 How to develop, run and optimize Spring Boot 3 1
  • 2.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Vadym Kazulkin ip.labs GmbH Bonn, Germany Co-Organizer of the Java User Group Bonn v.kazulkin@gmail.com @VKazulkin https://dev.to/vkazulkin https://github.com/Vadym79/ https://de.slideshare.net/VadymKazulkin/ https://www.linkedin.com/in/vadymkazulkin https://www.iplabs.de/ Contact
  • 3.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH About ip.labs 4
  • 4.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Your organization has profound Java and Spring Boot development skills ▪ You have an existing Spring Boot 3 (web) application (in AWS), or you want to develop a new one Current State 5
  • 5.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ You want to use AWS Lambda for hosting your web-based Applications written in Java ▪ To benefit from the advantages of the Serverless architectures ▪ Scalability ▪ Less focus on operational tasks ▪ API Gateway and Lambda offer fully managed approach ▪ Less focus on the code maintenance ▪ Requires securing the application only Desired State 6
  • 6.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH „AWS Lambda SnapStart „ series 7 https://dev.to/vkazulkin/measuring-java-11-lambda-cold-starts-with-snapstart-part-1-first-impressions-30a4 Article series covers they why and what behind Lambda SnapStart and priming techniques including measurements for cold and warm starts with different settings for ▪ Java 11 ▪ Java 17 ▪ Java 21 ▪ Micronaut ▪ Quarkus ▪ Spring Boot 2.7
  • 7.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ I can at best achieve acceptable performance by using pure Java on AWS Lambda ▪ Spring (Boot) performance on AWS Lambda would be bad ▪ Too much runtime annotation processing, reflection, dynamic class loading used ▪ As a result, high cold (application start up) and warm start times Most frequent replies and assumptions 8
  • 8.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ I use NoSQL Amazon DynamoDB for my examples ▪ You can make it work with the same introduced approaches by managing your own database or using Amazon RDS (i.e. PostgreSQL) ▪ Challenges around managing database connections. Possible solutions are: ▪ Using Amazon RDS Proxy and putting Lambda functions into VPC ▪ Using (PostgreSQL) Aurora Serverless v2 Data API ▪ Try out the newly introduced Aurora DSQL (in preview) ▪ You need to re-measure performance though Database usage 9
  • 9.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Demo Application 11 https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot ▪ PUT https://{YOUR_API_GATEWAY_URL}/prod /products/ ▪ GET https://{YOUR_API_GATEWAY_URL}/prod /products/ {productId}
  • 10.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } Spring Boot 3 Application class 13
  • 11.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH public record Product(String id, String name, BigDecimal price) { } Product Entity class 14
  • 12.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @Repository public class DynamoProductDao implements ProductDao { private static final String PRODUCT_TABLE_NAME = System.getenv("PRODUCT_TABLE_NAME"); private static final DynamoDbClient dynamoDbClient = DynamoDbClient.builder().build(); @Override public void putProduct(Product product) { dynamoDbClient.putItem(PutItemRequest.builder() .tableName(PRODUCT_TABLE_NAME) .item(ProductMapper.productToDynamoDb(product)) .build()); } Product Repository DAO class 15
  • 13.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @Override public Optional<Product> getProduct(String id) { GetItemResponse getItemResponse = dynamoDbClient.getItem (GetItemRequest.builder().key(Map.of("PK", AttributeValue.builder().s(id).build())).tableName(PRODUCT_TABLE_NAME).build()); if (getItemResponse.hasItem()) { return Optional.of(ProductMapper.productFromDynamoDB(getItemResponse.item())); } else { return Optional.empty(); } } .. } Product Repository DAO class 16
  • 14.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH public class GetProductByIdHandler implements RequestHandler<APIGatewayProxyRequestEvent, Optional<Product>> { private static final ProductDao productDao = new DynamoProductDao(); @Override public Optional<Product> handleRequest(APIGatewayProxyRequestEvent event, Context context) { String id = event.getPathParameters().get("id"); return productDao.getProduct(id); }} AWS Lambda Function with Java runtime How to develop, run and optimize Spring Boot 3 application on AWS Lambda 17 Invocation of the handeRequest method is the warm start
  • 15.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ You can use Lambda function (implement RequestHandler) interface and inject any types of the Spring beans like services or repositories directly there ▪ I’ll focus more on using more portable solutions using Spring Boot controllers ▪ Portable in the sense of more easily switching between Serverless, Containers (ECS, EKS, Fargate) or pure servers (EC2) on AWS ▪ Only adjust pom.xml, IaC and a small chunk of the source code ▪ Amazon DynamoDB locks you in into AWS Business Logic 18
  • 16.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ AWS Serverless Java Container ▪ AWS Lambda Web Adapter ▪ Spring Cloud Function with AWS Adapter ▪ (Docker) Container Image on AWS Lambda ▪ Optimization techniques ▪ AWS (Lambda) SnapStart ▪ GraalVM Native Image ▪ Cold and warm start measurements for all approaches and optimizations Agenda 19
  • 17.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS Serverless Java Container 20 https://github.com/aws/serverless-java-container/ https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-aws-serverless-java-container
  • 18.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ The AWS Serverless Java Container makes it easier to run Java applications written with frameworks such as Struts, Spring, Spring Boot 2 and 3, or JAX-RS/Jersey in Lambda. ▪ The container provides adapter logic to minimize code changes. Incoming events are translated to the Jakarta EE Servlet specification so that frameworks work as before AWS Serverless Java Container 21
  • 19.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH pom.xml <dependency> <groupId>com.amazonaws.serverless</groupId> <artifactId>aws-serverless-java-container-springboot3</artifactId> </dependency> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> AWS Serverless Java Container 22 exclude Tomcat, as we use Amazon API Gateway
  • 20.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @RestController @EnableWebMvc public class ProductController { @Autowired private DynamoProductDao productDao; @RequestMapping(path = “/products/{id}”, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Optional<Product> getProductById(@PathVariable(“id”) String id) { return productDao.getProduct(id); } @RequestMapping(path = “/products”, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) public void createProduct(@RequestBody Product product) { productDao.putProduct(product); } Spring Boot 3 Product (Rest)Controller class 23
  • 21.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @RequestMapping(path = “/products/{id}”, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Optional<Product> getProductById(@PathVariable(“id”) String id) { return productDao.getProduct(id); } Mapps to the Lambda function in IaC (AWS SAM template) GetProductByIdFunction: Type: AWS::Serverless::Function .... Events: GetRequestById: Type: Api Properties: RestApiId: !Ref MyApi Path: /products/{id} Method: get Spring Boot 3 Rest Controller method mapping to Lambda Function in IaC 24
  • 22.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Automatic proxying through AWS Serverless Java Container AWS SAM Template (template.yaml) GetProductByIdFunction: Type: AWS::Serverless::Function Properties: Handler: com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler CodeUri: target/ aws-lambda-spring-boot-3.4-aws-serverless-java-container… .jar Runtime: java21 MemorySize: 1024 Environment: Variables: MAIN_CLASS: com.amazonaws.Application AWS Serverless Java Container 26 Use generic AWS Serverless Container Spring Boot 3 proxy Lambda function
  • 23.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Self-written Lambda function AWS SAM Template (template.yaml) GetProductByIdFunction: Type: AWS::Serverless::Function Properties: Handler: software.amazonaws.example.product.handler.StreamLambdaHandler::handleRequest CodeUri: target/aws-lambda-spring-boot-3.4-aws-serverless-java-container… .jar Runtime: java21 MemorySize: 1024 AWS Serverless Java Container 27 Or implement our own Lambda function as an alternative
  • 24.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH public class StreamLambdaHandler implements RequestStreamHandler { SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class); @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { handler.proxyStream(inputStream, outputStream, context); } } AWS Serverless Java Container 28
  • 25.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS Lambda Web Adapter 30 https://github.com/awslabs/aws-lambda-web-adapter https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-lambda-web-adapter
  • 26.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ AWS Lambda Web Adapter is a tool written in Rust programming language to run web applications on AWS Lambda. ▪ It allows developers to build web apps (HTTP API) with familiar frameworks (e.g. Express.js, Next.js, Flask, Spring Boot, ASP.NET and Laravel, anything that speaks HTTP 1.1/1.0) and run it on AWS Lambda. ▪ The same Docker image can run also on Amazon EC2, AWS Fargate, and local computers AWS Lambda Web Adapter 31 https://github.com/awslabs/aws-lambda-web-adapter
  • 27.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Run web applications on AWS Lambda ▪ Supports Amazon API Gateway Rest API and HTTP API endpoints, Lambda Function URLs, and Application Load Balancer ▪ Supports all non-HTTP event triggers, such as SQS, SNS, S3, DynamoDB, Kinesis, Kafka, EventBridge, and Bedrock Agents AWS Lambda Web Adapter 32 https://github.com/awslabs/aws-lambda-web-adapter
  • 28.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Can be deployed in several ways: ▪ Lambda functions packaged as Docker Images ▪ Lambda functions packaged as OCI Images ▪ Lambda functions packaged as Zip package for AWS managed runtimes and attached as Lambda layer to your Lambda function AWS Lambda Web Adapter 33 https://github.com/awslabs/aws-lambda-web-adapter
  • 29.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @RestController @EnableWebMvc public class ProductController { @Autowired private DynamoProductDao productDao; @RequestMapping(path = “/products/{id}”, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Optional<Product> getProductById(@PathVariable(“id”) String id) { return productDao.getProduct(id); } @RequestMapping(path = “/products”, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) public void createProduct(@RequestBody Product product) { productDao.putProduct(product); } Spring Boot 3 Product (Rest)Controller class 34
  • 30.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @RequestMapping(path = “/products/{id}”, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Optional<Product> getProductById(@PathVariable(“id”) String id) { return productDao.getProduct(id); } Mapps to the Lambda function in IaC (AWS SAM template) GetProductByIdFunction: Type: AWS::Serverless::Function .... Events: GetRequestById: Type: Api Properties: RestApiId: !Ref MyApi Path: /products/{id} Method: get Spring Boot 3 Rest Controller method mapping to Lambda Function in IaC 35
  • 31.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @RequestMapping(path = “/products”, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) public void createProduct( @RequestBody Product product) { productDao.putProduct(product); } Mapps to the Lambda function in IaC (AWS SAM template) PutProductFunction: Type: AWS::Serverless::Function ... Events: PutRequest: Type: Api Properties: RestApiId: !Ref MyApi Path: /products Method: put Spring Boot 3 Rest Controller method mapping to Lambda Function in IaC 36
  • 32.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS SAM Template (template.yaml) GetProductByIdFunction: Type: AWS::Serverless::Function Properties: Handler: run.sh CodeUri: target/aws-lambda-spring-boot-3.4-lambda-web-adapter… .jar Runtime: java21 MemorySize: 1024 Layers: - !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:23 Environment: Variables: AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap AWS Lambda Web Adapter 37
  • 33.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH run.sh #!/bin/sh exec java -cp "./:lib/*" "software.amazonaws.Application" AWS Lambda Web Adapter 38
  • 34.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Spring Cloud Function with AWS Adapter 41 https://spring.io/projects/spring-cloud-function https://spring.io/projects/spring-cloud-aws https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-spring-cloud-function
  • 35.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Promote the implementation of business logic via functions. ▪ A simple function application (in context or Spring) is an application that contains beans of type Supplier, Java 8 Function interface or Consumer. Spring Cloud Function for AWS Lambda 42 https://spring.io/projects/spring-cloud-function https://spring.io/projects/spring-cloud-aws
  • 36.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task. One of these specific runtime targets can be AWS Lambda. ▪ Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers. Spring Cloud Function for AWS Lambda 43 https://spring.io/projects/spring-cloud-function https://spring.io/projects/spring-cloud-aws
  • 37.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ AWS Request Adapter converts the JSON coming from Lambda function to the HttpServletRequest which then invokes the Spring Dispatcher Servlet which then interacts with our Spring Boot application on API level without starting web server ▪ Then response flows back and AWS Response Adapter converts HttpServletResponse to JSON which Lambda function sends back to API Gateway. Spring Cloud Function for AWS Lambda 45 https://spring.io/projects/spring-cloud-function https://spring.io/projects/spring-cloud-aws
  • 38.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH In AWS Serverless Java Container we saw in pom.xml <dependency> <groupId>com.amazonaws.serverless</groupId> <artifactId> aws-serverless-java-container-springboot3 </artifactId> <version>2.0.0</version> </dependency> Spring Cloud Function for AWS Lambda 46 ▪ If we look into the whole dependency tree, we'll discover another dependency spring- cloud-function-serverless-web that artifact aws-serverless-java- container-springboot3 requires ▪ This is the collaboration effort between Spring and AWS Serverless developers ▪ Spring Boot 3 Application with AWS Serverless Java container is Spring Cloud function application as well
  • 39.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH pom.xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-function-adapter-aws</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-function-web</artifactId> </dependency> Spring Cloud Function for AWS Lambda 47
  • 40.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH pom.xml <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> Spring Cloud Function for AWS Lambda 48 exclude Tomcat, as we use Amazon API Gateway
  • 41.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS SAM Template (template.yaml) GetProductByIdFunction: Type: AWS::Serverless::Function Properties: Handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest CodeUri: target/aws-lambda-spring-boot-3.4-spring-cloud-function-aws… .jar Runtime: java21 MemorySize: 1024 Environment: Variables: MAIN_CLASS: software.amazonaws.Application SPRING_CLOUD_FUNCTION_DEFINITION: getProductByIdHandler Spring Cloud Function for AWS Lambda 49
  • 42.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @Component public class GetProductByIdHandler implements Function<APIGatewayProxyRequestEvent, Product> { @Autowired private DynamoProductDao productDao; ... public APIGatewayProxyResponseEvent apply(APIGatewayProxyRequestEvent requestEvent) { String id = requestEvent.getPathParameters().get("id"); return productDao.getProduct(id); } Spring Cloud Function for AWS Lambda 50 SPRING_CLOUD_FUNCTION_DEFINITION: getProductByIdHandler
  • 43.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Docker Image with Spring Cloud Function for AWS Lambda 51 https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-aws-serverless-java-container-as-lambda-docker-image
  • 44.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Re-use AWS Serverless Java Container ▪ Use Base Java 21 Docker Image from Amazon ECR public.ecr.aws/lambda/java:21 ▪ Docker build image ▪ Docker tag image ▪ Create Amazon ECR repository (if not exists) ▪ Docker push image to Amazon ECR repository Docker Container Image with Spring Cloud Function 52
  • 45.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Dockerfile FROM public.ecr.aws/lambda/java:21 # Copy function code and runtime dependencies from Maven layout COPY target/classes ${LAMBDA_TASK_ROOT} COPY target/classes/lib/* ${LAMBDA_TASK_ROOT}/lib/ Docker Container Image with Spring Cloud Function 53
  • 46.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS SAM Template (template.yaml) GetProductByIdFunction: Type: AWS::Serverless::Function Properties: PackageType: Image ImageUri: !Sub ${AWS::AccountId}.dkr.ecr.eu-central-1.amazonaws.com/spring-boot-3.4- with-aws-serverless-java-container-as-lambda-docker-image:v1 ImageConfig: Command: ["software.amazonaws.example.product.handler.StreamLambdaHandler::handleRequest"] MemorySize: 1024 ….. Docker Container Image with Spring Cloud Function 54
  • 47.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Lambda function lifecycle – a full cold start 55 Sources: Ajay Nair „Become a Serverless Black Belt” https://www.youtube.com/watch?v=oQFORsso2go Tomasz Łakomy "Notes from Optimizing Lambda Performance for Your Serverless Applications“ https://tlakomy.com/optimizing-lambda-performance-for-serverless-applications
  • 48.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Start Firecracker VM (execution environment) ▪ AWS Lambda starts the Java runtime ▪ Java runtime loads and initializes Lambda function code (Lambda handler Java class) ▪ Class loading ▪ Static initializer block of the handler class is executed (i.e. AWS service client creation) ▪ Runtime dependency injection ▪ Just-in-Time (JIT) compilation kicks in ▪ Lambda invokes the handler method 56 Lambda function lifecycle
  • 49.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Demo Application 57 https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot ▪ Used Spring 3.4 with Amazon Corretto Java 21 ▪ Lambda has 1024 MB memory setting ▪ Lambda uses x86 architecture ▪ Used „-XX:+TieredCompilation - XX:TieredStopAtLevel=1” Java compilation option ▪ Info about the experiments: ▪ Approx. 1 hour duration ▪ Approx. first* 100 cold starts ▪ Approx. first 100.000 warm starts *after Lambda function being re-deployed
  • 50.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 1000 2000 3000 4000 5000 6000 7000 8000 9000 AWS Serverless Java Container AWS Lambda Web AdapterSpring Cloud Function with AWS Adapter Docker Image based on AWS Serverless Java Container p50 p75 p90 p99 p99.9 max ms
  • 51.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 1000 2000 3000 4000 5000 6000 7000 8000 9000 AWS Serverless Java Container AWS Lambda Web Adapter Spring Cloud Function with AWS Adapter Docker Image based on AWS Serverless Java Container Pure Java w/o framework usage p50 p75 p90 p99 p99.9 max ms
  • 52.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0,00 5,00 10,00 15,00 20,00 25,00 AWS Serverless Java Container AWS Lambda Web AdapterSpring Cloud Function with AWS Adapter Docker Image based on AWS Serverless Java Container p50 p75 p90 p99 ms
  • 53.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0,00 5,00 10,00 15,00 20,00 25,00 AWS Serverless Java Container AWS Lambda Web Adapter Spring Cloud Function with AWS Adapter Docker Image based on AWS Serverless Java Container Pure Java w/o framework usage p50 p75 p90 p99 ms
  • 54.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 200 400 600 800 1000 1200 1400 1600 1800 2000 AWS Serverless Java Container AWS Lambda Web AdapterSpring Cloud Function with AWS Adapter Docker Image based on AWS Serverless Java Container p99.9 max ms
  • 55.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 200 400 600 800 1000 1200 1400 1600 1800 2000 AWS Serverless Java Container AWS Lambda Web Adapter Spring Cloud Function with AWS Adapter Docker Image based on AWS Serverless Java Container Pure Java w/o framework usage p99.9 max ms
  • 56.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS Lambda SnapStart 66
  • 57.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪Lambda SnapStart can improve startup performance for latency- sensitive applications ▪SnapStart is fully managed AWS Lambda SnapStart 69 https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html
  • 58.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS SnapStart Deployment & Invocation 70 https://dev.to/vkazulkin/measuring-java-11-lambda-cold-starts-with-snapstart-part-1-first-impressions-30a4 https://aws.amazon.com/de/blogs/compute/using-aws-lambda-snapstart-with-infrastructure-as-code-and-ci-cd-pipelines/
  • 59.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Currently available for Lambda managed Java Runtimes (Java 11, 17 and 21), Python and .NET ▪ Not available for all other Lambda runtimes: ▪ Docker Container Image ▪ Custom (Lambda) Runtime (a way to ship GraalVM Native Image) AWS Lambda SnapStart 71 https://github.com/Vadym79/AWSLambdaJavaDockerImage/
  • 60.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH available for: ▪ AWS Serverless Java Container ▪ AWS Lambda Web Adapter ▪ Spring Cloud Function (on AWS Lambda) ▪ (Docker) Container Image on AWS Lambda AWS Lambda SnapStart 72
  • 61.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS SnapStart Deployment & Invocation 74 https://aws.amazon.com/de/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/ Vadym Kazulkin @VKazulkin , ip.labs GmbH C Create Snapshot Firecracker microVM create & restore snapshot
  • 62.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Linux CRIU available since 2012 allows a running application to be paused and restarted at some point later in time, potentially on a different machine. ▪ The overall goal of the project is to support the migration of containers. ▪ When performing a checkpoint, essentially, the full context of the process is saved: program counter, registers, stacks, memory-mapped and shared memory ▪ To restore the application, all this data can be reloaded and (theoretically) it continues from the same point. ▪ Challenges ▪ open files ▪ network connections ▪ sudden change in the value of the system clock ▪ time-based caches CRIU (Checkpoint/Restore in Userspace) 76 https://criu.org/Main_Page
  • 63.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ CRaC is a JDK project that allows you to start Java programs with a shorter time to first transaction, combined with less time and resources to achieve full code speed. ▪ CRaC effectively takes a snapshot of the Java process (checkpoint) when it is fully warmed up, then uses that snapshot to launch any number of JVMs from this captured state. ▪ CRaC is based on CRIU or recently released Warp engine Ideas behind CRaC (Coordinated Restore at Checkpoint) 78 https://www.azul.com/blog/superfast-application-startup-java-on-crac/ https://github.com/CRaC/docs https://foojay.io/today/warp-the-new-crac-engine/
  • 64.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Comparing Firecracker and CRIU snapshotting: ▪ Firecracker snapshotting saves a whole running OS ▪ CRIU snapshotting saves a single process or container ▪ Advantages of the Firecracker snapshot : we don't have to care about file handles because they will be still valid after resume. ▪ Drawbacks the Firecracker snapshot : the need to reseed /dev/random and to sync the system clock. Snapshot /checkpointing and restore Firecracker microVM vs CRIU 79 https://mail.openjdk.org/pipermail/discuss/2021-July/005864.html
  • 65.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS Lambda SnapStart with Priming 82
  • 66.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Pre-load as many Java classes as possible before the SnapStart takes the snapshot ▪ Java loads classes on demand (lazy-loading) ▪ Pre-initialize as much as possible before the SnapStart takes the snapshot ▪ Http Clients (Apache, UrlConnection) and JSON Marshallers (Jackson) require expensive one-time initialization per (Lambda) lifecycle. They both are used when creating Amazon DynamoDbClient ▪ Expensive static (cached) one-time mappings Lambda API Gateway Request Event (received as JSON) to Spring Boot programming model (@RestController) through AWS Serverless Java Container and Spring Cloud Function (with AWS Adapter) frameworks Ideas behind priming 83
  • 67.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS SnapStart Deployment & Invocation 84 https://aws.amazon.com/de/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/ Vadym Kazulkin @VKazulkin , ip.labs GmbH Lambda uses the CRaC APIs for runtime hooks for Priming C Create Snapshot Firecracker microVM create & restore snapshot
  • 68.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS Lambda SnapStart with Priming Approach 1 Priming of DynamoDB request 86
  • 69.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Prime dependencies during initialization phase (when it worth doing) ▪ „Fake“ the calls to pre-initialize „some other expensive stuff“ (this technique is called Priming) ▪ In case of DynamoDB client put the following code outside of the handler method to pre-initialize the HTTP Client and Jackson Marshaller: DynamoDbClient client = DynamoDbClientBuilder.builder().region(Regions.US_WEST_2).build(); GetItemResponse getItemResponse = client.getItem(GetItemRequest.builder() .key(Map.of("PK", AttributeValue.builder().s(id).build())) .tableName(PRODUCT_TABLE_NAME).build()); …… Priming 87 invocation forces HTTP Client and Jackson Marshallers to pre-initialize getProductById (int id) method
  • 70.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @Configuration public class Priming implements Resource { @Autowired private DynamoProductDao productDao; public Priming() { Core.getGlobalContext().register(this); } @Override public void beforeCheckpoint(Context<? extends Resource> context) throws Exception { productDao.getProduct("0"); } @Override public void afterRestore(Context<? extends Resource> context) throws Exception { } } Priming of DynamoDB request 88 • Uses CRaC API: Resource interface with beforeCheckpoint and afterRestore methods • Applies priming to all Lambda functions in the deployment artifact
  • 71.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS Lambda SnapStart with Priming Approach 2 Priming/Proxying the whole web (API Gateway) request without going via network 95
  • 72.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH public class StreamLambdaHandler implements RequestStreamHandler, Resource { SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class); @Autowired private DynamoProductDao productDao; public StreamLambdaHandler() { Core.getGlobalContext().register(this); } @Override public void afterRestore(Context<? extends Resource> context) throws Exception { } 96 AWS Serverless Java Container (Priming of API Gateway request)
  • 73.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { handler.proxyStream(inputStream, outputStream, context); } @Override public void beforeCheckpoint(Context<? extends Resource> context) throws Exception { handler.proxy(getAwsProxyRequest(), new MockLambdaContext()); } 97 AWS Serverless Java Container (Priming of API Gateway request)
  • 74.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH private static AwsProxyRequest getAwsProxyRequest () { final AwsProxyRequest awsProxyRequest = new AwsProxyRequest (); awsProxyRequest.setHttpMethod("GET"); awsProxyRequest.setPath("/products/0"); awsProxyRequest.setResource("/products/{id}"); awsProxyRequest.setPathParameters(Map.of("id","0")); final AwsProxyRequestContext awsProxyRequestContext = new AwsProxyRequestContext(); final ApiGatewayRequestIdentity apiGatewayRequestIdentity= new ApiGatewayRequestIdentity(); apiGatewayRequestIdentity.setApiKey("blabla"); awsProxyRequestContext.setIdentity(apiGatewayRequestIdentity); awsProxyRequest.setRequestContext(awsProxyRequestContext); return awsProxyRequest; } } 98 AWS Serverless Java Container (Priming of API Gateway request) Simulate ApiGatewayProxyRequest
  • 75.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH public class GetProductByIdHandler implements RequestHandler<APIGatewayProxyRequestEvent, Optional<Product> { private static final ProductDao productDao = new DynamoProductDao(); @Override public Optional<Product> handleRequest(APIGatewayProxyRequestEvent requestEvent, Context context) { String id = requestEvent.getPathParameters().get("id"); return productDao.getProduct(id); } } AWS Lambda with pure Java 99
  • 76.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH This type of priming doesn’t work for AWS Web Adapter as doesn’t offer low-level API to stream/proxy the API Gateway web request 100
  • 77.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH @Component public class GetProductByIdHandler implements Function<APIGatewayProxyRequestEvent, Product>, Resource { @Autowired private DynamoProductDao productDao; public GetProductByIdWithDynamoDBRequestPrimingHandler () { Core.getGlobalContext().register(this); } ... public Product apply(APIGatewayProxyRequestEvent requestEvent) { String id = requestEvent.getPathParameters().get("id"); return productDao.getProduct(id); } 101 Spring Cloud Function for AWS (Priming of API Gateway request)
  • 78.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 1000 2000 3000 4000 5000 6000 7000 8000 AWS Serverless Java Container AWS Lambda Web Adapter Spring Cloud Function with AWS Adapter w/o SnapStart w SnapStart no Priming w SnapStart & DynamoDB request priming w SnapStart and API Gateway request priming p90 ms
  • 79.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 1000 2000 3000 4000 5000 6000 7000 8000 AWS Serverless Java Container AWS Lambda Web AdapterSpring Cloud Function with AWS Adapter Pure Java w/o framework usage w/o SnapStart w SnapStart no Priming w SnapStart & DynamoDB request priming w SnapStart & API Gateway request priming p90 ms
  • 80.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 500 1000 1500 2000 2500 3000 3500 AWS Serverless Java Container AWS Lambda Web Adapter Spring Cloud Function with AWS Adapter w/o SnapStart w SnapStart no Priming w SnapStart & DynamoDB request priming w SnapStart & API Gateway request priming max value ms
  • 81.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Lambda SnapStart Priming Guide 108 guide aims to explain techniques for priming Java applications. It assumes a base understanding of AWS Lambda, Lambda SnapStart, and CRaC. https://github.com/marksailes/snapstart-priming-guide
  • 82.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS Lambda Profiler Extension for Java High performance Serverless Java on AWS 109 https://github.com/aws/aws-lambda-java-libs/tree/main/experimental/aws-lambda-java-profiler https://dev.to/aws-heroes/aws-lambda-profiler-extension-for-java-part-2-improving-lambda-performance-with-lambda-snapstart-4p06
  • 83.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Demo Application 110 https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot ▪ Used Spring 3.4 with Amazon Corretto Java 21 ▪ Lambda has 1024 MB memory setting ▪ Lambda uses x86 architecture ▪ Used „-XX:+TieredCompilation - XX:TieredStopAtLevel=1” Java compilation option ▪ Info about the experiments: ▪ Approx. 1 hour duration ▪ Approx. first* 100 cold starts ▪ Approx. first 100.000 warm starts *after Lambda function being re-deployed
  • 84.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS SnapStart Deployment & Invocation 111 https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html https://aws.amazon.com/de/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/ Firecracker microVM create & restore snapshot
  • 85.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS SnapStart tiered cache 112 https://dev.to/aws-builders/aws-snapstart-part-17-impact-of-the-snapshot-tiered-cache-on-the-cold-starts-with-java-21-52ef • Due to the effect of snapshot tiered cache, cold start times reduces with the number of invocations • After certain number of invocations reached the cold start times becomes stable
  • 86.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH AWS Lambda under the Hood 113 https://www.infoq.com/articles/aws-lambda-under-the-hood/ https://www.infoq.com/presentations/aws-lambda-arch/
  • 87.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Demo Application 114 https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot ▪ Used Spring 3.4 with Amazon Corretto Java 21 ▪ Lambda has 1024 MB memory setting ▪ Lambda uses x86 architecture ▪ Used „-XX:+TieredCompilation - XX:TieredStopAtLevel=1” Java compilation option ▪ Info about the experiments: ▪ Approx. 1 hour duration ▪ Approx. last 70 from 100 cold starts
  • 88.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 500 1000 1500 2000 2500 3000 3500 4000 AWS Serverless Java Container all 100 AWS Serverless Java Container last 70 AWS Lambda Web Adapter all 100 AWS Lambda Web Adapter last 70 Spring Cloud Function with AWS Adapter all 100 Spring Cloud Function with AWS Adapter last 70 w SnapStart no Priming w SnapStart & DynamoDB request priming w SnapStart and API Gateway request priming p90 ms
  • 89.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Other optimizations 116 • Measure the cold and warm start of Lambda with SnapStart enabled (and with priming) for arm64 architecture and compare it to x86 • Measure the impact of the different Java compilation options and memory settings • Measure the impact of setting the different synchronous HTTP clients DynamoDbClient client = DynamoDbClient.builder().region(Region.EU_CENTRAL_1) . httpClient(ApacheHttpClient.create()) //.httpClient(UrlConnectionHttpClient.create()) //.httpClient(AwsCrtHttpClient.create()) .build();
  • 90.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Lambda x86_64 vs arm64 architecture 118 https://aws.amazon.com/lambda/pricing/?nc1=h_ls For the same memory setting Lambda and Lambda function execution duration the choice of arm64 over x86_64 architecture is approx. 25% cheaper
  • 91.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 500 1000 1500 2000 2500 3000 3500 4000 4500 w/o AWS SnapStart with AWS SnapStart w/o priming with AWS SnapStart with priming Cold starts of the pure Lambda function (no framework in use) with Java 21runtime with 1024 MB memory setting, Apache Http Client, compilation -XX:+TieredCompilation -XX:TieredStopAtLevel=1 for p90 x86_64 arm64 ms https://dev.to/aws-builders/aws-snapstart-part-16-measuring-cold-and-warm-starts-with-java-21-using-different-asynchronous-http-clients-4n2 +8,6% +23,9% +24,8%
  • 92.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 6,40 6,60 6,80 7,00 7,20 7,40 7,60 7,80 8,00 w/o AWS SnapStart with AWS SnapStart w/o priming with AWS SnapStart with priming Warm starts of pure Lambda function (no framework in use) with Java 21runtime with 1024 MB memory setting, Apache Http Client, compilation -XX:+TieredCompilation -XX:TieredStopAtLevel=1 for p90 x86_64 arm64 ms https://dev.to/aws-builders/aws-snapstart-part-16-measuring-cold-and-warm-starts-with-java-21-using-different-asynchronous-http-clients-4n2 +9,1% +10,1% +4,8%
  • 93.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ SnapStart supports the Java 11, 17 and 21 (Corretto), Python and .NET managed runtime only ▪ Deployment with SnapStart enabled takes more than 2-2,5 minutes additionally ▪ Snapshot is deleted from cache if Lambda function is not invoked for 14 days ▪ SnapStart currently does not support : ▪ Provisioned concurrency ▪ Amazon Elastic File System (Amazon EFS) ▪ Ephemeral storage greater than 512 MB AWS SnapStart Challenges & Limitations 121 https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html
  • 94.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Perform Priming out of the box without writing the logic on our own ▪ If snapshot not found do regular cold start and create snapshot under the hood ▪ Snapshot creation on first Lambda function invocation instead of during the deployment phase ▪ Regular cold start as long as the snapshot hasn’t been fully taken ▪ Trade off between duration of the deployment phase and several regular bigger cold starts until the snapshot is taken and SnapStart becomes effective (and therefore much shorter cold starts) AWS SnapStart Possible Next Steps 122 https://dev.to/aws-builders/reducing-cold-starts-on-aws-lambda-with-java-runtime-future-ideas-about-snapstart-graalvm-and-co-3a1b
  • 95.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ AWS Serverless Java Container and AWS Lambda Web Adapter offer similar functionality ▪ AWS Serverless Java Container offers a bit lower warm start times ▪ AWS Serverless Java Container offers more flexibility to apply priming techniques to the individual Lambda functions in the deployment artifact through its API ▪ AWS Lambda Web Adapter offers lower cold start times (with SnapStart and priming) ▪ AWS Lambda Web Adapter offers more event triggers (non HTTP events) ▪ Spring Cloud Function makes sense if already in use or you have a non-HTTP application (i.e. messaging) ▪ AWS Serverless Java Container (since 2.0.2) and Spring Cloud Function both support GraalVM Native Image ▪ Using Docker Image leads to the highest cold start. ▪ You can use the recent Java version though and use jlink to reduce the size of the JDK Framework Comparison 123 https://www.azul.com/blog/superfast-application-startup-java-on-crac/ https://github.com/CRaC/docs
  • 96.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 124
  • 97.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH GraalVM Architecture 126
  • 98.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH GraalVM Ahead-of-Time Compilation 127 Source: Oleg Šelajev, Thomas Wuerthinger, Oracle: “Deep dive into using GraalVM for Java and JavaScript” https://www.youtube.com/watch?v=a-XEZobXspo
  • 99.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Promise: Java Function compiled into a native executable using GraalVM Native Image significantly reduces ▪ “cold start” times ▪ memory footprint GraalVM Native Image 129
  • 100.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ AWS doesn’t provide GraalVM (Native Image) as Java Runtime out of the box ▪ AWS provides Custom Runtime Option Current Challenges with Native Executable using GraalVM 130
  • 101.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Custom Lambda Runtimes 131 https://github.com/Vadym79/AWSLambdaGraalVMNativeImage
  • 102.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪Spring Boot 3+ provides official support for compiling Spring Boot applications into the GraalVM Native Image ▪Spring AOT (Ahead-of-Time Processing) is a process that analyzes your application at build-time and generate an optimized version of it. It is a mandatory step to run a Spring ApplicationContext in a native image Spring Supprt for GraalVM Native Image 134 https://docs.spring.io/spring-boot/maven-plugin/aot.html
  • 103.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH pom.xml <profiles> <profile> <id>native</id> <activation> <property> <name>native</name> </property> </activation> .... </profile> </profiles> 135 mvn clean package -Pnative https://github.com/Vadym79/AWSLambdaJavaWithSpringBoot/tree/master/spring-boot-3.4-with-aws-serverless-java-container-as-graalvm-native-image GraalVM Native Image with AWS Serverless Java Container
  • 104.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH pom.xml <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <id>process-aot</id> <goals> <goal>process-aot</goal> </goals> </execution> </executions> </plugin> 136 GraalVM Native Image with AWS Serverless Java Container
  • 105.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH pom.xml <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <configuration> <mainClass>software.amazonaws.Application</mainClass> </configuration> <executions> <execution> <id>build-native</id> <goals> <goal>compile-no-fork</goal> </goals> 137 <phase>package</phase> </execution> </executions> </plugin> GraalVM Native Image with AWS Serverless Java Container
  • 106.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.annotation.RegisterReflection ForBinding; @Configuration @RegisterReflectionForBinding({DateTime.class, APIGatewayProxyRequestEvent.class, HashSet.class, APIGatewayProxyRequestEvent.ProxyRequestContext.class, APIGatewayProxyRequestEvent.RequestIdentity.class, Product.class, Products.class}) @ImportRuntimeHints(ApplicationConfiguration.Application RuntimeHintsRegistrar.class) GraalVM Native Image with AWS Serverless Java Container 139 public class ApplicationConfiguration { public static class ApplicationRuntimeHintsRegistrar implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { hints.reflection() .registerType(Product.class, PUBLIC_FIELDS, INVOKE_PUBLIC_METHODS, INVOKE_PUBLIC_CONSTRUCTORS ; } } }
  • 107.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 850 860 870 880 890 900 910 920 930 940 950 GraalVM 23 Native Image with AWS Serverless Java Container p50 p75 p90 p99 p99.9 max 1024 MB memory ms
  • 108.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 20 40 60 80 100 120 140 160 GraalVM 23 Native Image with AWS Serverless Java Container p50 p75 p90 p99 p99.9 max 1024 MB memory ms
  • 109.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 500 1000 1500 2000 2500 AWS Serverless Java Container last 70 AWS Lambda Web Adapter last 70 Spring Cloud Function with AWS Adapter last 70 GraalVM 23 Native Image with AWS Serverless Java Container w SnapStart no Priming w SnapStart & DynamoDB priming w SnapStart & API Gateway request priming p90
  • 110.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 500 1000 1500 2000 2500 3000 3500 AWS Serverless Java Container AWS Lambda Web AdapterSpring Cloud Function with AWS Adapter GraalVM 23 Native Image with AWS Serverless Java Container w/o SnapStart w SnapStart no Priming w SnapStart & DynamoDB priming w SnapStart & API Gateway request priming max value ms
  • 111.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 100 200 300 400 500 600 700 800 900 1000 GraalVM 23 Native Image with AWS Serverless Java Container GraalVM 23 Native Image with Pure Java w/o framework usage p50 p75 p90 p99 p99.9 max 1024 MB memory ms
  • 112.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 0 20 40 60 80 100 120 140 160 GraalVM 23 Native Image with AWS Serverless Java Container GraalVM 23 Native Image with Pure Java w/o framework usage p50 p75 p90 p99 p99.9 max 1024 MB memory ms
  • 113.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Other optimizations 147 AWS CRT Client supports GraalVM Native Image DynamoDbClient client = DynamoDbClient.builder(). region(Region.EU_CENTRAL_1) ..httpClient(AwsCrtHttpClient.create()) .build(); https://aws.amazon.com/de/blogs/developer/aws-crt-client-for-java-adds-graalvm-native-image-support/
  • 114.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ GraalVM is really powerful and has a lot of potential ▪ GraalVM Native Image improves cold starts and memory footprint significantly ▪ GraalVM Native Image is currently not without challenges ▪ AWS Lambda Custom Runtime requires Linux executable only ▪ Building Custom Runtime requires some additional effort ▪ e.g. you need a scalable CI/CD pipeline to build memory-intensive native image ▪ Build time is a factor ▪ You need to carefully test to avoid runtime errors GraalVM Conclusion 148
  • 115.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Frameworks and libraries Ready for GraalVM Native Image 149 https://www.graalvm.org/native-image/libraries-and-frameworks/
  • 116.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH “Spring Boot 3 application on AWS Lambda” series 150 Article series covers different ways to write Spring Boot 3 application on AWS Lambda ▪ AWS Serverless Java Container ▪ AWS Lambda Web Adapter ▪ Spring Cloud Functions ▪ Custom Docker Image ▪ GraalVM Native Image Cold and warm start time measurements are also provided https://dev.to/aws-builders/spring-boot-3-application-on-aws-lambda-part-1-introduction-to-the-series-2m5g
  • 117.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH “Data API for Amazon Aurora Serverless v2 with AWS SDK for Java” series 151 Article series also covers cold and warm start time measurements and optimization techniques https://dev.to/aws-builders/data-api-for-amazon-aurora-serverless-v2-with-aws-sdk-for-java-part-1-introduction-and-set-up-of-the-sample-application-3g71
  • 118.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Word of caution 152 Re-measure for your use case! Even with my examples measurements might already produce different results due to: ▪ Lambda Amazon Corretto Java 21 managed runtime minor version changes ▪ Spring Boot 3 (minor) version updates ▪ Lambda SnapStart snapshot create and restore improvements ▪ Firecracker VM improvements ▪ GraalVM and Native Image improvements and (minor) version updates ▪ There are still servers behind Lambda ▪ Java Memory Model impact (L or RAM caches hits and misses)
  • 119.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ Spring Boot natively supports CRaC ▪ For Checkpoint: -Dspring.context.checkpoint=onRefresh -XX:CRaCCheckpointTo={path} ▪ For Restore: -XX:CRaCRestoreFrom={path} ▪ Amazon Corretto doesn’t support CRaC ▪ Azul Zulu OpenJDK supports CRaC ▪ Can be deployed on Lambda with Docker Container Image ▪ Currently very complicated and only partially possible with hacks and many trade-offs ▪ Mounting EFS for CRaC snapshot. Requires Lambda being in VPC and managing EFS throughput Spring Boot Native CRaC Support 153 https://github.com/CRaC/example-lambda/issues/7
  • 120.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH ▪ With AWS SnapStart and GraalVM Native Image you can reduce cold start times of the Spring Boot 3 application on AWS Lambda to the acceptable values ▪ If you’re willing to accept slightly higher cold and warm start times for certain the Lambda function(s) and solid priming is applicable -> use fully managed AWS SnapStart with priming ▪ If a very high performance for certain the Lambda function(s) is really crucial for your business -> go for GraalVM Native Image Wrap up and personal suggestions 154
  • 121.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH Project Leyden 155 The primary goal of this Project is to improve the startup time, time to peak performance, and footprint of Java programs. https://www.youtube.com/watch?v=teXijm79vno https://openjdk.org/projects/leyden/
  • 122.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH FAQ Ask me Anything 156
  • 123.
    Vadym Kazulkin |@VKazulkin |ip.labs GmbH 157 Thank you