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