<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vadym Kazulkin</title>
    <description>The latest articles on Forem by Vadym Kazulkin (@vkazulkin).</description>
    <link>https://forem.com/vkazulkin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F501061%2Ffa23b5ee-9c5f-48bd-b3bb-341a26a773c6.JPG</url>
      <title>Forem: Vadym Kazulkin</title>
      <link>https://forem.com/vkazulkin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vkazulkin"/>
    <language>en</language>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and Aurora DSQL - Part 6 Using GraalVM Native Image</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Wed, 22 Apr 2026 14:02:36 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-6-34ni</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-6-34ni</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt;, we introduced our sample application. In parts 2-5, we measured Lambda function performance using different approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;without activation of Lambda SnapStart&lt;/li&gt;
&lt;li&gt;with activation of Lambda SnapStart, but without using any priming techniques&lt;/li&gt;
&lt;li&gt;with activation of Lambda SnapStart and using different priming techniques&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We observed that by activating the SnapSart and applying different priming techniques, we could significantly further reduce the Lambda cold start times. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. Moreover, we could significantly reduce the maximal value for the Lambda warm start times.&lt;/p&gt;

&lt;p&gt;In this article, we'll introduce another approach to improve the performance of the Lambda function - GraalVM Native Image.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;This article assumes prior knowledge of GraalVM and its native image capabilities. For a concise overview of them and how to get both installed, please refer to the following articles: &lt;a href="https://www.graalvm.org/latest/introduction/" rel="noopener noreferrer"&gt;Introduction to GraalVM&lt;/a&gt;, &lt;a href="https://www.graalvm.org/22.2/docs/introduction/" rel="noopener noreferrer"&gt;GraalVM Architecture&lt;/a&gt;, and &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/" rel="noopener noreferrer"&gt;GraalVM Native Image&lt;/a&gt; or read my article &lt;a href="https://dev.to/aws-builders/lambda-function-with-graalvm-native-image-part-1-introduction-to-graalvm-and-its-native-image-capabilities-5d17"&gt;Introduction to GraalVM and its native image capabilities&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install GraalVM and native image, please follow the instructions in the article &lt;a href="https://www.graalvm.org/latest/getting-started/#installing" rel="noopener noreferrer"&gt;Installing GraalVM&lt;/a&gt;. In my example, I used the 25.0.2-graal version, but you can use the newest one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application with JDBC and Hikari connection pool using GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-aurora-dsql" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt;, but adjust it. The goal is to be able to build GraalVM Native Image and deploy it on AWS Lambda as a Custom Runtime.&lt;/p&gt;

&lt;p&gt;Here is the final version of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image" rel="noopener noreferrer"&gt;aws-lambda-java-25-aurora-dsql-as-graalvm-native-image&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;Let's go step-by-step through the changes compared to the initial application from part 1. The business logic (Entity, ProductDao, and the Lambda handlers) remains completely the same.  All changes are made in the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt;, &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/template.yaml" rel="noopener noreferrer"&gt;AWS SAM template&lt;/a&gt;, and by providing additional GraalVM configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making sample application GraalVM Native Image capable
&lt;/h2&gt;

&lt;p&gt;For our sample application to run as a GraalVM Native Image, we need to declare all classes whose objects will be instantiated by reflection. These classes needed to be known by the AOT compiler at compile time. This happens in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/src/main/reflect-config.json" rel="noopener noreferrer"&gt;reflect-config.json&lt;/a&gt;.  As we can see, we need to declare there the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; all our Lambda functions like GetProductByIdHandler] and CreateProductHandler&lt;/li&gt;
&lt;li&gt; entities like Product that Jackson converts from JSON payload and back&lt;/li&gt;
&lt;li&gt; APIGatewayProxyRequestEvent and all its inner classes because we declared this event type as a request event in our Lambda functions, like GetProductByIdHandler and CreateProductHandler&lt;/li&gt;
&lt;li&gt;org.joda.time.DateTime, which will be used to convert a timestamp from a string and back. Such a timestamp is a part of the API Gateway proxy request and response events. In my opinion, it's time to switch from  &lt;a href="https://www.joda.org/joda-time/" rel="noopener noreferrer"&gt;Joda-Time&lt;/a&gt; to the Java Date/Time API for this.&lt;/li&gt;
&lt;li&gt;classes for Hikari Connection Pool creation (HikariConfig, HikariDataSource, ConcurrentBag$IConcurrentBagEntry[])&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are multiple ways, how, and where to define GraalVM Native configuration, like reflection configuration. For this, I refer you to the article &lt;a href="https://www.graalvm.org/22.2/reference-manual/native-image/guides/build-with-reflection/" rel="noopener noreferrer"&gt;Build a Native Executable with Reflection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We used these ways to define reflection configuration for the dependencies in use : &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/src/main/resources/META-INF/native-image/org.postgresql/postgresql/reflect-config.json" rel="noopener noreferrer"&gt;postgresql&lt;/a&gt; and &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/src/main/resources/META-INF/native-image/software.amazon.dsql/aurora-dsql-jdbc-connector/reflect-config.json" rel="noopener noreferrer"&gt;aurora-dsql-jdbc-connector&lt;/a&gt;. Many providers of the open source libraries ship this configuration for the GraalVM metadata directly. Unfortunately, not all of them do it, so we don't need to define it on our own.&lt;/p&gt;

&lt;p&gt;To avoid errors with Loggers during the initialization described in the article &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/ClassInitialization/" rel="noopener noreferrer"&gt;Class Initialization in Native Image&lt;/a&gt;, we need to add GraalVM Native Image build arguments in the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/src/main/resources/META-INF/native-image/org.slf4j/slf4j-simple/native-image.properties" rel="noopener noreferrer"&gt;native-image.properties&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;--allow-incomplete-classpath &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--initialize-at-build-time=org.slf4j.simple.SimpleLogger,&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;      &lt;span class="s"&gt;org.slf4j.LoggerFactory&lt;/span&gt;
    &lt;span class="err"&gt;--&lt;/span&gt; &lt;span class="py"&gt;--trace-class-initialization&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.slf4j.simple.SimpleLogger,&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;      &lt;span class="s"&gt;org.slf4j.LoggerFactory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The native-image.properties should be placed in the META-INF/native-image/${MavenGroupIid}/${MavenArtifactId}.&lt;/p&gt;

&lt;p&gt;As we use slf4j-simple Logger in our application, we need to place native-image.properties in the path META-INF/native-image/org.slf4j/slf4j-simple.&lt;br&gt;
If you use another Logger implementation (e.g., log4j or logback), you need to adjust this file and place it accordingly.&lt;/p&gt;

&lt;p&gt;Also, by default, no resources (schemas, services, and others) will be a part of the native image. We need to define them manually. There are multiple ways to &lt;a href="https://www.graalvm.org/jdk21/reference-manual/native-image/dynamic-features/Resources/" rel="noopener noreferrer"&gt;Include Resources in Native Image &lt;/a&gt;. We have to place them in the resource-config.json file in the META-INF/native-image/${MavenGroupIid}/${MavenArtifactId}. We need it to include the file containing the correct implementation of the java.sql.Driver. In our case, it's aurora-dsql-jdbc-connector. We defined it in the following &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/src/main/resources/META-INF/native-image/software.amazon.dsql/aurora-dsql-jdbc-connector/resource-config.json" rel="noopener noreferrer"&gt;resource-config.json&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To save the manual work of defining all this metadata, you can use &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/" rel="noopener noreferrer"&gt;GraalVM Tracing Agent&lt;/a&gt; to generate it for you.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lambda Custom Runtime
&lt;/h2&gt;

&lt;p&gt;There is no managed GraalVM (Native Image) on AWS Lambda. To deploy the native image on AWS Lambda, we need a &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html" rel="noopener noreferrer"&gt;custom runtime&lt;/a&gt;. For this, we need to package everything into a file with a &lt;strong&gt;.zip&lt;/strong&gt; extension, which includes the file with the name &lt;strong&gt;bootstrap&lt;/strong&gt;. This file can either be the GraalVM Native Image or contain instructions on how to invoke the GraalVM Native Image placed in another file. We'll use the latter way; let's explore it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;We'll build GraalVM Native Image automatically in the package phase defined in the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt;. The relevant part is defined in the following plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.graalvm.nativeimage&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;native-image-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;21.2.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;native-image&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;package&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;skip&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/skip&amp;gt;&lt;/span&gt;              
       &lt;span class="nt"&gt;&amp;lt;mainClass&amp;gt;&lt;/span&gt;
           com.formkiq.lambda.runtime.graalvm.LambdaRuntime
       &lt;span class="nt"&gt;&amp;lt;/mainClass&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;imageName&amp;gt;&lt;/span&gt;
           aws-lambda-java-25-with-aurora-dsql-as-graalvm-native-image 
       &lt;span class="nt"&gt;&amp;lt;/imageName&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;buildArgs&amp;gt;&lt;/span&gt;
         --no-fallback
         --enable-http
         -H:ReflectionConfigurationFiles=../src/main/reflect-config.json
      &lt;span class="nt"&gt;&amp;lt;/buildArgs&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;strong&gt;native-image-maven-plugin&lt;/strong&gt; from &lt;em&gt;org.graalvm.nativeimage&lt;/em&gt; tools and execute native-image in the package phase. You can also use the alternative &lt;a href="https://graalvm.github.io/native-build-tools/latest/maven-plugin.html" rel="noopener noreferrer"&gt;native-maven-plugin&lt;/a&gt; plugin, whose configuration is very similar. This plugin requires the definition of the main class, which a Lambda function doesn't have. That's why we use &lt;a href="https://github.com/formkiq/lambda-runtime-graalvm" rel="noopener noreferrer"&gt;Lambda Runtime GraalVM&lt;/a&gt; and define its main class &lt;em&gt;com.formkiq.lambda.runtime.graalvm.LambdaRuntime&lt;/em&gt;. Lambda Runtime GraalVM is a Java library that makes it easy to convert AWS Lambda written in the Java programming language to the GraalVM. We defined it previously in pom.xml as a dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.formkiq&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;lambda-runtime-graalvm&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.6.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then give the native image name &lt;strong&gt;aws-lambda-java-25-with-aurora-dsql-as-graalvm-native-image&lt;/strong&gt; (the default one will also be an artifact name). After it, we include some GraalVM Native Image arguments and previously defined &lt;strong&gt;reflection-config&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;buildArgs&amp;gt;&lt;/span&gt;
    --no-fallback
    --enable-http
   -H:ReflectionConfigurationFiles=../src/main/reflect-config.json
&lt;span class="nt"&gt;&amp;lt;/buildArgs&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To zip the built GraalVM Native Image as function.zip required by Lambda Custom Runtime, we use the maven-assembly plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-assembly-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;native-zip&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;package&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;single&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;inherited&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/inherited&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;finalName&amp;gt;&lt;/span&gt;function&lt;span class="nt"&gt;&amp;lt;/finalName&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;appendAssemblyId&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/appendAssemblyId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;descriptors&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;descriptor&amp;gt;&lt;/span&gt;src/assembly/native.xml&lt;span class="nt"&gt;&amp;lt;/descriptor&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/descriptors&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;finalName&lt;/em&gt; is the name of the zip file, in our case, &lt;strong&gt;function&lt;/strong&gt;. We also include &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/src/assembly/native.xml" rel="noopener noreferrer"&gt;native.xml&lt;/a&gt; descriptor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;assembly&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;native-zip&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;formats&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;format&amp;gt;&lt;/span&gt;zip&lt;span class="nt"&gt;&amp;lt;/format&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/formats&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;baseDirectory/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;fileSets&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;fileSet&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;src/shell/native&lt;span class="nt"&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;outputDirectory&amp;gt;&lt;/span&gt;/&lt;span class="nt"&gt;&amp;lt;/outputDirectory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;useDefaultExcludes&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/useDefaultExcludes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;fileMode&amp;gt;&lt;/span&gt;0775&lt;span class="nt"&gt;&amp;lt;/fileMode&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;bootstrap&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/fileSet&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;fileSet&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;target&lt;span class="nt"&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;outputDirectory&amp;gt;&lt;/span&gt;/&lt;span class="nt"&gt;&amp;lt;/outputDirectory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;useDefaultExcludes&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/useDefaultExcludes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;fileMode&amp;gt;&lt;/span&gt;0775&lt;span class="nt"&gt;&amp;lt;/fileMode&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;aws-lambda-java-25-with-aurora-dsql-as-graalvm-native-image&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/fileSet&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/fileSets&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/assembly&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This descriptor defines what files from which directories with what permissions will be added to the zip as an assembly format. &lt;em&gt;fileMode&lt;/em&gt; equal to &lt;strong&gt;0775&lt;/strong&gt; means it has permission to be executable on the Linux operating system.  We include previously built GraalVM Native Image with the name &lt;strong&gt;aws-lambda-java-25-with-aurora-dsql-as-graalvm-native-image&lt;/strong&gt; there. We also include the already defined &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/src/shell/native/bootstrap" rel="noopener noreferrer"&gt;bootstrap&lt;/a&gt; file, which basically invokes the GraalVM Native Image :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LAMBDA_TASK_ROOT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

./aws-lambda-java-25-with-aurora-dsql-as-graalvm-native-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end, we have to build GraalVM Native Image packaged as a zip file, which can be built as a Lambda Custom Runtime with &lt;code&gt;mvn clean package&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying GraalVM Native Image as a Lambda Custom Runtime
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/template.yaml" rel="noopener noreferrer"&gt;AWS SAM template&lt;/a&gt;, we set the Lambda runtime as &lt;strong&gt;provided.al2023&lt;/strong&gt;, which is the newest version of the &lt;a href="https://docs.aws.amazon.com/linux/al2023/ug/lambda.html" rel="noopener noreferrer"&gt;custom runtime&lt;/a&gt;, and provide the path to the previously built GraalVM Native Image as target/function.zip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;target/function.zip&lt;/span&gt;
    &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provided.al2023&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are ready to deploy our application with &lt;code&gt;sam deploy -g&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with JDBC and Hikari connection pool using GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql-as-graalvm-native-image/template.yaml" rel="noopener noreferrer"&gt;GetProductById&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt;. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEDQGVNI25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;We designed the experiment exactly as described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with provided:al2023.v124 version, and the deployed artifact size of this application was 20.459 KB.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Custom Runtime with GraalVM Native Image&lt;/td&gt;
&lt;td&gt;629&lt;/td&gt;
&lt;td&gt;802&lt;/td&gt;
&lt;td&gt;885&lt;/td&gt;
&lt;td&gt;943&lt;/td&gt;
&lt;td&gt;945&lt;/td&gt;
&lt;td&gt;946&lt;/td&gt;
&lt;td&gt;4.65&lt;/td&gt;
&lt;td&gt;5.16&lt;/td&gt;
&lt;td&gt;5.64&lt;/td&gt;
&lt;td&gt;9.16&lt;/td&gt;
&lt;td&gt;136.50&lt;/td&gt;
&lt;td&gt;668&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Sample application with Hibernate and Hikari connection pool and using GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;The same is true for the sample application using JDBC instead of Hibernate. We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-hibernate-aurora-dsql" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt;, but adjust it. The goal is to be able to build GraalVM Native Image and deploy it on AWS Lambda as a Custom Runtime.&lt;/p&gt;

&lt;p&gt;Here is the final version of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image" rel="noopener noreferrer"&gt;aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image&lt;/a&gt; application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making sample application GraalVM Native Image capable
&lt;/h2&gt;

&lt;p&gt;Many steps (creating a native image zip, building, and deploying) described above are implemented in exactly the same way. Additional complexity comes from using the Hibernate framework, which doesn't ship native image metadata. Also, Hibernate comes with a lot of internal loggers, which usually don't play well with the native image. It took me a lot of time to figure out the required GraalVM metadata (native-image.properties and reflect.json). You can review them &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/src/main/resources/META-INF/native-image/org.hibernate.orm" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I also had to include the whole package &lt;em&gt;org.hibernate.event.spi&lt;/em&gt; into the native image, see &lt;code&gt;-H:Preserve=package=org.hibernate.event.spi&lt;/code&gt; in the native image configuration in the pom.xml.&lt;/p&gt;

&lt;p&gt;It's probable that if you update the Hibernate version in use, something will break. And as a result, you'll need to adjust the native image metadata, for example, by re-running the &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/" rel="noopener noreferrer"&gt;GraalVM Tracing Agent&lt;/a&gt; to generate them for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;The biggest challenge was the fact that Hibernate uses &lt;a href="https://bytebuddy.net/" rel="noopener noreferrer"&gt;Byte Buddy&lt;/a&gt;. Byte Buddy is a code generation and manipulation library for creating and modifying Java classes during the runtime of a Java application, without the help of a compiler. This obviously doesn't work at runtime in the GraalVM native image. Hibernate also ships no-op &lt;em&gt;org.hibernate.bytecode.internal.none.BytecodeProviderImpl&lt;/em&gt;, but for many versions, it doesn't allow its configuration from outside. The Byte Buddy implementation was always found in the classpath and broke at runtime. Frameworks like Quarkus and Spring Boot can do this magic with the AOT, but as I don't use them, I was seeking the possibilities of how to solve it. Please, read my &lt;a href="https://discourse.hibernate.org/t/how-to-correctly-disable-byte-buddy-for-graalvm-native-image/12163/6" rel="noopener noreferrer"&gt;discussion&lt;/a&gt; in the Hibernate forum.&lt;/p&gt;

&lt;p&gt;Even if I'm not very happy with the solution, which feels more like a workaround, I'll describe how I solved it. &lt;/p&gt;

&lt;p&gt;I first defined &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/src/main/resources/META-INF/services/org.hibernate.bytecode.spi.BytecodeProvider" rel="noopener noreferrer"&gt;org.hibernate.bytecode.spi.BytecodeProvider&lt;/a&gt; with a no-op bytecode implementation in my application. Then I built the uber-jar with maven-shade-plugin. With that, I saw that it, of course, flattens META-INF/services and puts all service files into one shared META-INF/services folder, so that only one file per service can be there. And it indeed by default puts the org.hibernate.bytecode.spi.BytecodeProvider from my application with a no-op implementation, which I defined there. But even if it would put the ByteBuddy implementation from the &lt;em&gt;hibernate-core&lt;/em&gt; dependency, I could use the filtering feature of the maven-shade-plugin to exclude it in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;filters&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;filter&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifact&amp;gt;&lt;/span&gt;*:*&lt;span class="nt"&gt;&amp;lt;/artifact&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;excludes&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;exclude&amp;gt;&lt;/span&gt;META-INF/services/org.hibernate.bytecode.spi.BytecodeProvider
      &lt;span class="nt"&gt;&amp;lt;/exclude&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/excludes&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;/filter&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/filters&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then also need to include the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/src/main/resources/META-INF/services/org.hibernate.bytecode.spi.BytecodeProvider" rel="noopener noreferrer"&gt;no-op byte implementation&lt;/a&gt; in the  &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/src/main/resource-config.json" rel="noopener noreferrer"&gt;resource-config.json&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This will then default to no-op implementation if ServiceLoader doesn’t find any implementation.  Now, to build the GraalVM Native Image from the uber-jar, I had to use the &lt;em&gt;native-maven-plugin&lt;/em&gt; Maven plugin &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt; because it supports classpath definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.graalvm.buildtools&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;native-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
   ....
   &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;classpath&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;param&amp;gt;&lt;/span&gt;         
             ${project.build.directory}/${project.artifactId}-${project.version}.jar
      &lt;span class="nt"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;/classpath&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
 ....
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how the native-maven-plugin Maven plugin configuration looks in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt; with all changes that I described above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.graalvm.buildtools&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;native-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.11.4&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;mainClass&amp;gt;&lt;/span&gt;com.formkiq.lambda.runtime.graalvm.LambdaRuntime&lt;span class="nt"&gt;&amp;lt;/mainClass&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;imageName&amp;gt;&lt;/span&gt;aws-lambda-java-25-with-hibernate-and-aurora-dsql-as-graalvm-native-image&lt;span class="nt"&gt;&amp;lt;/imageName&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;buildArgs&amp;gt;&lt;/span&gt;
             &lt;span class="nt"&gt;&amp;lt;buildArg&amp;gt;&lt;/span&gt; --no-fallback &lt;span class="nt"&gt;&amp;lt;/buildArg&amp;gt;&lt;/span&gt;
             &lt;span class="nt"&gt;&amp;lt;buildArg&amp;gt;&lt;/span&gt; --enable-http &lt;span class="nt"&gt;&amp;lt;/buildArg&amp;gt;&lt;/span&gt;
             &lt;span class="nt"&gt;&amp;lt;buildArg&amp;gt;&lt;/span&gt; -H:ReflectionConfigurationFiles=src/main/reflect-config.json &lt;span class="nt"&gt;&amp;lt;/buildArg&amp;gt;&lt;/span&gt;
             &lt;span class="nt"&gt;&amp;lt;buildArg&amp;gt;&lt;/span&gt; -H:ResourceConfigurationFiles=src/main/resource-config.json &lt;span class="nt"&gt;&amp;lt;/buildArg&amp;gt;&lt;/span&gt;
             &lt;span class="nt"&gt;&amp;lt;buildArg&amp;gt;&lt;/span&gt; -H:Preserve=package=org.hibernate.event.spi &lt;span class="nt"&gt;&amp;lt;/buildArg&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/buildArgs&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;classpath&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;param&amp;gt;&lt;/span&gt;                   
    ${project.build.directory}/${project.artifactId}-${project.version}.jar
          &lt;span class="nt"&gt;&amp;lt;/param&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/classpath&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
  ....      
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with Hibernate and Hikari connection pool using GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/template.yaml" rel="noopener noreferrer"&gt;GetProductById&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql-as-graalvm-native-image/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt;. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEHDQGVNI25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;We designed the experiment exactly as described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with provided:al2023.v124 version, and the deployed artifact size of this application was 33.377 KB.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Custom Runtime with GraalVM Native Image&lt;/td&gt;
&lt;td&gt;945&lt;/td&gt;
&lt;td&gt;995&lt;/td&gt;
&lt;td&gt;1115&lt;/td&gt;
&lt;td&gt;1192&lt;/td&gt;
&lt;td&gt;1214&lt;/td&gt;
&lt;td&gt;1215&lt;/td&gt;
&lt;td&gt;4.69&lt;/td&gt;
&lt;td&gt;5.04&lt;/td&gt;
&lt;td&gt;5.42&lt;/td&gt;
&lt;td&gt;9.38&lt;/td&gt;
&lt;td&gt;38.76&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this part of the series, we first introduced GraalVM Native Image. Then we explained step-by-step how to convert the sample applications to those where the native image can be built. When we deployed the native image on AWS Lambda using the Lambda Custom Runtime. Finally, we measured the performance of the Lambda function. We observed that the cold and warm start times vary compared to using the Lambda SnapStart. Sometimes GraalVM, sometimes SnapStart provides better performance. Sometimes it depends on which priming techniques we use, see the measurements table in &lt;a href="https://dev.tourl"&gt;part 5&lt;/a&gt;. On the other hand, creating a Native Image is not for free, as we need to scale CI/CD pipeline to build a native image. Building it requires many GBs of memory, and the process takes many minutes depending on the hardware. Creating a full set of the Native Image metadata, even using GraalVM Tracing Agent, means introducing additional complexity. Lambda SnapStart, on the other hand, is fully managed. Especially if we use Hibernate in our application without using any frameworks (Spring Boot, Quarkus) on top, it makes things very complicated. That's why I'd advocate against using Hibernate alone in such scenarios. &lt;/p&gt;

&lt;p&gt;We'll compare both approaches in one of the next articles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36298"&gt;series&lt;/a&gt; where I use a NoSQL serverless &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; database instead of Aurora DSQL to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>java</category>
      <category>graalvm</category>
    </item>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and DynamoDB - Part 6 Using GraalVM Native Image</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Mon, 20 Apr 2026 16:31:10 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-6-using-1ji</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-6-using-1ji</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg"&gt;part 1&lt;/a&gt;, we introduced our sample application. In parts 2-5, we measured Lambda function performance using different approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;without activation of Lambda SnapStart&lt;/li&gt;
&lt;li&gt;with activation of Lambda SnapStart, but without using any priming techniques&lt;/li&gt;
&lt;li&gt;with activation of Lambda SnapStart and using different priming techniques&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We observed that by activating the SnapSart and applying different priming techniques, we could significantly further reduce the Lambda cold start times. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. Moreover, we could significantly reduce the maximal value for the Lambda warm start times.&lt;/p&gt;

&lt;p&gt;In this article, we'll introduce another approach to improve the performance of the Lambda function - GraalVM Native Image.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;This article assumes prior knowledge of GraalVM and its native image capabilities. For a concise overview of them and how to get both installed, please refer to the following articles: &lt;a href="https://www.graalvm.org/latest/introduction/" rel="noopener noreferrer"&gt;Introduction to GraalVM&lt;/a&gt;, &lt;a href="https://www.graalvm.org/22.2/docs/introduction/" rel="noopener noreferrer"&gt;GraalVM Architecture&lt;/a&gt;, and &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/" rel="noopener noreferrer"&gt;GraalVM Native Image&lt;/a&gt; or read my article &lt;a href="https://dev.to/aws-builders/lambda-function-with-graalvm-native-image-part-1-introduction-to-graalvm-and-its-native-image-capabilities-5d17"&gt;Introduction to GraalVM and its native image capabilities&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install GraalVM and native image, please follow the instructions in the article &lt;a href="https://www.graalvm.org/latest/getting-started/#installing" rel="noopener noreferrer"&gt;Installing GraalVM&lt;/a&gt;. In my example, I used the 25.0.2-graal version, but you can use the newest one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application using GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-dynamodb" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg"&gt;part 1&lt;/a&gt;, but adjust it. The goal is to be able to build GraalVM Native Image and deploy it on AWS Lambda as a Custom Runtime.&lt;/p&gt;

&lt;p&gt;Here is the final version of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image" rel="noopener noreferrer"&gt;aws-lambda-java-25-dynamodb-as-graalvm-native-image&lt;/a&gt; application.&lt;/p&gt;

&lt;p&gt;Let's go step-by-step through the changes compared to the initial application from part 1. The business logic (Entity, ProductDao, and the Lambda handlers) remains completely the same.  All changes are made in the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt;, &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/template.yaml" rel="noopener noreferrer"&gt;AWS SAM template&lt;/a&gt;, and by providing additional GraalVM configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making sample application GraalVM Native Image capable
&lt;/h2&gt;

&lt;p&gt;For our sample application to run as a GraalVM Native Image, we need to declare all classes whose objects will be instantiated by reflection. These classes needed to be known by the AOT compiler at compile time. This happens in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/src/main/reflect-config.json" rel="noopener noreferrer"&gt;reflect-config.json&lt;/a&gt;.  As we can see, we need to declare there the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; all our Lambda functions like GetProductByIdHandler] and CreateProductHandler&lt;/li&gt;
&lt;li&gt; entities like Product that Jackson converts from JSON payload and back&lt;/li&gt;
&lt;li&gt; APIGatewayProxyRequestEvent and all its inner classes because we declared this event type as a request event in our Lambda functions, like GetProductByIdHandler and CreateProductHandler&lt;/li&gt;
&lt;li&gt;org.joda.time.DateTime, which will be used to convert a timestamp from a string and back. Such a timestamp is a part of the API Gateway proxy request and response events. In my opinion, it's time to switch from  &lt;a href="https://www.joda.org/joda-time/" rel="noopener noreferrer"&gt;Joda-Time&lt;/a&gt; to the Java Date/Time API for this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are multiple ways, how, and where to define GraalVM Native configuration, like reflection configuration. For this, I refer you to the article &lt;a href="https://www.graalvm.org/22.2/reference-manual/native-image/guides/build-with-reflection/" rel="noopener noreferrer"&gt;Build a Native Executable with Reflection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To avoid errors with Loggers during the initialization described in the article &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/optimizations-and-performance/ClassInitialization/" rel="noopener noreferrer"&gt;Class Initialization in Native Image&lt;/a&gt;, we need to add GraalVM Native Image build arguments in the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/src/main/resources/META-INF/native-image/org.slf4j/slf4j-simple/native-image.properties" rel="noopener noreferrer"&gt;native-image.properties&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;--allow-incomplete-classpath &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--initialize-at-build-time=org.slf4j.simple.SimpleLogger,&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;      &lt;span class="s"&gt;org.slf4j.LoggerFactory&lt;/span&gt;
    &lt;span class="err"&gt;--&lt;/span&gt; &lt;span class="py"&gt;--trace-class-initialization&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;org.slf4j.simple.SimpleLogger,&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;      &lt;span class="s"&gt;org.slf4j.LoggerFactory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The native-image.properties should be placed in the META-INF/native-image/${MavenGroupIid}/${MavenArtifactId}&lt;/p&gt;

&lt;p&gt;As we use slf4j-simple Logger in our application, we need to place native-image.properties in the path META-INF/native-image/org.slf4j/slf4j-simple.&lt;br&gt;
If you use another Logger implementation (e.g., log4j or logback), you need to adjust this file and place it accordingly.&lt;/p&gt;

&lt;p&gt;To save the manual work of defining all this metadata, you can use &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/" rel="noopener noreferrer"&gt;GraalVM Tracing Agent&lt;/a&gt; to generate it for you.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lambda Custom Runtime
&lt;/h2&gt;

&lt;p&gt;There is no managed GraalVM (Native Image) on AWS Lambda. To deploy the native image on AWS Lambda, we need a &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html" rel="noopener noreferrer"&gt;custom runtime&lt;/a&gt;. For this, we need to package everything into a file with a &lt;strong&gt;.zip&lt;/strong&gt; extension, which includes the file with the name &lt;strong&gt;bootstrap&lt;/strong&gt;. This file can either be the GraalVM Native Image or contain instructions on how to invoke the GraalVM Native Image placed in another file. We'll use the latter way; let's explore it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;We'll build GraalVM Native Image automatically in the package phase defined in the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt;. The relevant part is defined in the following plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.graalvm.nativeimage&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;native-image-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;21.2.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;native-image&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;package&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;skip&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/skip&amp;gt;&lt;/span&gt;              
       &lt;span class="nt"&gt;&amp;lt;mainClass&amp;gt;&lt;/span&gt;
           com.formkiq.lambda.runtime.graalvm.LambdaRuntime
       &lt;span class="nt"&gt;&amp;lt;/mainClass&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;imageName&amp;gt;&lt;/span&gt;
           aws-lambda-java-25-with-dynamodb-as-graalvm-native-image
       &lt;span class="nt"&gt;&amp;lt;/imageName&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;buildArgs&amp;gt;&lt;/span&gt;
         --no-fallback
         --enable-http
         -H:ReflectionConfigurationFiles=../src/main/reflect-config.json
      &lt;span class="nt"&gt;&amp;lt;/buildArgs&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;strong&gt;native-image-maven-plugin&lt;/strong&gt; from &lt;em&gt;org.graalvm.nativeimage&lt;/em&gt; tools and execute native-image in the package phase. You can also use the alternative &lt;a href="https://graalvm.github.io/native-build-tools/latest/maven-plugin.html" rel="noopener noreferrer"&gt;native-maven-plugin&lt;/a&gt; plugin, whose configuration is very similar. This plugin requires the definition of the main class, which a Lambda function doesn't have. That's why we use &lt;a href="https://github.com/formkiq/lambda-runtime-graalvm" rel="noopener noreferrer"&gt;Lambda Runtime GraalVM&lt;/a&gt; and define its main class &lt;em&gt;com.formkiq.lambda.runtime.graalvm.LambdaRuntime&lt;/em&gt;. Lambda Runtime GraalVM is a Java library that makes it easy to convert AWS Lambda written in the Java programming language to the GraalVM. We defined it previously in pom.xml as a dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.formkiq&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;lambda-runtime-graalvm&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.6.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then give the native image name &lt;strong&gt;aws-lambda-java-25-with-dynamodb-as-graalvm-native-image&lt;/strong&gt; (the default one will also be an artifact name). After it, we include some GraalVM Native Image arguments and previously defined &lt;strong&gt;reflect-config&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;buildArgs&amp;gt;&lt;/span&gt;
    --no-fallback
    --enable-http
   -H:ReflectionConfigurationFiles=../src/main/reflect-config.json
&lt;span class="nt"&gt;&amp;lt;/buildArgs&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To zip the built GraalVM Native Image as function.zip required by Lambda Custom Runtime, we use the maven-assembly plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-assembly-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;native-zip&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;package&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;single&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;inherited&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/inherited&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;finalName&amp;gt;&lt;/span&gt;function&lt;span class="nt"&gt;&amp;lt;/finalName&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;appendAssemblyId&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/appendAssemblyId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;descriptors&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;descriptor&amp;gt;&lt;/span&gt;src/assembly/native.xml&lt;span class="nt"&gt;&amp;lt;/descriptor&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/descriptors&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;finalName&lt;/em&gt; is the name of the zip file, in our case, &lt;strong&gt;function&lt;/strong&gt;. We also include &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/src/assembly/native.xml" rel="noopener noreferrer"&gt;native.xml&lt;/a&gt; descriptor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;assembly&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;native-zip&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;formats&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;format&amp;gt;&lt;/span&gt;zip&lt;span class="nt"&gt;&amp;lt;/format&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/formats&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;baseDirectory/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;fileSets&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;fileSet&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;src/shell/native&lt;span class="nt"&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;outputDirectory&amp;gt;&lt;/span&gt;/&lt;span class="nt"&gt;&amp;lt;/outputDirectory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;useDefaultExcludes&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/useDefaultExcludes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;fileMode&amp;gt;&lt;/span&gt;0775&lt;span class="nt"&gt;&amp;lt;/fileMode&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;bootstrap&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/fileSet&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;fileSet&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;directory&amp;gt;&lt;/span&gt;target&lt;span class="nt"&gt;&amp;lt;/directory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;outputDirectory&amp;gt;&lt;/span&gt;/&lt;span class="nt"&gt;&amp;lt;/outputDirectory&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;useDefaultExcludes&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/useDefaultExcludes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;fileMode&amp;gt;&lt;/span&gt;0775&lt;span class="nt"&gt;&amp;lt;/fileMode&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;includes&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;include&amp;gt;&lt;/span&gt;aws-lambda-java-25-with-dynamodb-as-graalvm-native-image&lt;span class="nt"&gt;&amp;lt;/include&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/includes&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/fileSet&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/fileSets&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/assembly&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This descriptor defines what files from which directories with what permissions will be added to the zip as an assembly format. &lt;em&gt;fileMode&lt;/em&gt; equal to &lt;strong&gt;0775&lt;/strong&gt; means it has permission to be executable on the Linux operating system.  We include previously built GraalVM Native Image with the name &lt;strong&gt;aws-lambda-java-25-with-dynamodb-as-graalvm-native-image&lt;/strong&gt; there. We also include the already defined &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/src/shell/native/bootstrap" rel="noopener noreferrer"&gt;bootstrap&lt;/a&gt; file, which basically invokes the GraalVM Native Image :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LAMBDA_TASK_ROOT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

./aws-lambda-java-25-with-dynamodb-as-graalvm-native-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end, we have to build GraalVM Native Image packaged as a zip file, which can be built as a Lambda Custom Runtime with &lt;code&gt;mvn clean package&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying GraalVM Native Image as a Lambda Custom Runtime
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/template.yaml" rel="noopener noreferrer"&gt;AWS SAM template&lt;/a&gt;, we set the Lambda runtime as &lt;strong&gt;provided.al2023&lt;/strong&gt;, which is the newest version of the &lt;a href="https://docs.aws.amazon.com/linux/al2023/ug/lambda.html" rel="noopener noreferrer"&gt;custom runtime&lt;/a&gt;, and provide the path to the previously built GraalVM Native Image as target/function.zip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;target/function.zip&lt;/span&gt;
    &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provided.al2023&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are ready to deploy our application with &lt;code&gt;sam deploy -g&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of our application using GraalVM Native Image
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/template.yaml" rel="noopener noreferrer"&gt;GetProductById&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb-as-graalvm-native-image/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt;. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEVDDBGVNI25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;We designed the experiment exactly as described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with provided:al2023.v124 version, and the deployed artifact size of this application was 25.186 KB.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Custom Runtime with GraalVM Native Image&lt;/td&gt;
&lt;td&gt;559&lt;/td&gt;
&lt;td&gt;568&lt;/td&gt;
&lt;td&gt;593&lt;/td&gt;
&lt;td&gt;692&lt;/td&gt;
&lt;td&gt;739&lt;/td&gt;
&lt;td&gt;739&lt;/td&gt;
&lt;td&gt;3.84&lt;/td&gt;
&lt;td&gt;4.23&lt;/td&gt;
&lt;td&gt;4.88&lt;/td&gt;
&lt;td&gt;10.00&lt;/td&gt;
&lt;td&gt;55.92&lt;/td&gt;
&lt;td&gt;124&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this part of the series, we first introduced GraalVM Native Image. Then we explained step-by-step how to convert the sample application to one where the native image can be built. When we deployed the native image on AWS Lambda using the Lambda Custom Runtime. Finally, we measured the performance of the Lambda function. We observed that the cold and warm start times were lower compared to using the Lambda SnapStart, even with priming techniques, see the measurements table in &lt;a href="https://dev.tourl"&gt;part 5&lt;/a&gt;. On the other hand, creating a Native Image is not for free, as we need to scale CI/CD pipeline to build a native image. Building it requires many GBs of memory, and the process takes many minutes depending on the hardware. Creating a full set of the Native Image metadata, even using GraalVM Tracing Agent, means introducing additional complexity. Lambda SnapStart, on the other hand, is fully managed.&lt;/p&gt;

&lt;p&gt;We'll compare both approaches in one of the next articles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36919"&gt;series&lt;/a&gt; where I use a relational serverless &lt;a href="https://aws.amazon.com/rds/aurora/dsql/" rel="noopener noreferrer"&gt;Amazon Aurora DSQL&lt;/a&gt; database and additionally the &lt;a href="https://hibernate.org/" rel="noopener noreferrer"&gt;Hibernate ORM framework&lt;/a&gt; instead of DynamoDB to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>graalvm</category>
    </item>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and Aurora DSQL - Part 5 SnapStart and full priming</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Wed, 15 Apr 2026 14:47:26 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-5-3dlj</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-5-3dlj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt;, we introduced our sample application. In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;, we measured the performance (cold and warm start times) of the Lambda function without any optimizations. We observed quite a large cold start time, especially if we use the Hibernate ORM framework. Using this framework also significantly increases the artifact size.  &lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2"&gt;part 3&lt;/a&gt;, we introduced AWS Lambda SnapStart as one of the approaches to reduce the cold start times of the Lambda function. We observed that by enabling the SnapStart on the Lambda function, the cold start time goes down significantly for both sample applications. &lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-4-1kg5"&gt;part 4&lt;/a&gt;, we introduced how to apply Lambda SnapStart priming technique, such as Aurora DSQL request priming. The goal was to even further improve the performance of our Lambda functions. We saw that by doing this kind of priming and writing some additional code, we could additionally reduce the Lambda cold start times compared to simply activating the SnapStart. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. Moreover, we could also reduce the maximal value for the Lambda warm start times by preloading classes (as Java lazily loads classes when they are required for the first time) and doing some preinitialization work (by invoking the method to retrieve the product from the Aurora DSQL products table by its ID). Previously, all this happened once during the first warm execution of the Lambda function.&lt;/p&gt;

&lt;p&gt;In this article, we'll introduce another Lambda SnapStart priming technique. I call it API Gateway Request Event priming (or full priming). We'll then measure the Lambda performance by applying it and comparing the results with other already introduced approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application with JDBC and Hikari connection pool and the enabled AWS Lambda SnapStart using full priming
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt; and do exactly the same performance measurement as we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, please make sure that we have enabled Lambda SnapStart in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;template.yaml&lt;/a&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;SnapStart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApplyOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishedVersions&lt;/span&gt; 
    &lt;span class="s"&gt;....&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:+TieredCompilation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:TieredStopAtLevel=1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about the concepts behind the Lambda SnapStart in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt; and about SnapStart runtime hooks (which we'll use again) in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2"&gt;part 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I will introduce you to the API Gateway Request Event priming (or full priming for short). We implemented it in the extra &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithFullPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithFullPrimingHandler&lt;/a&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProductByIdWithFullPrimingHandler&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; 
                 &lt;span class="nc"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;GetProductByIdWithFullPrimingHandler&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getGlobalContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeCheckpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="nc"&gt;LambdaEventSerializers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;serializerFor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ClassLoader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSystemClassLoader&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;                 
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getAPIGatewayProxyRequestEventAsJson&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockLambdaContext&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;


&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getAPIGatewayProxyRequestEventAsJson&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHttpMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPathParameters&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;      
 &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterRestore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;   
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPathParameters&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optionalProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withStatusCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OK&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;                                                 
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;optionalProduct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I refer to &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-4-1kg5"&gt;part 4&lt;/a&gt; for the explanation about how the Lambda SnapStart runtime hooks work. Please read my article &lt;a href="https://dev.to/aws-heroes/aws-snapstart-part-27-using-insights-from-aws-lambda-profiler-extension-for-java-to-reduce-lambda-2a1i"&gt;Using insights from AWS Lambda Profiler Extension for Java to reduce Lambda cold starts&lt;/a&gt;, on how I came up with this idea. I also described in this article in detail why it is supposed to speed things up. Shortly speaking, we primed another expensive &lt;em&gt;LambdaEventSerializers.serializerFor&lt;/em&gt; invocation. It consists of class loading and expensive initialization logic, which I identified. By invoking &lt;em&gt;handleRequest&lt;/em&gt;, we fully prime this method invocation, which consists mainly of Aurora DSQL request priming introduced in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-4-1kg5"&gt;part 4&lt;/a&gt;. At the end, we also prime the &lt;em&gt;APIGatewayProxyResponseEvent&lt;/em&gt; object construction. &lt;/p&gt;

&lt;p&gt;In our example, we primed the APIGatewayProxyRequestEvent "get product by id equal to zero" request. This is enough to instantiate and initialize all we need, even if we'd like to invoke the "create product" request. This priming implementation is also a read request without any side effects. But if you'd like, for example, to prime a "create product" request, you can do it as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getAPIGatewayProxyRequestEventAsJson&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHttpMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{ 'id': 0, 'name': 'Print 10x13', 'price': 15 }"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;      
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use some artificial product ID like 0 or a negative one that isn't used in production. If you use API Gateway HTTP API instead of REST API (like in our example), you can use APIGatewayV2HTTPEvent instead of APIGatewayProxyRequestEvent to prime such a request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with JDBC and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithDSQLAndFullPriming&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithFullPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithFullPrimingHandler&lt;/a&gt; shown above. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEDQ25" https://{$API_GATEWAY_URL}/prod/productsWithFullPriming/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;We designed the experiment exactly as described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I will present the Lambda performance measurements with SnapStart being activated for all approx. 100 cold start times (labelled as &lt;em&gt;all&lt;/em&gt; in the table), but also for the last approx. 70 (labelled as &lt;em&gt;last 70&lt;/em&gt; in the table). With that, the effect of the snapshot tiered cache, which we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2"&gt;part 3&lt;/a&gt;, becomes visible to you.&lt;/p&gt;

&lt;p&gt;To show the impact of the SnapStart full priming, we'll also present the Lambda performance measurements from all previous parts.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 17.150 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;2336&lt;/td&gt;
&lt;td&gt;2453&lt;/td&gt;
&lt;td&gt;2827&lt;/td&gt;
&lt;td&gt;3026&lt;/td&gt;
&lt;td&gt;3131&lt;/td&gt;
&lt;td&gt;3132&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.29&lt;/td&gt;
&lt;td&gt;5.73&lt;/td&gt;
&lt;td&gt;8.88&lt;/td&gt;
&lt;td&gt;195.38&lt;/td&gt;
&lt;td&gt;531&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;970&lt;/td&gt;
&lt;td&gt;1058&lt;/td&gt;
&lt;td&gt;1705&lt;/td&gt;
&lt;td&gt;1726&lt;/td&gt;
&lt;td&gt;1734&lt;/td&gt;
&lt;td&gt;1735&lt;/td&gt;
&lt;td&gt;4.92&lt;/td&gt;
&lt;td&gt;5.33&lt;/td&gt;
&lt;td&gt;5.86&lt;/td&gt;
&lt;td&gt;9.84&lt;/td&gt;
&lt;td&gt;198.52&lt;/td&gt;
&lt;td&gt;1134&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;901&lt;/td&gt;
&lt;td&gt;960&lt;/td&gt;
&lt;td&gt;1061&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.29&lt;/td&gt;
&lt;td&gt;5.77&lt;/td&gt;
&lt;td&gt;9.54&lt;/td&gt;
&lt;td&gt;196.94&lt;/td&gt;
&lt;td&gt;719&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DSQL database request priming applied, all&lt;/td&gt;
&lt;td&gt;879&lt;/td&gt;
&lt;td&gt;980&lt;/td&gt;
&lt;td&gt;1499&lt;/td&gt;
&lt;td&gt;1515&lt;/td&gt;
&lt;td&gt;1518&lt;/td&gt;
&lt;td&gt;1518&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.25&lt;/td&gt;
&lt;td&gt;5.77&lt;/td&gt;
&lt;td&gt;9.54&lt;/td&gt;
&lt;td&gt;163.96&lt;/td&gt;
&lt;td&gt;914&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DSQL database request priming applied, last 70&lt;/td&gt;
&lt;td&gt;803&lt;/td&gt;
&lt;td&gt;912&lt;/td&gt;
&lt;td&gt;996&lt;/td&gt;
&lt;td&gt;1056&lt;/td&gt;
&lt;td&gt;1056&lt;/td&gt;
&lt;td&gt;1056&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.25&lt;/td&gt;
&lt;td&gt;5.77&lt;/td&gt;
&lt;td&gt;9.54&lt;/td&gt;
&lt;td&gt;152.61&lt;/td&gt;
&lt;td&gt;597&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and full priming applied, all&lt;/td&gt;
&lt;td&gt;543&lt;/td&gt;
&lt;td&gt;625&lt;/td&gt;
&lt;td&gt;1306&lt;/td&gt;
&lt;td&gt;1411&lt;/td&gt;
&lt;td&gt;1433&lt;/td&gt;
&lt;td&gt;1434&lt;/td&gt;
&lt;td&gt;4.77&lt;/td&gt;
&lt;td&gt;5.20&lt;/td&gt;
&lt;td&gt;5.68&lt;/td&gt;
&lt;td&gt;9.16&lt;/td&gt;
&lt;td&gt;159.36&lt;/td&gt;
&lt;td&gt;864&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and full priming applied, last 70&lt;/td&gt;
&lt;td&gt;526&lt;/td&gt;
&lt;td&gt;578&lt;/td&gt;
&lt;td&gt;885&lt;/td&gt;
&lt;td&gt;945&lt;/td&gt;
&lt;td&gt;945&lt;/td&gt;
&lt;td&gt;945&lt;/td&gt;
&lt;td&gt;4.77&lt;/td&gt;
&lt;td&gt;5.16&lt;/td&gt;
&lt;td&gt;5.64&lt;/td&gt;
&lt;td&gt;9.24&lt;/td&gt;
&lt;td&gt;146.93&lt;/td&gt;
&lt;td&gt;560&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Sample application with Hibernate and Hikari connection pool and the enabled AWS Lambda SnapStart using full priming
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-hibernate-aurora-dsql/" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt; and do exactly the same performance measurement as we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We implemented the full priming in the extra &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithFullPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithFullPrimingHandler&lt;/a&gt; class. &lt;/p&gt;

&lt;p&gt;The explanation of what we would like to achieve and how is exactly the same as in the first sample application above. And the code itself looks the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with Hibernate and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithHibernateAndDSQLAndFullPriming&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithFullPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithFullPrimingHandler&lt;/a&gt; shown above. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEHADQ25" https://{$API_GATEWAY_URL}/prod/productsWithFullPriming/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Please read &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt; for the description of how we designed the experiment.&lt;/p&gt;

&lt;p&gt;To show the impact of the SnapStart full priming, we'll also present the Lambda performance measurements from all previous parts.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 42.333 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;6243&lt;/td&gt;
&lt;td&gt;6625&lt;/td&gt;
&lt;td&gt;7056&lt;/td&gt;
&lt;td&gt;8480&lt;/td&gt;
&lt;td&gt;8651&lt;/td&gt;
&lt;td&gt;8658&lt;/td&gt;
&lt;td&gt;5.46&lt;/td&gt;
&lt;td&gt;5.96&lt;/td&gt;
&lt;td&gt;6.50&lt;/td&gt;
&lt;td&gt;9.77&lt;/td&gt;
&lt;td&gt;200.10&lt;/td&gt;
&lt;td&gt;707&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;1277&lt;/td&gt;
&lt;td&gt;1360&lt;/td&gt;
&lt;td&gt;3050&lt;/td&gt;
&lt;td&gt;3103&lt;/td&gt;
&lt;td&gt;3200&lt;/td&gt;
&lt;td&gt;3201&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;td&gt;6.01&lt;/td&gt;
&lt;td&gt;6.45&lt;/td&gt;
&lt;td&gt;10.16&lt;/td&gt;
&lt;td&gt;196.94&lt;/td&gt;
&lt;td&gt;2349&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;1258&lt;/td&gt;
&lt;td&gt;1320&lt;/td&gt;
&lt;td&gt;1437&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;5.42&lt;/td&gt;
&lt;td&gt;5.91&lt;/td&gt;
&lt;td&gt;6.40&lt;/td&gt;
&lt;td&gt;10.08&lt;/td&gt;
&lt;td&gt;195.94&lt;/td&gt;
&lt;td&gt;1093&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DSQL database request priming applied, all&lt;/td&gt;
&lt;td&gt;1030&lt;/td&gt;
&lt;td&gt;1185&lt;/td&gt;
&lt;td&gt;2310&lt;/td&gt;
&lt;td&gt;2341&lt;/td&gt;
&lt;td&gt;2345&lt;/td&gt;
&lt;td&gt;2347&lt;/td&gt;
&lt;td&gt;5.33&lt;/td&gt;
&lt;td&gt;5.91&lt;/td&gt;
&lt;td&gt;6.50&lt;/td&gt;
&lt;td&gt;11.64&lt;/td&gt;
&lt;td&gt;201.70&lt;/td&gt;
&lt;td&gt;1607&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DSQL database request priming applied, last 70&lt;/td&gt;
&lt;td&gt;970&lt;/td&gt;
&lt;td&gt;1076&lt;/td&gt;
&lt;td&gt;1226&lt;/td&gt;
&lt;td&gt;1511&lt;/td&gt;
&lt;td&gt;1511&lt;/td&gt;
&lt;td&gt;1511&lt;/td&gt;
&lt;td&gt;5.37&lt;/td&gt;
&lt;td&gt;5.96&lt;/td&gt;
&lt;td&gt;6.61&lt;/td&gt;
&lt;td&gt;12.01&lt;/td&gt;
&lt;td&gt;203.32&lt;/td&gt;
&lt;td&gt;670&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and full priming applied, all&lt;/td&gt;
&lt;td&gt;811&lt;/td&gt;
&lt;td&gt;933&lt;/td&gt;
&lt;td&gt;2101&lt;/td&gt;
&lt;td&gt;2148&lt;/td&gt;
&lt;td&gt;2154&lt;/td&gt;
&lt;td&gt;2155&lt;/td&gt;
&lt;td&gt;5.42&lt;/td&gt;
&lt;td&gt;5.91&lt;/td&gt;
&lt;td&gt;6.45&lt;/td&gt;
&lt;td&gt;9.84&lt;/td&gt;
&lt;td&gt;196.94&lt;/td&gt;
&lt;td&gt;1420&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and full priming applied, last 70&lt;/td&gt;
&lt;td&gt;748&lt;/td&gt;
&lt;td&gt;831&lt;/td&gt;
&lt;td&gt;918&lt;/td&gt;
&lt;td&gt;1033&lt;/td&gt;
&lt;td&gt;1033&lt;/td&gt;
&lt;td&gt;1033&lt;/td&gt;
&lt;td&gt;5.42&lt;/td&gt;
&lt;td&gt;5.91&lt;/td&gt;
&lt;td&gt;6.40&lt;/td&gt;
&lt;td&gt;9.84&lt;/td&gt;
&lt;td&gt;195.38&lt;/td&gt;
&lt;td&gt;546&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this part of the series, we introduced how to apply another Lambda SnapStart priming technique. I call it API Gateway Request Event priming (or full priming). The goal was to even further improve the performance of our Lambda functions. We saw that by doing this kind of priming and writing even more additional (but simple) code, we could further reduce the Lambda cold start times compared to simply activating the SnapStart and doing Aurora DSQL request priming. It's once again especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. Moreover, we could again reduce the maximal value for the Lambda warm start times by preloading classes and doing some preinitialization work. &lt;/p&gt;

&lt;p&gt;It's up to you to decide whether this additional complexity is worth the Lambda function performance improvement. You could also be happy with its performance using the Lambda SnapStart with the Aurora DSQL request priming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36298"&gt;series&lt;/a&gt; where I use a NoSQL serverless &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; database instead of Aurora DSQL to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>awslambda</category>
    </item>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and DynamoDB - Part 5 SnapStart and full priming</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Mon, 13 Apr 2026 15:50:42 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-5-5gpe</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-5-5gpe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg"&gt;part 1&lt;/a&gt;, we introduced our sample application. In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;, we measured the performance (cold and warm start times) of the Lambda function without any optimizations. What we observed was quite a large cold start time. We introduced AWS Lambda SnapStart in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-3-2h31"&gt;part 3&lt;/a&gt; as one of the approaches to reduce the cold start times of the Lambda function. We saw that by enabling the SnapStart on the Lambda function, the cold start time goes down. &lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-4-45i8"&gt;part 4&lt;/a&gt;, we introduced how to apply Lambda SnapStart priming techniques and started with DynamoDB request priming. We saw that by doing this kind of priming and writing some additional code, we could significantly further reduce the Lambda cold start times compared to simply activating the SnapStart. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. Moreover, we could significantly reduce the maximal value for the Lambda warm start times by preloading classes (as Java lazily loads classes when they are required for the first time) and doing some preinitialization work (by invoking the method to retrieve the product from the DynamoDB table by its ID). Previously, all this happened once during the first warm execution of the Lambda function.&lt;/p&gt;

&lt;p&gt;In this article, we'll introduce another Lambda SnapStart priming technique. I call it API Gateway Request Event priming (or full priming). We'll then measure the Lambda performance by applying it and comparing the results with other already introduced approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application with the enabled AWS Lambda SnapStart using full  priming
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-dynamodb" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg"&gt;part 1&lt;/a&gt; and do exactly the same performance measurement as we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, please make sure that we have enabled Lambda SnapStart in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/template.yaml" rel="noopener noreferrer"&gt;template.yaml&lt;/a&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;SnapStart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApplyOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishedVersions&lt;/span&gt; 
    &lt;span class="s"&gt;....&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:+TieredCompilation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:TieredStopAtLevel=1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about the concepts behind the Lambda SnapStart in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt; and about SnapStart runtime hooks (which we'll use again) in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-3-2h31"&gt;part 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I will introduce you to the API Gateway Request Event priming (or full priming for short). We implemented it in the extra &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithFullPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithFullPrimingHandler&lt;/a&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProductByIdWithFullPrimingHandler&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; 
                 &lt;span class="nc"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;GetProductByIdWithFullPrimingHandler&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getGlobalContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeCheckpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="nc"&gt;LambdaEventSerializers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;serializerFor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ClassLoader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSystemClassLoader&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;                 
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getAPIGatewayProxyRequestEventAsJson&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockLambdaContext&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;


&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getAPIGatewayProxyRequestEventAsJson&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHttpMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPathParameters&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;      
 &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterRestore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;   
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPathParameters&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optionalProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withStatusCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OK&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;                                                 
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;optionalProduct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I refer to &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-4-45i8"&gt;part 4&lt;/a&gt; for the explanation about how the Lambda SnapStart runtime hooks work. Please read my article &lt;a href="https://dev.to/aws-heroes/aws-snapstart-part-27-using-insights-from-aws-lambda-profiler-extension-for-java-to-reduce-lambda-2a1i"&gt;Using insights from AWS Lambda Profiler Extension for Java to reduce Lambda cold starts&lt;/a&gt;, on how I came up with this idea. I also described in this article in detail why it is supposed to speed things up. Shortly speaking, we primed another expensive &lt;em&gt;LambdaEventSerializers.serializerFor&lt;/em&gt; invocation. It consists of class loading and expensive initialization logic, which I identified. By invoking &lt;em&gt;handleRequest&lt;/em&gt;, we fully prime this method invocation, which consists mainly of DynamoDB request priming introduced in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-4-45i8"&gt;part 4&lt;/a&gt;. At the end, we also prime the &lt;em&gt;APIGatewayProxyResponseEvent&lt;/em&gt; object construction. &lt;/p&gt;

&lt;p&gt;In our example, we primed the APIGatewayProxyRequestEvent "get product by id equal to zero" request. This is enough to instantiate and initialize all we need, even if we'd like to invoke the "create product" request. This priming implementation is also a read request without any side effects. But if you'd like, for example, to prime a "create product" request, you can do it as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getAPIGatewayProxyRequestEventAsJson&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHttpMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{ 'id': 0, 'name': 'Print 10x13', 'price': 15 }"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;      
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use some artificial product ID like 0 or a negative one that isn't used in production. If you use API Gateway HTTP API instead of REST API (like in our example), you can use APIGatewayV2HTTPEvent instead of APIGatewayProxyRequestEvent to prime such a request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of our application with Lambda SnapStart and full priming
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithDynamoDBAndFullPriming&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithFullPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithFullPrimingHandler&lt;/a&gt; shown above. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEVDDB25" https://{$API_GATEWAY_URL}/prod/productsWithFullPriming/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;We designed the experiment exactly as described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I will present the Lambda performance measurements with SnapStart being activated for all approx. 100 cold start times (labelled as &lt;em&gt;all&lt;/em&gt; in the table), but also for the last approx. 70 (labelled as &lt;em&gt;last 70&lt;/em&gt; in the table). With that, the effect of the snapshot tiered cache, which we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-3-2h31"&gt;part 3&lt;/a&gt;, becomes visible to you.&lt;/p&gt;

&lt;p&gt;To show the impact of the SnapStart full priming, we'll also present the Lambda performance measurements from all previous parts.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 13.796 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;3800&lt;/td&gt;
&lt;td&gt;3967&lt;/td&gt;
&lt;td&gt;4183&lt;/td&gt;
&lt;td&gt;4411&lt;/td&gt;
&lt;td&gt;4495&lt;/td&gt;
&lt;td&gt;4499&lt;/td&gt;
&lt;td&gt;5.55&lt;/td&gt;
&lt;td&gt;6.15&lt;/td&gt;
&lt;td&gt;7.00&lt;/td&gt;
&lt;td&gt;12.18&lt;/td&gt;
&lt;td&gt;56.37&lt;/td&gt;
&lt;td&gt;4000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;2294&lt;/td&gt;
&lt;td&gt;2366&lt;/td&gt;
&lt;td&gt;3530&lt;/td&gt;
&lt;td&gt;3547&lt;/td&gt;
&lt;td&gt;3548&lt;/td&gt;
&lt;td&gt;3551&lt;/td&gt;
&lt;td&gt;5.68&lt;/td&gt;
&lt;td&gt;6.30&lt;/td&gt;
&lt;td&gt;7.33&lt;/td&gt;
&lt;td&gt;13.43&lt;/td&gt;
&lt;td&gt;44.74&lt;/td&gt;
&lt;td&gt;2923&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;2247&lt;/td&gt;
&lt;td&gt;2324&lt;/td&gt;
&lt;td&gt;2389&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;5.68&lt;/td&gt;
&lt;td&gt;6.35&lt;/td&gt;
&lt;td&gt;7.39&lt;/td&gt;
&lt;td&gt;13.65&lt;/td&gt;
&lt;td&gt;44.03&lt;/td&gt;
&lt;td&gt;2051&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DynamoDB request priming applied, all&lt;/td&gt;
&lt;td&gt;778&lt;/td&gt;
&lt;td&gt;817&lt;/td&gt;
&lt;td&gt;1544&lt;/td&gt;
&lt;td&gt;1572&lt;/td&gt;
&lt;td&gt;1601&lt;/td&gt;
&lt;td&gt;1602&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;td&gt;6.10&lt;/td&gt;
&lt;td&gt;6.99&lt;/td&gt;
&lt;td&gt;12.01&lt;/td&gt;
&lt;td&gt;34.74&lt;/td&gt;
&lt;td&gt;933&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DynamoDB request priming applied, last 70&lt;/td&gt;
&lt;td&gt;752&lt;/td&gt;
&lt;td&gt;790&lt;/td&gt;
&lt;td&gt;837&lt;/td&gt;
&lt;td&gt;988&lt;/td&gt;
&lt;td&gt;988&lt;/td&gt;
&lt;td&gt;988&lt;/td&gt;
&lt;td&gt;5.46&lt;/td&gt;
&lt;td&gt;6.05&lt;/td&gt;
&lt;td&gt;6.99&lt;/td&gt;
&lt;td&gt;11.92&lt;/td&gt;
&lt;td&gt;42.65&lt;/td&gt;
&lt;td&gt;412&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and full priming applied, all&lt;/td&gt;
&lt;td&gt;600&lt;/td&gt;
&lt;td&gt;660&lt;/td&gt;
&lt;td&gt;172&lt;/td&gt;
&lt;td&gt;1310&lt;/td&gt;
&lt;td&gt;1325&lt;/td&gt;
&lt;td&gt;1325&lt;/td&gt;
&lt;td&gt;5.46&lt;/td&gt;
&lt;td&gt;6.05&lt;/td&gt;
&lt;td&gt;6.93&lt;/td&gt;
&lt;td&gt;11.82&lt;/td&gt;
&lt;td&gt;37.25&lt;/td&gt;
&lt;td&gt;630&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and full priming applied, last 70&lt;/td&gt;
&lt;td&gt;598&lt;/td&gt;
&lt;td&gt;640&lt;/td&gt;
&lt;td&gt;711&lt;/td&gt;
&lt;td&gt;895&lt;/td&gt;
&lt;td&gt;895&lt;/td&gt;
&lt;td&gt;895&lt;/td&gt;
&lt;td&gt;5.42&lt;/td&gt;
&lt;td&gt;6.01&lt;/td&gt;
&lt;td&gt;6.93&lt;/td&gt;
&lt;td&gt;12.01&lt;/td&gt;
&lt;td&gt;34.95&lt;/td&gt;
&lt;td&gt;214&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this part of the series, we introduced how to apply another Lambda SnapStart priming technique. I call it API Gateway Request Event priming (or full priming). The goal was to even further improve the performance of our Lambda functions. We saw that by doing this kind of priming and writing even more additional (but simple) code, we could further reduce the Lambda cold start times compared to simply activating the SnapStart and doing DynamoDB request priming. It's once again especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. Moreover, we could again significantly reduce the maximal value for the Lambda warm start times by preloading classes and doing some preinitialization work. &lt;/p&gt;

&lt;p&gt;It's up to you to decide whether this additional complexity is worth the Lambda function performance improvement. You could also be happy with its performance using the Lambda SnapStart with the DynamoDB request priming.&lt;/p&gt;

&lt;p&gt;In the next part, we'll introduce another approach to reduce the cold start time of the Lambda function - GraalVM Native Image. We'll create the native image of our application and deploy it as a Lambda Custom Runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36919"&gt;series&lt;/a&gt; where I use a relational serverless &lt;a href="https://aws.amazon.com/rds/aurora/dsql/" rel="noopener noreferrer"&gt;Amazon Aurora DSQL&lt;/a&gt; database and additionally the &lt;a href="https://hibernate.org/" rel="noopener noreferrer"&gt;Hibernate ORM framework&lt;/a&gt; instead of DynamoDB to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>awslmabda</category>
    </item>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and Aurora DSQL - Part 4 SnapStart and DSQL request priming</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Thu, 09 Apr 2026 15:14:36 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-4-1kg5</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-4-1kg5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt;, we introduced our sample application. In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;, we measured the performance (cold and warm start times) of the Lambda function without any optimizations. We observed quite a large cold start time, especially if we use the Hibernate ORM framework. Using this framework also significantly increases the artifact size.  &lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2"&gt;part 3&lt;/a&gt;, we introduced AWS Lambda SnapStart as one of the approaches to reduce the cold start times of the Lambda function. We observed that by enabling the SnapStart on the Lambda function, the cold start time goes down significantly for both sample applications. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. The biggest impact of just enabling the SnapStart is on the application using the Hibernate.&lt;/p&gt;

&lt;p&gt;In this part of our article series, we'll introduce how to apply Lambda SnapStart priming techniques. We'll start with the database (in our case, Aurora DSQL) request priming. The goal is to further improve the performance of our Lambda functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application with JDBC and Hikari connection pool and the enabled AWS Lambda SnapStart using Aurora DSQL request priming
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt; and do exactly the same performance measurement as we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, please make sure that we have enabled Lambda SnapStart in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;template.yaml&lt;/a&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;SnapStart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApplyOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishedVersions&lt;/span&gt; 
    &lt;span class="s"&gt;....&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:+TieredCompilation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:TieredStopAtLevel=1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about the concepts behind the Lambda SnapStart in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/" rel="noopener noreferrer"&gt;SnapStart and runtime hooks&lt;/a&gt; offer you new possibilities to create your Lambda functions for low startup latency. With the pre-snapshot hook, we can prepare our Java application as much as possible for the first call. We load and initialize as much as possible that our Lambda function needs before the Lambda SnapStart creates the snapshot. The name for this technique is priming.&lt;/p&gt;

&lt;p&gt;In this article, I will introduce you to the priming of database (in our case, Aurora DSQL) requests, which we implemented in the extra &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithDSQLPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithDSQLPrimingHandler&lt;/a&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProductByIdWithDSQLPrimingHandler&lt;/span&gt;
        &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;GetProductByIdWithDSQLPrimingHandler&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getGlobalContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
 &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeCheckpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProductById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterRestore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;       
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPathParameters&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optionalProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProductById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withStatusCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OK&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;                                    
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;optionalProduct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/snapstart-runtime-hooks-java.html" rel="noopener noreferrer"&gt;Lambda SnapStart CRaC runtime hooks&lt;/a&gt; here. To do this, we need to declare the following dependency in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.crac&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;org-crac&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GetProductByIdWithDSQLPrimingHandler class additionally implements &lt;em&gt;org.crac.Resource&lt;/em&gt; interface. The class registers itself as a CRaC resource in the constructor of this class. The priming itself happens in the method where we search for the product with the ID equal to 0 in the products table of the Aurora DSQL database. &lt;em&gt;beforeCheckpoint&lt;/em&gt; method is a CRaC runtime hook that is invoked before creating the microVM snapshot. We are not even processing the result of the call to &lt;em&gt;productDao.getProductById(0)&lt;/em&gt;. The product with the ID equal to zero might not even exist in the database. But with this invocation, Java lazily loads and instantiates all the classes that it requires for this invocation.  PreparedStatement, ResultSet, and many others are among such classes. We also instantiate everything required to establish the connection to the database via JDBC and process the request and response.&lt;/p&gt;

&lt;p&gt;We can leave the afterRestore method empty. This is because we don't need to perform any action after the SnapStart snapshot has been restored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with JDBC and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithDSQLAndDSQLPriming&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithDSQLPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithDSQLPrimingHandler&lt;/a&gt; shown above. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEDQ25" https://{$API_GATEWAY_URL}/prod/productsWithAuroraPriming/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Please read &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt; for the description of how we designed the experiment.&lt;/p&gt;

&lt;p&gt;I will present the Lambda performance measurements with SnapStart being activated for all approx. 100 cold start times (labelled as &lt;em&gt;all&lt;/em&gt; in the table), but also for the last approx. 70 (labelled as &lt;em&gt;last 70&lt;/em&gt; in the table). With that, the effect of the snapshot tiered cache, which we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2"&gt;part 3&lt;/a&gt;, becomes visible to you.&lt;/p&gt;

&lt;p&gt;To show the impact of the SnapStart with the Aurora DSQL database request priming, we'll also present the Lambda performance measurements from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt; and &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2"&gt;part 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 17.150 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;2336&lt;/td&gt;
&lt;td&gt;2453&lt;/td&gt;
&lt;td&gt;2827&lt;/td&gt;
&lt;td&gt;3026&lt;/td&gt;
&lt;td&gt;3131&lt;/td&gt;
&lt;td&gt;3132&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.29&lt;/td&gt;
&lt;td&gt;5.73&lt;/td&gt;
&lt;td&gt;8.88&lt;/td&gt;
&lt;td&gt;195.38&lt;/td&gt;
&lt;td&gt;531&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;970&lt;/td&gt;
&lt;td&gt;1058&lt;/td&gt;
&lt;td&gt;1705&lt;/td&gt;
&lt;td&gt;1726&lt;/td&gt;
&lt;td&gt;1734&lt;/td&gt;
&lt;td&gt;1735&lt;/td&gt;
&lt;td&gt;4.92&lt;/td&gt;
&lt;td&gt;5.33&lt;/td&gt;
&lt;td&gt;5.86&lt;/td&gt;
&lt;td&gt;9.84&lt;/td&gt;
&lt;td&gt;198.52&lt;/td&gt;
&lt;td&gt;1134&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;901&lt;/td&gt;
&lt;td&gt;960&lt;/td&gt;
&lt;td&gt;1061&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.29&lt;/td&gt;
&lt;td&gt;5.77&lt;/td&gt;
&lt;td&gt;9.54&lt;/td&gt;
&lt;td&gt;196.94&lt;/td&gt;
&lt;td&gt;719&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DSQL database request priming applied, all&lt;/td&gt;
&lt;td&gt;879&lt;/td&gt;
&lt;td&gt;980&lt;/td&gt;
&lt;td&gt;1499&lt;/td&gt;
&lt;td&gt;1515&lt;/td&gt;
&lt;td&gt;1518&lt;/td&gt;
&lt;td&gt;1518&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.25&lt;/td&gt;
&lt;td&gt;5.77&lt;/td&gt;
&lt;td&gt;9.54&lt;/td&gt;
&lt;td&gt;163.96&lt;/td&gt;
&lt;td&gt;914&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DSQL database request priming applied, last 70&lt;/td&gt;
&lt;td&gt;803&lt;/td&gt;
&lt;td&gt;912&lt;/td&gt;
&lt;td&gt;996&lt;/td&gt;
&lt;td&gt;1056&lt;/td&gt;
&lt;td&gt;1056&lt;/td&gt;
&lt;td&gt;1056&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.25&lt;/td&gt;
&lt;td&gt;5.77&lt;/td&gt;
&lt;td&gt;9.54&lt;/td&gt;
&lt;td&gt;152.61&lt;/td&gt;
&lt;td&gt;597&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Sample application with Hibernate and Hikari connection pool and the enabled AWS Lambda SnapStart using Aurora DSQL request priming
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-hibernate-aurora-dsql/" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt; and do exactly the same performance measurement as we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We implemented the priming of the database (in our case, Aurora DSQL) request in the extra &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithDSQLPrimingHandler" rel="noopener noreferrer"&gt;GetProductByIdWithDSQLPrimingHandler&lt;/a&gt; class. &lt;/p&gt;

&lt;p&gt;The explanation of what we would like to achieve and how is exactly the same as in the first sample application above. The main difference is that we use the Hibernate framework on top and also preload and preinitialize its classes and abstractions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with Hibernate and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithHibernateAndDSQLAndDSQLPriming&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithDSQLPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithDSQLPrimingHandler&lt;/a&gt; shown above. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEHADQ25" https://{$API_GATEWAY_URL}/prod/productsWithAuroraPriming/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Please read &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt; for the description of how we designed the experiment.&lt;/p&gt;

&lt;p&gt;To show the impact of the SnapStart with the Aurora DSQL database request priming, we'll also present the Lambda performance measurements from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt; and &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2"&gt;part 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 42.333 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;6243&lt;/td&gt;
&lt;td&gt;6625&lt;/td&gt;
&lt;td&gt;7056&lt;/td&gt;
&lt;td&gt;8480&lt;/td&gt;
&lt;td&gt;8651&lt;/td&gt;
&lt;td&gt;8658&lt;/td&gt;
&lt;td&gt;5.46&lt;/td&gt;
&lt;td&gt;5.96&lt;/td&gt;
&lt;td&gt;6.50&lt;/td&gt;
&lt;td&gt;9.77&lt;/td&gt;
&lt;td&gt;200.10&lt;/td&gt;
&lt;td&gt;707&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;1277&lt;/td&gt;
&lt;td&gt;1360&lt;/td&gt;
&lt;td&gt;3050&lt;/td&gt;
&lt;td&gt;3103&lt;/td&gt;
&lt;td&gt;3200&lt;/td&gt;
&lt;td&gt;3201&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;td&gt;6.01&lt;/td&gt;
&lt;td&gt;6.45&lt;/td&gt;
&lt;td&gt;10.16&lt;/td&gt;
&lt;td&gt;196.94&lt;/td&gt;
&lt;td&gt;2349&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;1258&lt;/td&gt;
&lt;td&gt;1320&lt;/td&gt;
&lt;td&gt;1437&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;5.42&lt;/td&gt;
&lt;td&gt;5.91&lt;/td&gt;
&lt;td&gt;6.40&lt;/td&gt;
&lt;td&gt;10.08&lt;/td&gt;
&lt;td&gt;195.94&lt;/td&gt;
&lt;td&gt;1093&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DSQL database request priming applied, all&lt;/td&gt;
&lt;td&gt;1030&lt;/td&gt;
&lt;td&gt;1185&lt;/td&gt;
&lt;td&gt;2310&lt;/td&gt;
&lt;td&gt;2341&lt;/td&gt;
&lt;td&gt;2345&lt;/td&gt;
&lt;td&gt;2347&lt;/td&gt;
&lt;td&gt;5.33&lt;/td&gt;
&lt;td&gt;5.91&lt;/td&gt;
&lt;td&gt;6.50&lt;/td&gt;
&lt;td&gt;11.64&lt;/td&gt;
&lt;td&gt;201.70&lt;/td&gt;
&lt;td&gt;1607&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DSQL database request priming applied, last 70&lt;/td&gt;
&lt;td&gt;970&lt;/td&gt;
&lt;td&gt;1076&lt;/td&gt;
&lt;td&gt;1226&lt;/td&gt;
&lt;td&gt;1511&lt;/td&gt;
&lt;td&gt;1511&lt;/td&gt;
&lt;td&gt;1511&lt;/td&gt;
&lt;td&gt;5.37&lt;/td&gt;
&lt;td&gt;5.96&lt;/td&gt;
&lt;td&gt;6.61&lt;/td&gt;
&lt;td&gt;12.01&lt;/td&gt;
&lt;td&gt;203.32&lt;/td&gt;
&lt;td&gt;670&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this part of our article series, we introduced how to apply the Lambda SnapStart priming technique, such as Aurora DSQL request priming. The goal was to even further improve the performance of our Lambda functions. We saw that by doing this kind of priming and writing some additional code, we could additionally reduce the Lambda cold start times compared to simply activating the SnapStart. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. Moreover, we could also reduce the maximal value for the Lambda warm start times by preloading classes (as Java lazily loads classes when they are required for the first time) and doing some preinitialization work (by invoking the method to retrieve the product from the Aurora DSQL products table by its ID). Previously, all this happened &lt;em&gt;once&lt;/em&gt; during the &lt;em&gt;first warm execution&lt;/em&gt; of the Lambda function.&lt;/p&gt;

&lt;p&gt;In the next part of our article series, we'll introduce another Lambda SnapStart priming technique. I call it API Gateway Request Event priming (or full priming). We'll then measure the Lambda performance by applying it and comparing the results with other already introduced approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36298"&gt;series&lt;/a&gt; where I use a NoSQL serverless &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; database instead of Aurora DSQL to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>awslambda</category>
    </item>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and DynamoDB - Part 4 SnapStart and DynamoDB request priming</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Tue, 07 Apr 2026 14:06:04 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-4-45i8</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-4-45i8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg"&gt;part 1&lt;/a&gt;, we introduced our sample application. In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;, we measured the performance (cold and warm start times) of the Lambda function without any optimizations. What we observed was quite a large cold start time. We introduced AWS Lambda SnapStart in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-3-2h31"&gt;part 3&lt;/a&gt; as one of the approaches to reduce the cold start times of the Lambda function. We saw that by enabling the SnapStart on the Lambda function, the cold start time goes down. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect.&lt;/p&gt;

&lt;p&gt;In this part of our article series, we'll introduce how to apply Lambda SnapStart priming techniques, starting with DynamoDB request priming. The goal is to further improve the performance of our Lambda functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application with the enabled AWS Lambda SnapStart using DynamoDB request priming
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-dynamodb" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg"&gt;part 1&lt;/a&gt; and do exactly the same performance measurement as we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, please make sure that we have enabled Lambda SnapStart in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/template.yaml" rel="noopener noreferrer"&gt;template.yaml&lt;/a&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;SnapStart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApplyOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishedVersions&lt;/span&gt; 
    &lt;span class="s"&gt;....&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:+TieredCompilation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:TieredStopAtLevel=1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about the concepts behind the Lambda SnapStart in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/" rel="noopener noreferrer"&gt;SnapStart and runtime hooks&lt;/a&gt; offer you new possibilities to create your Lambda functions for low startup latency. With the pre-snapshot hook, we can prepare our Java application as much as possible for the first call. We load and initialize as much as possible that our Lambda function needs before the Lambda SnapStart creates the snapshot. The name for this technique is priming.&lt;/p&gt;

&lt;p&gt;In this article, I will introduce you to the priming of DynamoDB requests, which we implemented in the extra &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithDynamoDBPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithDynamoDBPrimingHandler&lt;/a&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProductByIdWithDynamoDBPrimingHandler&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; 
                 &lt;span class="nc"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;GetProductByIdWithDynamoDBPrimingHandler&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getGlobalContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;beforeCheckpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterRestore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;   

&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requestEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPathParameters&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optionalProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withStatusCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OK&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;                                    
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objectMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeValueAsString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;optionalProduct&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/snapstart-runtime-hooks-java.html" rel="noopener noreferrer"&gt;Lambda SnapStart CRaC runtime hooks&lt;/a&gt; here. To do this, we need to declare the following dependency in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/pom.xml" rel="noopener noreferrer"&gt;pom.xml&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.github.crac&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;org-crac&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GetProductByIdWithDynamoDBPrimingHandler class additionally implements &lt;em&gt;org.crac.Resource&lt;/em&gt; interface. The class registers itself as a CRaC resource in the constructor of this class. The priming itself happens in the method where we search for the product with the ID equal to 0 in the DynamoDB table. &lt;em&gt;beforeCheckpoint&lt;/em&gt; method is a CRaC runtime hook that is invoked before creating the microVM snapshot. We are not even processing the result of the call to &lt;em&gt;productDao.getProduct("0")&lt;/em&gt;. The product with the ID equal to zero might not even exist in the database. But with this invocation, Java lazily loads and instantiates all the classes that it requires for this invocation. GetItemRequest, GetItemResponse, and many others are among such classes. Also, the expensive one-time initialization of the HTTP Client (default is Apache HTTP Client) happens. The same is true for the initialization of the Jackson Marshaller for converting Java objects to JSON and vice versa. As the priming happens during the deployment phase of the Lambda function when SnapStart is activated and before the SnapStart snapshot is created, the snapshot will already contain all of this. After the fast snapshot restore phase during the Lambda invoke, we'll gain a lot in performance in case the cold start happens. We can leave the afterRestore method empty. We don't need to perform any action after the SnapStart snapshot has been restored. &lt;/p&gt;

&lt;p&gt;By the way, we could also achieve the same result by sending nearly any other request to DynamoDB, for example, &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DescribeTable.html" rel="noopener noreferrer"&gt;DescribeTable&lt;/a&gt;. However, I found it easier to reuse some already existing database operations. And we already implemented the getProduct by ID method in our DynamoDB DAO.  This is a read request without any side effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of our application with Lambda SnapStart and DynamoDB request priming
&lt;/h2&gt;

&lt;p&gt;We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithDynamoDBAndDDBPriming&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithDynamoDBPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithDynamoDBPrimingHandler&lt;/a&gt; shown above. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEVDDB25" https://{$API_GATEWAY_URL}/prod/productsWithDynamoDBPriming/1&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;We designed the experiment exactly as described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I will present the Lambda performance measurements with SnapStart being activated for all approx. 100 cold start times (labelled as &lt;em&gt;all&lt;/em&gt; in the table), but also for the last approx. 70 (labelled as &lt;em&gt;last 70&lt;/em&gt; in the table). With that, the effect of the snapshot tiered cache, which we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-3-2h31"&gt;part 3&lt;/a&gt;, becomes visible to you.&lt;/p&gt;

&lt;p&gt;To show the impact of the SnapStart with the DynamoDB request priming, we'll also present the Lambda performance measurements from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt; and &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-3-2h31"&gt;part 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 13.796 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;3800&lt;/td&gt;
&lt;td&gt;3967&lt;/td&gt;
&lt;td&gt;4183&lt;/td&gt;
&lt;td&gt;4411&lt;/td&gt;
&lt;td&gt;4495&lt;/td&gt;
&lt;td&gt;4499&lt;/td&gt;
&lt;td&gt;5.55&lt;/td&gt;
&lt;td&gt;6.15&lt;/td&gt;
&lt;td&gt;7.00&lt;/td&gt;
&lt;td&gt;12.18&lt;/td&gt;
&lt;td&gt;56.37&lt;/td&gt;
&lt;td&gt;4000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;2294&lt;/td&gt;
&lt;td&gt;2366&lt;/td&gt;
&lt;td&gt;3530&lt;/td&gt;
&lt;td&gt;3547&lt;/td&gt;
&lt;td&gt;3548&lt;/td&gt;
&lt;td&gt;3551&lt;/td&gt;
&lt;td&gt;5.68&lt;/td&gt;
&lt;td&gt;6.30&lt;/td&gt;
&lt;td&gt;7.33&lt;/td&gt;
&lt;td&gt;13.43&lt;/td&gt;
&lt;td&gt;44.74&lt;/td&gt;
&lt;td&gt;2923&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;2247&lt;/td&gt;
&lt;td&gt;2324&lt;/td&gt;
&lt;td&gt;2389&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;5.68&lt;/td&gt;
&lt;td&gt;6.35&lt;/td&gt;
&lt;td&gt;7.39&lt;/td&gt;
&lt;td&gt;13.65&lt;/td&gt;
&lt;td&gt;44.03&lt;/td&gt;
&lt;td&gt;2051&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DynamoDB request priming applied, all&lt;/td&gt;
&lt;td&gt;778&lt;/td&gt;
&lt;td&gt;817&lt;/td&gt;
&lt;td&gt;1544&lt;/td&gt;
&lt;td&gt;1572&lt;/td&gt;
&lt;td&gt;1601&lt;/td&gt;
&lt;td&gt;1602&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;td&gt;6.10&lt;/td&gt;
&lt;td&gt;6.99&lt;/td&gt;
&lt;td&gt;12.01&lt;/td&gt;
&lt;td&gt;34.74&lt;/td&gt;
&lt;td&gt;933&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DynamoDB request priming applied, last 70&lt;/td&gt;
&lt;td&gt;752&lt;/td&gt;
&lt;td&gt;790&lt;/td&gt;
&lt;td&gt;837&lt;/td&gt;
&lt;td&gt;988&lt;/td&gt;
&lt;td&gt;988&lt;/td&gt;
&lt;td&gt;988&lt;/td&gt;
&lt;td&gt;5.46&lt;/td&gt;
&lt;td&gt;6.05&lt;/td&gt;
&lt;td&gt;6.99&lt;/td&gt;
&lt;td&gt;11.92&lt;/td&gt;
&lt;td&gt;42.65&lt;/td&gt;
&lt;td&gt;412&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this part of the series, we introduced how to apply Lambda SnapStart priming techniques and started with DynamoDB request priming. The goal was to even further improve the performance of our Lambda functions. We saw that by doing this kind of priming and writing some additional code, we could significantly further reduce the Lambda cold start times compared to simply activating the SnapStart. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. Moreover, we could significantly reduce the maximal value for the Lambda warm start times by preloading classes (as Java lazily loads classes when they are required for the first time) and doing some preinitialization work (by invoking the method to retrieve the product from the DynamoDB table by its ID). Previously, all this happened &lt;em&gt;once&lt;/em&gt; during the &lt;em&gt;first warm execution&lt;/em&gt; of the Lambda function.&lt;/p&gt;

&lt;p&gt;In the next part of our article series, we'll introduce another Lambda SnapStart priming technique. I call it API Gateway Request Event priming (or full priming). We'll then measure the Lambda performance by applying it and comparing the results with other already introduced approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36919"&gt;series&lt;/a&gt; where I use a relational serverless &lt;a href="https://aws.amazon.com/rds/aurora/dsql/" rel="noopener noreferrer"&gt;Amazon Aurora DSQL&lt;/a&gt; database and additionally the &lt;a href="https://hibernate.org/" rel="noopener noreferrer"&gt;Hibernate ORM framework&lt;/a&gt; instead of DynamoDB to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>awslambda</category>
    </item>
    <item>
      <title>Spring AI with Amazon Bedrock - Part 6 Adding AgentCore Observability</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Thu, 02 Apr 2026 14:45:07 +0000</pubDate>
      <link>https://forem.com/aws-heroes/spring-ai-with-amazon-bedrock-part-6-adding-agentcore-observability-2njj</link>
      <guid>https://forem.com/aws-heroes/spring-ai-with-amazon-bedrock-part-6-adding-agentcore-observability-2njj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/spring-ai-with-amazon-bedrock-part-5-spring-ai-meets-amazon-bedrock-agentcore-2n6n"&gt;part 5&lt;/a&gt;, we showed how to implement a Custom Agent written in Java with Spring AI and to use its MCP Client based on HTTP Streamable transport protocol. We deployed our agent on the Amazon Bedrock AgentCore Runtime. What we didn't show in that part was how to implement AgentCore Observability. And this is what we'll cover now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding AgentCore Observability
&lt;/h2&gt;

&lt;p&gt;If we follow the steps described in the articles &lt;a href="https://dev.to/aws-heroes/amazon-bedrock-agentcore-runtime-part-3-agentcore-observability-f08"&gt;AgentCore Runtime Observability&lt;/a&gt; and &lt;a href="https://dev.to/aws-heroes/amazon-bedrock-agentcore-gateway-part-4-agentcore-gateway-observability-2775"&gt;AgentCore Gateway Observability&lt;/a&gt; and activate logging and tracing for both AgentCore Gateway and Runtime, like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoj784vj2rvzaxxv1t8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoj784vj2rvzaxxv1t8t.png" alt=" " width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobqzs6i1g5zfcwb4rbo4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobqzs6i1g5zfcwb4rbo4.png" alt=" " width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;we'll only see the basic AgentCore metrics, but completely miss Sessions and Traces. The reason for this is that we provided the examples using the Strands Agents SDK. It works well with AgentCore Observability (baked by CloudWatch Generative AI Observability). We only had to add the dependency to &lt;a href="https://github.com/Vadym79/amazon-bedrock-agentcore-demos/blob/main/amazon-agentcore-runtime-to-gateway-demos/bedrock-agentcore-custom-agent/requirements.txt" rel="noopener noreferrer"&gt;aws-opentelemetry-distro&lt;/a&gt; and instrument our code, as shown below in the &lt;a href="https://github.com/Vadym79/amazon-bedrock-agentcore-demos/blob/main/amazon-agentcore-runtime-to-gateway-demos/bedrock-agentcore-custom-agent/Dockerfile" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; file. Strands Agent has all the information on where to send the metrics and traces to the default OTEL provider, AWS CloudWatch. But how does it work for Java applications based on Spring AI and hosted on AgentCore Runtime?&lt;/p&gt;

&lt;p&gt;To view the metrics in the CloudWatch Generative AI observability, we need to add the AWS Distro for Open Telemetry (ADOT) SDK to our agent code. &lt;a href="https://aws-otel.github.io/" rel="noopener noreferrer"&gt;ADOT&lt;/a&gt; is a secure, production-ready, AWS-supported distribution of the OpenTelemetry project. Part of the Cloud Native Computing Foundation, OpenTelemetry provides open source APIs, libraries, and agents to collect distributed traces and metrics for application monitoring. With ADOT, we can instrument our applications just once to send correlated metrics and traces to multiple AWS and Partner monitoring solutions. In our case, we will send the metrics to the CloudWatch GenAI Observability service. &lt;/p&gt;

&lt;p&gt;AWS offers &lt;a href="https://docs.aws.amazon.com/xray/latest/devguide/xray-java-opentel-sdk.html" rel="noopener noreferrer"&gt;AWS Distro for OpenTelemetry Java&lt;/a&gt; with the AWS Distro for OpenTelemetry (ADOT). To get started, see the &lt;a href="https://aws-otel.github.io/docs/getting-started/java-sdk/auto-instr" rel="noopener noreferrer"&gt;AWS Distro for OpenTelemetry Java documentation&lt;/a&gt;. We see there that we have to download the &lt;em&gt;aws-opentelemetry-agent&lt;/em&gt; and run it as the  Java agent to instrument the code on the fly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; https://github.com/aws-observability/aws-otel-java-instrumentation/releases/latest/download/aws-opentelemetry-agent.jar /opt/aws-opentelemetry-agent.jar&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JAVA_TOOL_OPTIONS=-javaagent:/opt/aws-opentelemetry-agent.jar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documentation also says that the second component is required to receive the metrics and traces: the AWS Distro for OpenTelemetry Collector. In all the &lt;a href="https://aws-otel.github.io/docs/getting-started/collector" rel="noopener noreferrer"&gt;examples&lt;/a&gt; AWS provides, the collector is a sidecar application deployed with Docker Compose. Unfortunately, it's not possible to use Docker Compose for the AgentCore Runtime. We only provide the reference to the image in the &lt;a href="https://aws.amazon.com/ecr/" rel="noopener noreferrer"&gt;Amazon Elastic Container Registry&lt;/a&gt; (ECR) repository that the AgentCore Runtime pulls and runs for us.&lt;/p&gt;

&lt;p&gt;It took me a while to figure out how to achieve this, and I even created the &lt;a href="https://github.com/awslabs/agentcore-samples/issues/996" rel="noopener noreferrer"&gt;issue&lt;/a&gt; for it. There is a so-called collector-less &lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-configure.html" rel="noopener noreferrer"&gt;Observability for the Amazon Bedrock AgentCore resources&lt;/a&gt;. As of now, unfortunately, not all parameters to be configured are described in this article. But I combined this information with the article &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-OTLP-UsingADOT.html" rel="noopener noreferrer"&gt;Exporting collector-less telemetry using AWS Distro for OpenTelemetry (ADOT) SDK&lt;/a&gt; to achieve the goal. &lt;/p&gt;

&lt;p&gt;For it, I updated my &lt;a href="https://github.com/Vadym79/amazon-bedrock-agentcore-spring-ai/tree/main/spring-ai-1.1-agent-demo" rel="noopener noreferrer"&gt;spring-ai-1.1-agent-demo&lt;/a&gt; application from &lt;a href="https://dev.to/aws-heroes/spring-ai-with-amazon-bedrock-part-5-spring-ai-meets-amazon-bedrock-agentcore-2n6n"&gt;part 5&lt;/a&gt;. I now use Java 25, Spring Boot 4.0.5, and Spring AI 1.1.3, though you can update to their recent versions. Spring AI 2.0 is currently not GA, so I'll update to it later.&lt;/p&gt;

&lt;p&gt;The complete AgentCore Runtime observability configuration is provided in the &lt;a href="https://github.com/Vadym79/amazon-bedrock-agentcore-spring-ai/blob/main/spring-ai-1.1-agent-demo/Dockerfile" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; https://github.com/aws-observability/aws-otel-java-instrumentation/releases/latest/download/aws-opentelemetry-agent.jar /opt/aws-opentelemetry-agent.jar&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JAVA_TOOL_OPTIONS=-javaagent:/opt/aws-opentelemetry-agent.jar \&lt;/span&gt;
AGENT_OBSERVABILITY_ENABLED=true \
OTEL_RESOURCE_ATTRIBUTES=service.name=agentcore_runtime_spring_ai_demo,aws.log.group.names=/aws/bedrock-agentcore/runtimes/agentcore_runtime_spring_ai_demo-tD7f1W6RGi \
OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-aws-log-group=/aws/bedrock-agentcore/runtimes/agentcore_runtime_spring_ai_demo-tD7f1W6RGi,x-aws-log-stream=runtime-logs,x-aws-metric-namespace=bedrock-agentcore \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf \
OTEL_TRACES_EXPORTER=otlp \
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://xray.us-east-1.amazonaws.com/v1/traces \
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf \
OTEL_LOGS_EXPORTER=otlp \
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs.us-east-1.amazonaws.com/v1/logs 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides the already described steps to download the &lt;em&gt;aws-opentelemetry-agent&lt;/em&gt; and run it as the Java agent to instrument the code, we configured the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AGENT_OBSERVABILITY_ENABLED=true  to indicate that we use Agent Observability and would like to view the traces in the CloudWatch Generative AI Observability and not in X-Ray.&lt;/li&gt;
&lt;li&gt;OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, and OTEL_EXPORTER_OTLP_LOGS_PROTOCOL to be all &lt;em&gt;http/protobuf&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;OTEL_EXPORTER_OTLP_TRACES_ENDPOINT and OTEL_EXPORTER_OTLP_LOGS_ENDPOINT as regional endpoints for traces and logs. If you deploy your application in another region other than us-east-1, you need to adjust the URL.&lt;/li&gt;
&lt;li&gt;OTEL_RESOURCE_ATTRIBUTES to be service.name=agentcore_runtime_spring_ai_demo,aws.log.group.names=/aws/bedrock-agentcore/runtimes/agentcore_runtime_spring_ai_demo-tD7f1W6RGi. Please adjust &lt;em&gt;service.name&lt;/em&gt; value to how you named the service in AgentCore Runtime. I called it &lt;em&gt;agentcore_runtime_spring_ai_demo&lt;/em&gt;. For the suffix of the &lt;em&gt;aws.log.group.names&lt;/em&gt; use your AgentCore Runtime ID (in my case &lt;em&gt;agentcore_runtime_spring_ai_demo-tD7f1W6RGi&lt;/em&gt;). AWS Log Group Name for AgentCore Runtime always follows the pattern: /aws/bedrock-agentcore/runtimes/{RUNTIME_ID}.&lt;/li&gt;
&lt;li&gt;OTEL_EXPORTER_OTLP_LOGS_HEADERS to be x-aws-log-group=/aws/bedrock-agentcore/runtimes/agentcore_runtime_spring_ai_demo-tD7f1W6RGi,x-aws-log-stream=runtime-logs,x-aws-metric-namespace=bedrock-agentcore. The same as above: for the suffix of the &lt;em&gt;x-aws-log-group&lt;/em&gt; use your AgentCore Runtime ID again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After rebuilding and redeploying the application, we can see similar metrics and traces as provided in the articles &lt;a href="https://dev.to/aws-heroes/amazon-bedrock-agentcore-runtime-part-3-agentcore-observability-f08"&gt;AgentCore Runtime Observability&lt;/a&gt; and &lt;a href="https://dev.to/aws-heroes/amazon-bedrock-agentcore-gateway-part-4-agentcore-gateway-observability-2775"&gt;AgentCore Gateway Observability&lt;/a&gt;. There are, of course, some differences in the collected metadata. This is because we use the AWS Open Telemetry Agent distribution for Java and not for Python, as in the articles above. Here are some selected screenshots taken from the &lt;a href="https://us-east-1.console.aws.amazon.com/cloudwatch/home?region=us-east-1#/gen-ai-observability/agent-core/agents" rel="noopener noreferrer"&gt;CloudWatch GenAI Observability: Bedrock AgentCore Observability&lt;/a&gt; for the prompt "Give me an overview of the order with the id equals 210" (the order id exists in the database) which I sent :&lt;/p&gt;

&lt;p&gt;Agent Sessions view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsiz69ncmgforaomq734.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsiz69ncmgforaomq734.png" alt=" " width="739" height="688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agent Traces view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8heo2eros1t9lpgm8xrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8heo2eros1t9lpgm8xrq.png" alt=" " width="717" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Traces trajectory view: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ojbblphop64hmvcptq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ojbblphop64hmvcptq1.png" alt=" " width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Traces timeline views: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faun9pf7ata3gvn0jerrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faun9pf7ata3gvn0jerrq.png" alt=" " width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniiwa175n4z2dppcpz9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniiwa175n4z2dppcpz9z.png" alt=" " width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please note that our Spring AI application currently doesn't use short-term and long-term AgentCore Memory. We implemented both with Strands Agents but not Spring AI. So, there won't be any Memory metrics. I'll add AgentCore Memory later, when I cover &lt;a href="https://github.com/spring-ai-community/spring-ai-agentcore/blob/main/README.md#agentcore-memory" rel="noopener noreferrer"&gt;Spring AI AgentCore&lt;/a&gt;, which is currently in preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we described how to configure AgentCore Observability in a collector-less way. This involves running the Java &lt;em&gt;aws-opentelemetry-agent&lt;/em&gt; agent to instrument the code and set a bunch of environment variables in the Docker file. Please also make sure you have activated logging and tracing for both AgentCore Gateway and Runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>agenticai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and Aurora DSQL - Part 3 Introducing Lambda SnapStart</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Tue, 31 Mar 2026 14:34:36 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-3-4ep2</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt;, we introduced our sample application. In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;, we measured the performance (cold and warm start times) of the Lambda function without any optimizations. We observed quite a large cold start time, especially if we use the Hibernate ORM framework. Using this framework also significantly increases the artifact size. In this article, we'll introduce AWS Lambda SnapStart as one of the approaches to reducing the cold start times of the Lambda function. We'll also provide the cold and warm start measurements of the sample application when the SnapStart is enabled for the Lambda function.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Lambda SnapStart
&lt;/h2&gt;

&lt;p&gt;As we saw in part 2, without any optimizations, Lambda performance measurements showed quite high values, especially for the cold start times. The article &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html" rel="noopener noreferrer"&gt;Understanding the Lambda execution environment lifecycle&lt;/a&gt; provides a good overview of this topic. &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html" rel="noopener noreferrer"&gt;Lambda SnapStart&lt;/a&gt; is one of the optimization approaches to reduce the cold start times. &lt;/p&gt;

&lt;p&gt;Lambda SnapStart can provide a start time of a Lambda function of less than one second. SnapStart simplifies the development of responsive and scalable applications without provisioning resources or implementing complex performance optimizations.&lt;/p&gt;

&lt;p&gt;The largest portion of startup latency (often referred to as cold start time) is the time Lambda spends initializing the function. This includes loading the function code, starting the runtime, and initializing the function code. With SnapStart, Lambda initializes our function when we publish a function version. Lambda takes a Firecracker microVM snapshot of the memory and disk state of the initialized execution environment. Then it encrypts the snapshot and intelligently caches it to optimize retrieval latency.&lt;/p&gt;

&lt;p&gt;To ensure reliability, Lambda manages multiple copies of each snapshot. Lambda automatically patches snapshots and their copies with the latest runtime and security updates. When we invoke the function version, Lambda restores a new execution environment from the cached snapshot. This happens instead of initializing it from scratch, which improves startup latency. More information can be found in the article &lt;a href="https://aws.amazon.com/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/" rel="noopener noreferrer"&gt;Reducing Java cold starts on AWS Lambda functions with SnapStart&lt;/a&gt;. You can find more information about the Lambda SnapStart in the article &lt;a href="https://aws.amazon.com/blogs/compute/under-the-hood-how-aws-lambda-snapstart-optimizes-function-startup-latency/" rel="noopener noreferrer"&gt;Under the hood: how AWS Lambda SnapStart optimizes function startup latency&lt;/a&gt;. I have published the whole series about &lt;a href="https://dev.to/vkazulkin/measuring-java-11-lambda-cold-starts-with-snapstart-part-1-first-impressions-30a4"&gt;Lambda SnapStart for Java applications&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with JDBC and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-aurora-dsql" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.tourl"&gt;part 1&lt;/a&gt; and do exactly the same performance measurement as we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;. We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithDSQL&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt;. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEDQ25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/em&gt; .&lt;/p&gt;

&lt;p&gt;One important aspect is that we instantiate Jackson ObjectMapper and ProductDao directly in the static initializer block of the GetProductByIdHandler Lambda function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProductByIdHandler&lt;/span&gt;
        &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you create an ObjectMapper for the first time, it initializes a lot of other classes. As a part of this process, it instantiates a lot of singletons. It takes, depending on the hardware, more than a hundred milliseconds. If you create the second ObjectMapper in the same Java process, it takes only 1 millisecond because all the singletons are already there.  By moving the ObjectMapper instantiation to the static initializer block of the Lambda function, we decrease the cold start time. The reason for that is that this initialized object becomes a part of the SnapStart snapshot.&lt;/p&gt;

&lt;p&gt;The same is true for &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/src/main/java/software/amazonaws/example/product/dao/ProductDao.java" rel="noopener noreferrer"&gt;ProductDao&lt;/a&gt;, especially taking into account that we directly preinitialize &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/src/main/java/software/amazonaws/example/product/dao/DsqlDataSourceConfig.java" rel="noopener noreferrer"&gt;DsqlDataSourceConfig&lt;/a&gt; there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;DsqlDataSourceConfig&lt;/span&gt; &lt;span class="n"&gt;dsqlDataSourceConfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DsqlDataSourceConfig&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This, in turn, loads a lot of classes and creates the Hikari Data Source. Moreover, it creates the Hikari connection pool as well. The part of the process is to search for the available JDBC driver. In our case, the PostgreSQL database driver will be found and loaded. Then the initialization of the database connection to Aurora DSQL happens, and this connection is added to the connection pool. We configured the pool size to be 1, because this is enough for the single-thread Lambda function. That's why exactly one database connection will be created. With that, the database connection is ready to be reused. All of these become a part of the SnapStart snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DsqlDataSourceConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;AURORA_DSQL_CLUSTER_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AURORA_DSQL_CLUSTER_ENDPOINT"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;JDBC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"jdbc:aws-dsql:postgresql://"&lt;/span&gt;
    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;AURORA_DSQL_CLUSTER_ENDPOINT&lt;/span&gt;
     &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;":5432/postgres?sslmode=verify-full&amp;amp;sslfactory=org.postgresql.ssl.DefaultJavaSSLFactory"&lt;/span&gt;
 &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;amp;token-duration-secs=900"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;


&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;HikariDataSource&lt;/span&gt; &lt;span class="n"&gt;hds&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;    
   &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HikariConfig&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
   &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUsername&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"admin"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJdbcUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JDBC_URL&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMaxLifetime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1500&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// pool connection expiration time in milli seconds, default 30&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMaximumPoolSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// default is 10&lt;/span&gt;
    &lt;span class="n"&gt;hds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HikariDataSource&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, please make sure that we have enabled Lambda SnapStart in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;template.yaml&lt;/a&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;SnapStart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApplyOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishedVersions&lt;/span&gt; 
    &lt;span class="s"&gt;....&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:+TieredCompilation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:TieredStopAtLevel=1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that I measured only the performance of the Lambda function. On top of that comes also the latency of the trigger - in our case, the API Gateway REST API.&lt;/p&gt;

&lt;p&gt;Please also note the effect of the &lt;a href="https://dev.to/aws-builders/aws-snapstart-part-17-impact-of-the-snapshot-tiered-cache-on-the-cold-starts-with-java-21-52ef"&gt;Lambda SnapStart snapshot tiered cache&lt;/a&gt;. This means that in the case of SnapStart activation, we get the largest cold starts during the first measurements. Due to the tiered cache, the subsequent cold starts will have lower values. For more details about the technical implementation of AWS SnapStart and its tiered cache, I refer you to the presentation by Mike Danilov: &lt;a href="https://www.infoq.com/presentations/aws-lambda-arch/" rel="noopener noreferrer"&gt;"AWS Lambda Under the Hood"&lt;/a&gt;. Please also read the already mentioned article &lt;a href="https://aws.amazon.com/blogs/compute/under-the-hood-how-aws-lambda-snapstart-optimizes-function-startup-latency/" rel="noopener noreferrer"&gt;Under the hood: how AWS Lambda SnapStart optimizes function startup latency&lt;/a&gt;. Therefore, I will present the Lambda performance measurements with SnapStart being activated for 2 cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For all approximately 100 cold start times (labelled as &lt;em&gt;all&lt;/em&gt; in the table)&lt;/li&gt;
&lt;li&gt;For the last approximately 70 (labelled as &lt;em&gt;last 70&lt;/em&gt; in the table). With that, the effect of the snapshot tiered cache becomes visible to you. Depending on how often the respective Lambda function is updated and thus some layers of the cache are invalidated, a Lambda function can experience thousands or tens of thousands of cold starts during its life cycle, so that the first longer-lasting cold starts no longer carry much weight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To show the impact of the SnapStart, we'll also present the Lambda performance measurements without SnapStart being activated from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 42.333 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;2336&lt;/td&gt;
&lt;td&gt;2453&lt;/td&gt;
&lt;td&gt;2827&lt;/td&gt;
&lt;td&gt;3026&lt;/td&gt;
&lt;td&gt;3131&lt;/td&gt;
&lt;td&gt;3132&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.29&lt;/td&gt;
&lt;td&gt;5.73&lt;/td&gt;
&lt;td&gt;8.88&lt;/td&gt;
&lt;td&gt;195.38&lt;/td&gt;
&lt;td&gt;531&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;970&lt;/td&gt;
&lt;td&gt;1058&lt;/td&gt;
&lt;td&gt;1705&lt;/td&gt;
&lt;td&gt;1726&lt;/td&gt;
&lt;td&gt;1734&lt;/td&gt;
&lt;td&gt;1735&lt;/td&gt;
&lt;td&gt;4.92&lt;/td&gt;
&lt;td&gt;5.33&lt;/td&gt;
&lt;td&gt;5.86&lt;/td&gt;
&lt;td&gt;9.84&lt;/td&gt;
&lt;td&gt;198.52&lt;/td&gt;
&lt;td&gt;1134&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;901&lt;/td&gt;
&lt;td&gt;960&lt;/td&gt;
&lt;td&gt;1061&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;1212&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.29&lt;/td&gt;
&lt;td&gt;5.77&lt;/td&gt;
&lt;td&gt;9.54&lt;/td&gt;
&lt;td&gt;196.94&lt;/td&gt;
&lt;td&gt;719&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with Hibernate and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;The same as mentioned above holds true for the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-hibernate-aurora-dsql" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.tourl"&gt;part 1&lt;/a&gt;, using Hibernate instead of JDBC. We will measure the performance of our &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithHibernateAndDSQL&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt;. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEHADQ25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The most important difference is that in the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/dao/ProductDao.java" rel="noopener noreferrer"&gt;ProductDao&lt;/a&gt;, we create the Hibernate Session Factory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SessionFactory&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;   &lt;span class="nc"&gt;HibernateUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSessionFactory&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/dao/HibernateUtils.java" rel="noopener noreferrer"&gt;HibernateUtils&lt;/a&gt;, we set the same Hikari connection pool properties as in the example above. We then pass those properties to the Hibernate configuration along with the classes annotated as entities. The final part is to build a Hibernate session factory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HibernateUtils&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;AURORA_DSQL_CLUSTER_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getenv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AURORA_DSQL_CLUSTER_ENDPOINT"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;JDBC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"jdbc:aws-dsql:postgresql://"&lt;/span&gt;
   &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;AURORA_DSQL_CLUSTER_ENDPOINT&lt;/span&gt;
   &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;":5432/postgres?sslmode=verify-full&amp;amp;sslfactory=org.postgresql.ssl.DefaultJavaSSLFactory"&lt;/span&gt;
   &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;amp;token-duration-secs=900"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;SessionFactory&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getHibernateSessionFactory&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;HibernateUtils&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;SessionFactory&lt;/span&gt; &lt;span class="nf"&gt;getHibernateSessionFactory&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;           
   &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Properties&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
   &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jakarta.persistence.jdbc.user"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"admin"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jakarta.persistence.jdbc.url"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;JDBC_URL&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hibernate.connection.pool_size"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hibernate.hikari.maxLifetime"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Configuration&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProperties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAnnotatedClass&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buildSessionFactory&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;    
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;SessionFactory&lt;/span&gt; &lt;span class="nf"&gt;getSessionFactory&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All these steps involve a lot of class loading and preinitialization: Hikari Data Source, Hibernate Configuration, and Session Factory. Moreover, it creates the Hikari connection pool as well. The part of the process is to search for the available JDBC driver. In our case, the PostgreSQL database driver will be found and loaded. Then the initialization of the database connection to Aurora DSQL happens, and this connection is added to the connection pool. We configured the pool size to be 1, because this is enough for the single-thread Lambda function. That's why exactly one database connection will be created. With that, the database connection is ready to be reused. All of these become a part of the SnapStart snapshot.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 42.333 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;6243&lt;/td&gt;
&lt;td&gt;6625&lt;/td&gt;
&lt;td&gt;7056&lt;/td&gt;
&lt;td&gt;8480&lt;/td&gt;
&lt;td&gt;8651&lt;/td&gt;
&lt;td&gt;8658&lt;/td&gt;
&lt;td&gt;5.46&lt;/td&gt;
&lt;td&gt;5.96&lt;/td&gt;
&lt;td&gt;6.50&lt;/td&gt;
&lt;td&gt;9.77&lt;/td&gt;
&lt;td&gt;200.10&lt;/td&gt;
&lt;td&gt;707&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;1277&lt;/td&gt;
&lt;td&gt;1360&lt;/td&gt;
&lt;td&gt;3050&lt;/td&gt;
&lt;td&gt;3103&lt;/td&gt;
&lt;td&gt;3200&lt;/td&gt;
&lt;td&gt;3201&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;td&gt;6.01&lt;/td&gt;
&lt;td&gt;6.45&lt;/td&gt;
&lt;td&gt;10.16&lt;/td&gt;
&lt;td&gt;196.94&lt;/td&gt;
&lt;td&gt;2349&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;1258&lt;/td&gt;
&lt;td&gt;1320&lt;/td&gt;
&lt;td&gt;1437&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;1634&lt;/td&gt;
&lt;td&gt;5.42&lt;/td&gt;
&lt;td&gt;5.91&lt;/td&gt;
&lt;td&gt;6.40&lt;/td&gt;
&lt;td&gt;10.08&lt;/td&gt;
&lt;td&gt;195.94&lt;/td&gt;
&lt;td&gt;1093&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article of the series, we introduced AWS Lambda SnapStart as one of the approaches to reduce the cold start times of the Lambda function. We observed that by enabling the SnapStart on the Lambda function, the cold start time goes down significantly for both sample applications. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. The biggest impact of just enabling the SnapStart is on the application using the Hibernate. But still, the cold start remains quite high. In the next article, we'll explore the first Lambda SnapStart priming technique. I call it the database (in our case, Aurora DSQL) request priming. The goal of applying priming is to preload and preinitialize as much as possible in the SnapStart snapshot during the deployment phase. With that, all those things will already be available directly after the SnapStart snapshot restore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36298"&gt;series&lt;/a&gt; where I use a NoSQL serverless &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; database instead of Aurora DSQL to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>awslambda</category>
    </item>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and DynamoDB - Part 3 Introducing Lambda SnapStart</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Mon, 30 Mar 2026 15:08:46 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-3-2h31</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-dynamodb-part-3-2h31</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg"&gt;part 1&lt;/a&gt;, we introduced our sample application. In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;, we measured the performance (cold and warm start times) of the Lambda function without any optimizations. We observed quite a large cold start time. In this article, we'll introduce AWS Lambda SnapStart as one of the approaches to reducing the cold start times of the Lambda function. We'll also provide the cold and warm start measurements of the sample application when the SnapStart is enabled for the Lambda function.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Lambda SnapStart
&lt;/h2&gt;

&lt;p&gt;As we saw in part 2, without any optimizations, Lambda performance measurements showed quite high values, especially for the cold start times. The article &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html" rel="noopener noreferrer"&gt;Understanding the Lambda execution environment lifecycle&lt;/a&gt; provides a good overview of this topic. &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html" rel="noopener noreferrer"&gt;Lambda SnapStart&lt;/a&gt; is one of the optimization approaches to reduce the cold start times. &lt;/p&gt;

&lt;p&gt;Lambda SnapStart can provide a start time of a Lambda function of less than one second. SnapStart simplifies the development of responsive and scalable applications without provisioning resources or implementing complex performance optimizations.&lt;/p&gt;

&lt;p&gt;The largest portion of startup latency (often referred to as cold start time) is the time Lambda spends initializing the function, which includes loading the function code, starting the runtime, and initializing the function code. With SnapStart, Lambda initializes our function when we publish a function version. Lambda takes a Firecracker microVM snapshot of the memory and disk state of the initialized execution environment. Then it encrypts the snapshot and intelligently caches it to optimize retrieval latency.&lt;/p&gt;

&lt;p&gt;To ensure reliability, Lambda manages multiple copies of each snapshot. Lambda automatically patches snapshots and their copies with the latest runtime and security updates. When we invoke the function version, Lambda restores a new execution environment from the cached snapshot, instead of initializing it from scratch, which improves startup latency. You can find more information about the Lambda SnapStart in the article &lt;a href="https://aws.amazon.com/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/" rel="noopener noreferrer"&gt;Reducing Java cold starts on AWS Lambda functions with SnapStart&lt;/a&gt;. You can also read about the internals of the SnapStart in the article &lt;a href="https://aws.amazon.com/blogs/compute/under-the-hood-how-aws-lambda-snapstart-optimizes-function-startup-latency/" rel="noopener noreferrer"&gt;Under the hood: how AWS Lambda SnapStart optimizes function startup latency&lt;/a&gt;. I have published the whole series about &lt;a href="https://dev.to/vkazulkin/measuring-java-11-lambda-cold-starts-with-snapstart-part-1-first-impressions-30a4"&gt;Lambda SnapStart for Java applications&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application
&lt;/h2&gt;

&lt;p&gt;We'll reuse the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-dynamodb" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; from &lt;a href="https://dev.tourl"&gt;part 1&lt;/a&gt; and do exactly the same performance measurement as we described in &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;. We'll measure the performance of the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithDynamoDB&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt;. We will trigger it by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEVDDB25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;One important aspect is that we instantiate Jackson ObjectMapper and ProductDao directly in the static initializer block of the GetProductByIdHandler Lambda function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetProductByIdHandler&lt;/span&gt;
        &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RequestHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;APIGatewayProxyRequestEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="n"&gt;objectMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="n"&gt;productDao&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you create an ObjectMapper for the first time, it initializes a lot of other classes. As a part of this process, it instantiates a lot of singletons. It takes, depending on the hardware, more than a hundred milliseconds. If you create the second ObjectMapper in the same Java process, it takes only 1 millisecond because all the singletons are already there.  By moving the ObjectMapper instantiation to the static initializer block of the Lambda function, we decrease the cold start time. The reason for that is that this initialized object becomes a part of the SnapStart snapshot.&lt;/p&gt;

&lt;p&gt;The same is true for &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/src/main/java/software/amazonaws/example/product/dao/ProductDao.java" rel="noopener noreferrer"&gt;ProductDao&lt;/a&gt;, especially taking into account that we directly create the instance of the DynamoDbClient there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductDao&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;DynamoDbClient&lt;/span&gt; &lt;span class="n"&gt;dynamoDbClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DynamoDbClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;credentialsProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DefaultCredentialsProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;overrideConfiguration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ClientOverrideConfiguration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This, in turn, loads a lot of classes, which also become a part of the SnapStart snapshot.&lt;/p&gt;

&lt;p&gt;Also, please make sure that we have enabled Lambda SnapStart in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-dynamodb/template.yaml" rel="noopener noreferrer"&gt;template.yaml&lt;/a&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;SnapStart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApplyOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishedVersions&lt;/span&gt; 
    &lt;span class="s"&gt;....&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:+TieredCompilation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:TieredStopAtLevel=1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that I measured only the performance of the Lambda function. On top of that comes also the latency of the trigger - in our case, the API Gateway REST API.&lt;/p&gt;

&lt;p&gt;Please also note the effect of the &lt;a href="https://dev.to/aws-builders/aws-snapstart-part-17-impact-of-the-snapshot-tiered-cache-on-the-cold-starts-with-java-21-52ef"&gt;Lambda SnapStart snapshot tiered cache&lt;/a&gt;. This means that in the case of SnapStart activation, we get the largest cold starts during the first measurements. Due to the tiered cache, the subsequent cold starts will have lower values. For more details about the technical implementation of AWS SnapStart and its tiered cache, I refer you to the presentation by Mike Danilov: &lt;a href="https://www.infoq.com/presentations/aws-lambda-arch/" rel="noopener noreferrer"&gt;"AWS Lambda Under the Hood"&lt;/a&gt; and already mentioned article &lt;a href="https://aws.amazon.com/blogs/compute/under-the-hood-how-aws-lambda-snapstart-optimizes-function-startup-latency/" rel="noopener noreferrer"&gt;Under the hood: how AWS Lambda SnapStart optimizes function startup latency&lt;/a&gt;. Therefore, I will present the Lambda performance measurements with SnapStart being activated for all approx. 100 cold start times (labelled as &lt;em&gt;all&lt;/em&gt; in the table), but also for the last approx. 70 (labelled as &lt;em&gt;last 70&lt;/em&gt; in the table), so that the effect of the snapshot tiered cache becomes visible to you. Depending on how often the respective Lambda function is updated and thus some layers of the cache are invalidated, a Lambda function can experience thousands or tens of thousands of cold starts during its life cycle, so that the first longer-lasting cold starts no longer carry much weight.&lt;/p&gt;

&lt;p&gt;To show the impact of the SnapStart, we'll also present the Lambda performance measurements without SnapStart being activated from &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 13.796 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;3800&lt;/td&gt;
&lt;td&gt;3967&lt;/td&gt;
&lt;td&gt;4183&lt;/td&gt;
&lt;td&gt;4411&lt;/td&gt;
&lt;td&gt;4495&lt;/td&gt;
&lt;td&gt;4499&lt;/td&gt;
&lt;td&gt;5.55&lt;/td&gt;
&lt;td&gt;6.15&lt;/td&gt;
&lt;td&gt;7.00&lt;/td&gt;
&lt;td&gt;12.18&lt;/td&gt;
&lt;td&gt;56.37&lt;/td&gt;
&lt;td&gt;4000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;2294&lt;/td&gt;
&lt;td&gt;2366&lt;/td&gt;
&lt;td&gt;3530&lt;/td&gt;
&lt;td&gt;3547&lt;/td&gt;
&lt;td&gt;3548&lt;/td&gt;
&lt;td&gt;3551&lt;/td&gt;
&lt;td&gt;5.68&lt;/td&gt;
&lt;td&gt;6.30&lt;/td&gt;
&lt;td&gt;7.33&lt;/td&gt;
&lt;td&gt;13.43&lt;/td&gt;
&lt;td&gt;44.74&lt;/td&gt;
&lt;td&gt;2923&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;2247&lt;/td&gt;
&lt;td&gt;2324&lt;/td&gt;
&lt;td&gt;2389&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;2637&lt;/td&gt;
&lt;td&gt;5.68&lt;/td&gt;
&lt;td&gt;6.35&lt;/td&gt;
&lt;td&gt;7.39&lt;/td&gt;
&lt;td&gt;13.65&lt;/td&gt;
&lt;td&gt;44.03&lt;/td&gt;
&lt;td&gt;2051&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article of the series, we introduced AWS Lambda SnapStart as one of the approaches to reduce the cold start times of the Lambda function. We observed that by enabling the SnapStart on the Lambda function, the cold start time goes down. It's especially noticeable when looking at the "last 70" measurements with the snapshot tiered cache effect. But still, the cold start remains quite high. In the next article, we'll explore the first Lambda SnapStart priming technique. I call it the database (in our case, DynamoDB) request priming. The goal of applying priming is to preload and preinitialize as much as possible in the SnapStart snapshot during the deployment phase. With that, all those things will already be available directly after the SnapStart snapshot restore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36919"&gt;series&lt;/a&gt; where I use a relational serverless &lt;a href="https://aws.amazon.com/rds/aurora/dsql/" rel="noopener noreferrer"&gt;Amazon Aurora DSQL&lt;/a&gt; database and additionally the &lt;a href="https://hibernate.org/" rel="noopener noreferrer"&gt;Hibernate ORM framework&lt;/a&gt; instead of DynamoDB to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>awslmabda</category>
    </item>
    <item>
      <title>Micronaut 4 application on AWS Lambda- Part 8 Measuring Lambda cold and warm starts with REST API application</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Fri, 27 Mar 2026 14:26:08 +0000</pubDate>
      <link>https://forem.com/aws-heroes/micronaut-4-application-on-aws-lambda-part-8-measuring-lambda-cold-and-warm-starts-with-rest-api-59hl</link>
      <guid>https://forem.com/aws-heroes/micronaut-4-application-on-aws-lambda-part-8-measuring-lambda-cold-and-warm-starts-with-rest-api-59hl</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/micronaut-4-application-on-aws-lambda-part-6-rest-api-application-h42"&gt;part 6&lt;/a&gt;, we learned how to develop a pure Micronaut REST application and deploy it on AWS Lambda. &lt;/p&gt;

&lt;p&gt;For the preparation of my talk about developing Serverless Java REST applications on AWS using frameworks such as Micronaut, I found time to measure the performance of the Lambda function with the different approaches (see below). I refer to my articles where we didn't use the Micronaut REST controllers directly, but the pure AWS Lambda functions, to see how we implemented the SnapStart and priming approaches. They look completely the same when using Micronaut REST controllers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No SnapStart. Please read the article &lt;a href="https://dev.to/aws-heroes/micronaut-4-application-on-aws-lambda-part-1-introduction-to-the-sample-application-and-first-1g62"&gt;Introduction to the sample application and first Lambda performance measurements&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SnapStart enabled, but no priming applied. Please read the article &lt;a href="https://dev.to/aws-heroes/micronaut-4-application-on-aws-lambda-part-2-reducing-lambda-cold-starts-with-lambda-snapstart-9l6"&gt;Reducing Lambda cold starts with Lambda SnapStart&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SnapStart enabled, and DynamoDB request priming applied. Please read the article &lt;a href="https://dev.to/aws-heroes/micronaut-4-application-on-aws-lambda-part-3-reducing-lambda-cold-starts-with-snapstart-and-4f30"&gt;Reducing Lambda cold starts with SnapStart and DynamoDB request priming&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SnapStart enabled, and API Gateway request event priming applied. Please read the article &lt;a href="https://dev.to/aws-heroes/micronaut-4-application-on-aws-lambda-part-4-reducing-lambda-cold-starts-with-snapstart-and-api-hkn"&gt;Reducing Lambda cold starts with SnapStart and API Gateway request event priming&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please review the &lt;a href="https://github.com/Vadym79/AWSLambdaJavaWithMicronaut/tree/main/micronaut-4.9-rest-api" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; where I provided the implementation of the SnapStart priming approaches, similar to the sample application, where we didn't use the Micronaut REST controllers. The business logic for it is in the &lt;a href="https://github.com/Vadym79/AWSLambdaJavaWithMicronaut/tree/main/micronaut-4.9-rest-api/src/main/java/software/amazonaws/example/product/controller" rel="noopener noreferrer"&gt;package&lt;/a&gt;. But you also have to adjust the &lt;a href="https://github.com/Vadym79/AWSLambdaJavaWithMicronaut/blob/main/micronaut-4.9-rest-api/template.yaml" rel="noopener noreferrer"&gt;SAM template&lt;/a&gt; accordingly, as I described in the above-mentioned references.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario Number&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;5496&lt;/td&gt;
&lt;td&gt;5646&lt;/td&gt;
&lt;td&gt;5894&lt;/td&gt;
&lt;td&gt;6239&lt;/td&gt;
&lt;td&gt;6378&lt;/td&gt;
&lt;td&gt;6383&lt;/td&gt;
&lt;td&gt;8.00&lt;/td&gt;
&lt;td&gt;9.68&lt;/td&gt;
&lt;td&gt;13.51&lt;/td&gt;
&lt;td&gt;29.40&lt;/td&gt;
&lt;td&gt;68.19&lt;/td&gt;
&lt;td&gt;1817&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;2064&lt;/td&gt;
&lt;td&gt;2116&lt;/td&gt;
&lt;td&gt;2185&lt;/td&gt;
&lt;td&gt;2260&lt;/td&gt;
&lt;td&gt;4007&lt;/td&gt;
&lt;td&gt;4009&lt;/td&gt;
&lt;td&gt;8.00&lt;/td&gt;
&lt;td&gt;9.23&lt;/td&gt;
&lt;td&gt;11.34&lt;/td&gt;
&lt;td&gt;23.54&lt;/td&gt;
&lt;td&gt;49.64&lt;/td&gt;
&lt;td&gt;3201&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;2042&lt;/td&gt;
&lt;td&gt;2100&lt;/td&gt;
&lt;td&gt;2166&lt;/td&gt;
&lt;td&gt;2262&lt;/td&gt;
&lt;td&gt;2262&lt;/td&gt;
&lt;td&gt;2262&lt;/td&gt;
&lt;td&gt;8.13&lt;/td&gt;
&lt;td&gt;9.53&lt;/td&gt;
&lt;td&gt;12.09&lt;/td&gt;
&lt;td&gt;24.30&lt;/td&gt;
&lt;td&gt;48.86&lt;/td&gt;
&lt;td&gt;1742&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DynamoDB request priming applied, all&lt;/td&gt;
&lt;td&gt;849&lt;/td&gt;
&lt;td&gt;911&lt;/td&gt;
&lt;td&gt;1856&lt;/td&gt;
&lt;td&gt;1892&lt;/td&gt;
&lt;td&gt;11951&lt;/td&gt;
&lt;td&gt;1951&lt;/td&gt;
&lt;td&gt;7.75&lt;/td&gt;
&lt;td&gt;8.88&lt;/td&gt;
&lt;td&gt;10.41&lt;/td&gt;
&lt;td&gt;22.19&lt;/td&gt;
&lt;td&gt;52.47&lt;/td&gt;
&lt;td&gt;1115&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DynamoDB request priming applied, last 70&lt;/td&gt;
&lt;td&gt;827&lt;/td&gt;
&lt;td&gt;862&lt;/td&gt;
&lt;td&gt;923&lt;/td&gt;
&lt;td&gt;1151&lt;/td&gt;
&lt;td&gt;1151&lt;/td&gt;
&lt;td&gt;1151&lt;/td&gt;
&lt;td&gt;7.87&lt;/td&gt;
&lt;td&gt;9.09&lt;/td&gt;
&lt;td&gt;10.66&lt;/td&gt;
&lt;td&gt;22.73&lt;/td&gt;
&lt;td&gt;48.06&lt;/td&gt;
&lt;td&gt;449&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and API Gateway request event priming applied, all&lt;/td&gt;
&lt;td&gt;693&lt;/td&gt;
&lt;td&gt;741&lt;/td&gt;
&lt;td&gt;1443&lt;/td&gt;
&lt;td&gt;1457&lt;/td&gt;
&lt;td&gt;1460&lt;/td&gt;
&lt;td&gt;1461&lt;/td&gt;
&lt;td&gt;8.07&lt;/td&gt;
&lt;td&gt;9.54&lt;/td&gt;
&lt;td&gt;11.73&lt;/td&gt;
&lt;td&gt;22.37&lt;/td&gt;
&lt;td&gt;61.04&lt;/td&gt;
&lt;td&gt;685&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and API Gateway request event priming applied, last 70&lt;/td&gt;
&lt;td&gt;685&lt;/td&gt;
&lt;td&gt;728&lt;/td&gt;
&lt;td&gt;773&lt;/td&gt;
&lt;td&gt;893&lt;/td&gt;
&lt;td&gt;893&lt;/td&gt;
&lt;td&gt;893&lt;/td&gt;
&lt;td&gt;8.33&lt;/td&gt;
&lt;td&gt;10.00&lt;/td&gt;
&lt;td&gt;12.30&lt;/td&gt;
&lt;td&gt;23.28&lt;/td&gt;
&lt;td&gt;52.47&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What we observe is that both cold and warm start times of the sample application using Micronaut REST controllers are generally higher when using the pure AWS Lambda functions. I suspect the reason for it lies in an extra adapter layer used to convert the Lambda request to the REST controller. This adapter uses several extra dependencies, which leads to a much bigger artifact size, which is also one of the factors of the increased cold start times. The advantage of using the REST controller is the ability to more easily migrate the business logic from AWS Lambda to EC2 or Containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>micronaut</category>
    </item>
    <item>
      <title>Quarkus 3 application on AWS Lambda- Part 10 Measuring Lambda cold and warm starts with REST API application</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Thu, 26 Mar 2026 16:16:56 +0000</pubDate>
      <link>https://forem.com/aws-heroes/quarkus-3-application-on-aws-lambda-part-10-measuring-lambda-cold-and-warm-starts-with-rest-api-ike</link>
      <guid>https://forem.com/aws-heroes/quarkus-3-application-on-aws-lambda-part-10-measuring-lambda-cold-and-warm-starts-with-rest-api-ike</guid>
      <description>&lt;h2&gt;
  
  
  Measurements of cold and warm start times of our application
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/quarkus-3-application-on-aws-lambda-part-8-rest-api-application-3p4"&gt;part 8&lt;/a&gt;, we learned how to develop a pure Quarkus REST application and deploy it on AWS Lambda. &lt;/p&gt;

&lt;p&gt;For the preparation of my talk about developing Serverless Java REST applications on AWS using frameworks such as Quarkus, I found time to measure the performance of the Lambda function with the different approaches (see below). I refer to my articles where we didn't use the Quarkus REST controllers directly, but the pure AWS Lambda functions, to see how we implemented the SnapStart and priming approaches. They look completely the same when using Quarkus REST controllers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No SnapStart. Please read the article &lt;a href="https://dev.to/aws-heroes/quarkus-3-application-on-aws-lambda-part-1-introduction-to-the-sample-application-and-first-lambda-30lb"&gt;Introduction to the sample application and first Lambda performance measurements&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SnapStart enabled, but no priming applied. Please read the article &lt;a href="https://dev.to/aws-heroes/quarkus-3-application-on-aws-lambda-part-2-reducing-lambda-cold-starts-with-lambda-snapstart-5fo9"&gt;Reducing Lambda cold starts with Lambda SnapStart&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SnapStart enabled, and DynamoDB request priming applied. Please read the article &lt;a href="https://dev.to/aws-heroes/quarkus-3-application-on-aws-lambda-part-3-reducing-lambda-cold-starts-with-snapstart-and-dynamodb-195a"&gt;Reducing Lambda cold starts with SnapStart and DynamoDB request priming&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;SnapStart enabled, and API Gateway request event priming applied. Please read the article &lt;a href="https://dev.to/aws-heroes/quarkus-3-application-on-aws-lambda-part-4-reducing-lambda-cold-starts-with-snapstart-and-api-35al"&gt;Reducing Lambda cold starts with SnapStart and API Gateway request event priming&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please review the &lt;a href="https://github.com/Vadym79/AWSLambdaJavaWithQuarkus/tree/main/quarkus-3.24-rest-api/" rel="noopener noreferrer"&gt;sample application&lt;/a&gt; where I provided the implementation of the SnapStart priming approaches, similar to the sample application, where we didn't use the Quarkus REST controllers. The business logic for it is in the &lt;a href="https://github.com/Vadym79/AWSLambdaJavaWithQuarkus/tree/main/quarkus-3.24-rest-api/src/main/java/software/amazonaws/example/product/controller" rel="noopener noreferrer"&gt;package&lt;/a&gt;. But you also have to adjust the &lt;a href="https://github.com/Vadym79/AWSLambdaJavaWithQuarkus/blob/main/quarkus-3.24-rest-api/template.yaml" rel="noopener noreferrer"&gt;SAM template&lt;/a&gt; accordingly, as I described in the above-mentioned references.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario Number&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;4586&lt;/td&gt;
&lt;td&gt;4697&lt;/td&gt;
&lt;td&gt;4855&lt;/td&gt;
&lt;td&gt;5073&lt;/td&gt;
&lt;td&gt;5384&lt;/td&gt;
&lt;td&gt;5389&lt;/td&gt;
&lt;td&gt;6.51&lt;/td&gt;
&lt;td&gt;7.39&lt;/td&gt;
&lt;td&gt;9.08&lt;/td&gt;
&lt;td&gt;22.45&lt;/td&gt;
&lt;td&gt;101.41&lt;/td&gt;
&lt;td&gt;1747&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, all&lt;/td&gt;
&lt;td&gt;1979&lt;/td&gt;
&lt;td&gt;2049&lt;/td&gt;
&lt;td&gt;2816&lt;/td&gt;
&lt;td&gt;2916&lt;/td&gt;
&lt;td&gt;2990&lt;/td&gt;
&lt;td&gt;2991&lt;/td&gt;
&lt;td&gt;6.51&lt;/td&gt;
&lt;td&gt;7.51&lt;/td&gt;
&lt;td&gt;9.08&lt;/td&gt;
&lt;td&gt;22.09&lt;/td&gt;
&lt;td&gt;106.35&lt;/td&gt;
&lt;td&gt;2134&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled but no priming applied, last 70&lt;/td&gt;
&lt;td&gt;1953&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;2098&lt;/td&gt;
&lt;td&gt;2278&lt;/td&gt;
&lt;td&gt;2278&lt;/td&gt;
&lt;td&gt;2278&lt;/td&gt;
&lt;td&gt;6.51&lt;/td&gt;
&lt;td&gt;7.51&lt;/td&gt;
&lt;td&gt;9.08&lt;/td&gt;
&lt;td&gt;22.09&lt;/td&gt;
&lt;td&gt;78.66&lt;/td&gt;
&lt;td&gt;1673&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DynamoDB request priming applied, all&lt;/td&gt;
&lt;td&gt;1017&lt;/td&gt;
&lt;td&gt;1057&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;2041&lt;/td&gt;
&lt;td&gt;2043&lt;/td&gt;
&lt;td&gt;2045&lt;/td&gt;
&lt;td&gt;6.30&lt;/td&gt;
&lt;td&gt;7.27&lt;/td&gt;
&lt;td&gt;8.94&lt;/td&gt;
&lt;td&gt;21.07&lt;/td&gt;
&lt;td&gt;56.36&lt;/td&gt;
&lt;td&gt;1273&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and DynamoDB request priming applied, last 70&lt;/td&gt;
&lt;td&gt;1017&lt;/td&gt;
&lt;td&gt;1040&lt;/td&gt;
&lt;td&gt;1095&lt;/td&gt;
&lt;td&gt;1177&lt;/td&gt;
&lt;td&gt;1177&lt;/td&gt;
&lt;td&gt;1177&lt;/td&gt;
&lt;td&gt;6.30&lt;/td&gt;
&lt;td&gt;7.27&lt;/td&gt;
&lt;td&gt;8.94&lt;/td&gt;
&lt;td&gt;21.07&lt;/td&gt;
&lt;td&gt;47.44&lt;/td&gt;
&lt;td&gt;629&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and API Gateway request event priming applied, all&lt;/td&gt;
&lt;td&gt;747&lt;/td&gt;
&lt;td&gt;801&lt;/td&gt;
&lt;td&gt;1092&lt;/td&gt;
&lt;td&gt;1258&lt;/td&gt;
&lt;td&gt;1310&lt;/td&gt;
&lt;td&gt;1311&lt;/td&gt;
&lt;td&gt;6.51&lt;/td&gt;
&lt;td&gt;7.63&lt;/td&gt;
&lt;td&gt;9.53&lt;/td&gt;
&lt;td&gt;21.75&lt;/td&gt;
&lt;td&gt;62.00&lt;/td&gt;
&lt;td&gt;304&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart enabled and API Gateway request event priming applied, last 70&lt;/td&gt;
&lt;td&gt;735&lt;/td&gt;
&lt;td&gt;766&lt;/td&gt;
&lt;td&gt;841&lt;/td&gt;
&lt;td&gt;1061&lt;/td&gt;
&lt;td&gt;1061&lt;/td&gt;
&lt;td&gt;1061&lt;/td&gt;
&lt;td&gt;6.51&lt;/td&gt;
&lt;td&gt;7.63&lt;/td&gt;
&lt;td&gt;9.34&lt;/td&gt;
&lt;td&gt;21.75&lt;/td&gt;
&lt;td&gt;55.48&lt;/td&gt;
&lt;td&gt;261&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What we observe is that both cold and warm start times of the sample application using Quarkus REST controllers are generally higher when using the pure AWS Lambda functions. I suspect the reason for it lies in an extra adapter layer used to convert the Lambda request to the REST controller. This adapter uses several extra dependencies, which leads to a much bigger artifact size, which is also one of the factors of the increased cold start times. The advantage of using the REST controller is the ability to more easily migrate the business logic from AWS Lambda to EC2 or Containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>quarkus</category>
    </item>
    <item>
      <title>Serverless applications on AWS with Lambda using Java 25, API Gateway and Aurora DSQL - Part 2 Initial performance measurements</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Tue, 24 Mar 2026 15:27:00 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-2-4bbb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27"&gt;part 1&lt;/a&gt;, we introduced our 2 sample applications (one using JDBC directly and one using Hibernate). In this article, we'll measure the performance (cold and warm start times) of the Lambda function in both applications without any optimizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with JDBC and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;In the following, we will measure the performance of our &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithDSQL&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt;, which we will trigger by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEDQ25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/em&gt;. I assume that you have already created some products as described in part 1.  Two aspects are important to us in terms of performance: cold and warm start times. It is known that Java applications, in particular, have a very high cold start time. The article &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html" rel="noopener noreferrer"&gt;Understanding the Lambda execution environment lifecycle&lt;/a&gt; provides a good overview of this topic. &lt;/p&gt;

&lt;p&gt;The results of the experiment are based on reproducing more than 100 cold starts and about 100,000 warm starts with the Lambda function GetProductByIdJava25WithDSQL. We retrieve the already existing product with ID=1  for the duration of about 1 hour. We send several hundred requests in parallel with some pauses in between. With that, the existing execution environments will be destroyed, and we will experience new Lambda cold starts. We give the Lambda function 1024 MB of memory, which is a good trade-off between performance and cost. We also use (default) x86 Lambda architecture. For the load tests, I used the load test tool &lt;a href="https://github.com/rakyll/hey" rel="noopener noreferrer"&gt;hey&lt;/a&gt;, but you can use whatever tool you want, like &lt;a href="https://www.npmjs.com/package/serverless-artillery" rel="noopener noreferrer"&gt;Serverless-artillery&lt;/a&gt; or &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;With &lt;a href="https://aws.amazon.com/de/blogs/compute/aws-lambda-now-supports-java-25/" rel="noopener noreferrer"&gt;AWS Lambda now supporting Java 25&lt;/a&gt;, there have been some changes for the Java compilation options, especially tiered compilation. Java’s tiered compilation is a just-in-time (JIT) optimization strategy that employs multiple compiler tiers to enhance the performance of frequently executed code progressively using runtime profiling data. Since Java 17, AWS Lambda has modified the default JVM behavior by stopping compilation at the C1 tier (client compiler). This minimizes cold start times for function invocations for most functions. Although for compute-intensive functions with a long duration, customers can benefit from tuning tiered compilation to their workload. Starting with Java 25, Lambda no longer stops tiered compilation at C1 for SnapStart and Provisioned Concurrency. This improves performance in these cases without incurring a cold start penalty. This is because tiered compilation occurs outside of the invoke path in these cases.&lt;/p&gt;

&lt;p&gt;I've found out that stopping compilation at the C1 tier (client compiler) provides the best trade-off for this type of application. This is also true when using SnapStart and priming techniques, which we'll cover in the next article. But this may not be the best choice for other applications. I also show the Lambda performance measurements with the default compilation behavior described above in one of the next articles.&lt;/p&gt;

&lt;p&gt;That's why we set &lt;em&gt;XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation option. To do so, we have to set it in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;template.yaml&lt;/a&gt; in the JAVA_OPTIONS environment variable as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="c1"&gt;#SnapStart:&lt;/span&gt;
      &lt;span class="c1"&gt;#ApplyOn: PublishedVersions &lt;/span&gt;
    &lt;span class="s"&gt;....&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-XX:+TieredCompilation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-XX:TieredStopAtLevel=1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, please make sure that SnapStart isn't configured or disabled as shown above for the initial measurement. We'll cover it in the next articles.&lt;/p&gt;

&lt;p&gt;Please note that I measured only the performance of the Lambda function. On top of that comes also the latency of the trigger - in our case, the API Gateway REST API.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 17.150 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;2336&lt;/td&gt;
&lt;td&gt;2453&lt;/td&gt;
&lt;td&gt;2827&lt;/td&gt;
&lt;td&gt;3026&lt;/td&gt;
&lt;td&gt;3131&lt;/td&gt;
&lt;td&gt;3132&lt;/td&gt;
&lt;td&gt;4.84&lt;/td&gt;
&lt;td&gt;5.29&lt;/td&gt;
&lt;td&gt;5.73&lt;/td&gt;
&lt;td&gt;8.88&lt;/td&gt;
&lt;td&gt;195.38&lt;/td&gt;
&lt;td&gt;531&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Measurements of cold and warm start times of the Lambda function of the sample application with Hibernate and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;The same holds true for this application, but for the application &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-hibernate-aurora-dsql" rel="noopener noreferrer"&gt;aws-lambda-java-25-hibernate-aurora-dsql&lt;/a&gt;. We will measure the performance of our &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/template.yaml" rel="noopener noreferrer"&gt;GetProductByIdJava25WithHibernateAndDSQL&lt;/a&gt; Lambda function mapped to the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-hibernate-aurora-dsql/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt;, which we will trigger by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEHADQ25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I did the measurements with java:25.v19 Amazon Corretto version, and the deployed artifact size of this application was 42.333 KB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold (c) and warm (w) start time with &lt;em&gt;-XX:+TieredCompilation -XX:TieredStopAtLevel=1&lt;/em&gt; compilation in ms:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;c p50&lt;/th&gt;
&lt;th&gt;c p75&lt;/th&gt;
&lt;th&gt;c p90&lt;/th&gt;
&lt;th&gt;c p99&lt;/th&gt;
&lt;th&gt;c p99.9&lt;/th&gt;
&lt;th&gt;c max&lt;/th&gt;
&lt;th&gt;w p50&lt;/th&gt;
&lt;th&gt;w p75&lt;/th&gt;
&lt;th&gt;w p90&lt;/th&gt;
&lt;th&gt;w p99&lt;/th&gt;
&lt;th&gt;w p99.9&lt;/th&gt;
&lt;th&gt;w max&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No SnapStart enabled&lt;/td&gt;
&lt;td&gt;6243&lt;/td&gt;
&lt;td&gt;6625&lt;/td&gt;
&lt;td&gt;7056&lt;/td&gt;
&lt;td&gt;8480&lt;/td&gt;
&lt;td&gt;8651&lt;/td&gt;
&lt;td&gt;8658&lt;/td&gt;
&lt;td&gt;5.46&lt;/td&gt;
&lt;td&gt;5.96&lt;/td&gt;
&lt;td&gt;6.50&lt;/td&gt;
&lt;td&gt;9.77&lt;/td&gt;
&lt;td&gt;200.10&lt;/td&gt;
&lt;td&gt;707&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we made the first Lambda performance measurements (cold and warm start times) of both of our sample applications. We observed quite a large cold start time, especially if we use the Hibernate ORM framework. Using this framework also significantly increases the artifact size. In the next parts of the series, we'll introduce approaches and techniques to reduce the Lambda cold start time with AWS Lambda SnapStart (including various priming techniques) and GraalVM Native Image, and also measure their impact on the Lambda warm start time for both sample applications. Stay tuned!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also watch out for another &lt;a href="https://dev.to/vkazulkin/series/36298"&gt;series&lt;/a&gt; where I use a NoSQL serverless &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; database instead of Aurora DSQL to do the same Lambda performance measurements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you like my content, please follow me on &lt;a href="https://github.com/Vadym79" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and give my repositories a star!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please also check out my &lt;a href="https://vkazulkin.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; for more technical content and upcoming public speaking activities.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>serverless</category>
      <category>awslambda</category>
    </item>
  </channel>
</rss>
