<?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>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>
    <item>
      <title>Serverless applications on AWS using Lambda with Java 25, API Gateway and DynamoDB - Part 2 Initial performance measurements</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Mon, 23 Mar 2026 15:45:18 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-2-initial-3fdj</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; of the series, we introduced our sample application. In this article, we'll measure the performance (cold and warm start times) of the Lambda function without any optimizations.&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;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-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;, which we will trigger by invoking &lt;em&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEVDDB25" 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 &lt;em&gt;GetProductByIdJava25WithDynamoDB&lt;/em&gt;. 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 and default Apache HTTP 4.5 Client to talk to DynamoDB. 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 AWS Lambda now supporting Java 25, 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, also when using SnapStart and priming techniques (which we'll cover in the next article), but may not be the best choice for others. So you simply need to measure to find the right compilation settings for your application. 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-dynamodb/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 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;/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 our sample application. We observed quite a large cold start time. 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. 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/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>Serverless applications on AWS with Lambda using Java 25, API Gateway and Aurora DSQL - Part 1 Sample applications</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Mon, 16 Mar 2026 15:31:43 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-with-lambda-using-java-25-api-gateway-and-aurora-dsql-part-1-2g27</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article series, we'll explain how to implement a serverless application on AWS using Lambda with the &lt;a href="https://aws.amazon.com/de/blogs/compute/aws-lambda-now-supports-java-25/" rel="noopener noreferrer"&gt;support of the released Java 25 version&lt;/a&gt;. We'll also use API Gateway, relational Serverless database Aurora DSQL, and AWS SAM for the Infrastructure as Code. After it, we'll measure the performance (cold and warm start times) of the Lambda function without any optimizations. Hereafter, we'll introduce various cold start time reduction approaches like Lambda SnapStart with priming techniques and GraalVM Native Image. In this article, we'll introduce our sample application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample applications and their architecture
&lt;/h2&gt;

&lt;p&gt;You can find a code example of our 2 sample applications in my GitHub repositories: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-aurora-dsql" rel="noopener noreferrer"&gt;aws-lambda-java-25-aurora-dsql&lt;/a&gt;. Here we use JDBC with &lt;a href="https://github.com/brettwooldridge/HikariCP" rel="noopener noreferrer"&gt;Hikari connection pool&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&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;. Here we use &lt;a href="https://hibernate.org/" rel="noopener noreferrer"&gt;Hibernate ORM framework&lt;/a&gt; with &lt;a href="https://github.com/brettwooldridge/HikariCP" rel="noopener noreferrer"&gt;Hikari connection pool&lt;/a&gt;. I think that Hibernate JPA is mostly in use together with frameworks like Spring Boot, Quarkus, or Micronaut (this is the topic of my future article series). But I'd like to show you the implications of adding such a framework to Lambda performance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For both applications, we'll use &lt;a href="https://docs.aws.amazon.com/aurora-dsql/latest/userguide/SECTION_program-with-jdbc-connector.html" rel="noopener noreferrer"&gt;Aurora DSQ JDBC connector&lt;/a&gt;, which simplifies dealing with passwords. See my  &lt;a href="https://dev.to/aws-heroes/serverless-applications-with-java-and-aurora-dsql-part-2-using-aurora-dsql-jdbc-connector-eaa"&gt;article&lt;/a&gt; about this topic.&lt;/p&gt;

&lt;p&gt;The architecture of both sample applications is shown below:&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%2Fm7nop76zptxjabbv6e3h.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%2Fm7nop76zptxjabbv6e3h.png" alt=" " width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this application, we will create products and retrieve them by their ID, and use &lt;a href="https://aws.amazon.com/rds/aurora/dsql/" rel="noopener noreferrer"&gt;Amazon Aurora DSQL&lt;/a&gt; as a relational serverless database for the persistence layer. We use &lt;a href="https://aws.amazon.com/api-gateway/?nc1=h_ls" rel="noopener noreferrer"&gt;Amazon API Gateway&lt;/a&gt;, which makes it easy for developers to create, publish, maintain, monitor, and secure APIs. Of course, we rely on &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; to execute code without the need to provision or manage servers. We also use &lt;a href="https://aws.amazon.com/serverless/sam/?nc1=h_ls" rel="noopener noreferrer"&gt;AWS SAM&lt;/a&gt;, which provides a short syntax optimised for defining infrastructure as code (hereafter IaC) for serverless applications. For this article, I assume a basic understanding of the mentioned AWS services, serverless architectures on AWS, and AWS SAM. The application is intentionally fairly simple. The goal is to demonstrate the general development concepts and cover approaches to reduce the cold start time of the Lambda. Please also watch out for another &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;series&lt;/a&gt; where I use No SQL serverless &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; instead of Aurora DSQL to do the same Lambda performance measurements.&lt;/p&gt;

&lt;p&gt;To build and deploy the sample application, we need the following local installations: &lt;a href="https://docs.aws.amazon.com/corretto/latest/corretto-25-ug/downloads-list.html" rel="noopener noreferrer"&gt;Java 25&lt;/a&gt;, &lt;a href="https://maven.apache.org/download.cgi" rel="noopener noreferrer"&gt;Maven&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt;, and &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html" rel="noopener noreferrer"&gt;SAM CLI&lt;/a&gt;. Later, we'll also need &lt;a href="https://www.graalvm.org/" rel="noopener noreferrer"&gt;GraalVM&lt;/a&gt;, including its &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/" rel="noopener noreferrer"&gt;Native Image&lt;/a&gt; capabilities. Using it, we'll build a native image of our application to deploy it on AWS Lambda using the Custom Runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application with JDBC and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;Let's first start with &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-aurora-dsql" rel="noopener noreferrer"&gt;aws-lambda-java-25-aurora-dsql&lt;/a&gt; application, which uses JDBC with Hikari connection pool.&lt;/p&gt;

&lt;p&gt;First, we cover the Infrastructure as Code (IaC) part described 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;AWS SAM template.yaml&lt;/a&gt;. We'll focus only on the parts relevant to the definitions of the Lambda functions there.&lt;/p&gt;

&lt;p&gt;In the global section, we define the common properties valid for all defined Lambda functions. To such properties belong code URI, runtime (in our case Java 25), Snapstart usage yes/no, timeout, memory size, and environment variables:&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;....&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;java25&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="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30&lt;/span&gt; 
    &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;
    &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;x86_64&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;AURORA_DSQL_CLUSTER_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${DSQL}.dsql.${AWS::Region}.on.aws&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is an example of the definition of the Lambda function with the name &lt;em&gt;GetProductByIdJava25WithDSQL&lt;/em&gt;. We define the handler: a Java class and method that will be invoked. We also give this Lambda function access to the Aurora DSQL cluster that we create within this template. At the end, we define the event to invoke this particular Lambda function. As we use a REST application and API Gateway in front, we define the HTTP method &lt;em&gt;get&lt;/em&gt; and the path &lt;em&gt;/products/{id}&lt;/em&gt; for it. This means that the invocation of this Lambda function occurs when an HTTP GET request comes in to retrieve the product by its id.&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;GetProductByIdFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GetProductByIdJava25WithDSQL&lt;/span&gt;
      &lt;span class="na"&gt;AutoPublishAlias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;liveVersion&lt;/span&gt; 
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;software.amazonaws.example.product.handler.GetProductByIdHandler::handleRequest&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt; &lt;span class="c1"&gt;# Policy Document&lt;/span&gt;
          &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
              &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dsql:DbConnectAdmin&lt;/span&gt;
              &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;arn:${AWS::Partition}:dsql:${AWS::Region}:${AWS::AccountId}:cluster/${DSQL}&lt;/span&gt;

      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GetRequestById&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Api&lt;/span&gt;
          &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;RestApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MyApi&lt;/span&gt;
            &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/products/{id}&lt;/span&gt;
            &lt;span class="na"&gt;Method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get&lt;/span&gt;     
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The definition of another Lambda function &lt;em&gt;PostProductJava25WithDSQL&lt;/em&gt; is similar.&lt;/p&gt;

&lt;p&gt;Now let's look at the source code of 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; Lambda function that will be invoked when the Lambda function with the name &lt;em&gt;GetProductByIdJava25WithDSQL&lt;/em&gt; gets invoked. This Lambda function determines the product based on its ID and returns it:&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="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="kt"&gt;var&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="kt"&gt;var&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;if&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;isEmpty&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="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;NOT_FOUND&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="s"&gt;"Product with id = "&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="s"&gt;" not found"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only method &lt;em&gt;handleRequest&lt;/em&gt; receives an object of type APIGatewayProxyRequestEvent as input, as APIGatewayRequest invokes the Lambda function. From this input object, we retrieve the product ID by invoking requestEvent.getPathParameters().get("id") and ask our ProductDao to find the product with this ID in the Aurora DSQL by invoking productDao.getProduct(id). Depending on whether the product exists or not, we wrap the &lt;a href="https://github.com/FasterXML/jackson" rel="noopener noreferrer"&gt;Jackson&lt;/a&gt; serialised response in an object of type APIGatewayProxyResponseEvent and send it back to Amazon API Gateway as a response. The source code of the Lambda function &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/CreateProductHandler.java" rel="noopener noreferrer"&gt;CreateProductHandler&lt;/a&gt;, which we use to create and persist products, looks similar.&lt;/p&gt;

&lt;p&gt;The source code of 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/entity/Product.java" rel="noopener noreferrer"&gt;Product&lt;/a&gt; entity looks very simple:&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="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Product&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="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;price&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;The implementation of 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/dao/ProductDao.java" rel="noopener noreferrer"&gt;ProductDao&lt;/a&gt; persistence layer uses JDBC to write to or read from the Aurora DSQL database. Here is an example of the source code of the getProductById method, which we used in the GetProductByIdHandler Lambda function described above:&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="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="nf"&gt;getProductById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&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="k"&gt;try&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;con&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getConnection&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;pst&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;getProductByIdPreparedStatement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;con&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pst&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeQuery&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
          &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price"&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;product&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;Product&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="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&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="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
          &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&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;Here, we use the plain &lt;a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/" rel="noopener noreferrer"&gt;Java JDBC API&lt;/a&gt; to talk to the database. We use the Hikari connection pool to manage the connection to the database, as creating such a connection is not free. We set up the Hikari pool in 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/dao/DsqlDataSourceConfig.java" rel="noopener noreferrer"&gt;DsqlDataSourceConfig&lt;/a&gt; directly in the static initializer block:&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="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;Here we set the use name, JDBC_URL, which is constructed with the help of through Lambda exposed environment variable AURORA_DSQL_CLUSTER_ENDPOINT. We also set max life time of the pool and the maximum connection size to 1. This is enough, as only one Lambda function is executed within the microVM, and we have a single-threaded application. Aurora DSQL JDBC connector handles the logic to retrieve a short-lived token and set it as a password behind the scenes. Each time we invoke &lt;em&gt;getConnection&lt;/em&gt; method in the ProductDao, the Hikari Datasource is responsible for obtaining the connection:&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;static&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt; &lt;span class="nf"&gt;getPooledConnection&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;SQLException&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;hds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConnection&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;Now we have to build the application with &lt;em&gt;mvn clean package&lt;/em&gt;  and deploy it with &lt;em&gt;sam deploy -g&lt;/em&gt;. We will see our customised Amazon API Gateway URL in the return.  After it, you need to &lt;a href="https://dev.to/aws-heroes/serverless-applications-with-java-and-aurora-dsql-part-3-integrated-query-editor-2o03"&gt;connect to the create Aurora DSQL cluster&lt;/a&gt; and execute these 2 statements to create the table and the sequence:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CREATE TABLE products (id int PRIMARY KEY, name varchar (256) NOT NULL, price int NOT NULL);&lt;br&gt;
CREATE SEQUENCE product_id CACHE 1;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can use it to create products and retrieve them by ID. The interface is secured with the API key. We have to send the following as an HTTP header: "X-API-Key: a6ZbcDefQW12BN56WEDQ25", see MyApiKey definition 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;. To create the product, we can use the following curl query:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -m PUT -d '{"name": "Print 10x13", "price": 0.15 }' -H "X-API-Key: a6ZbcDefQW12BN56WEDQ25" https://{$API_GATEWAY_URL}/prod/products&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our application uses the next value of the sequence with the name product_id to generate the product id. The output of this request contains this product. To query the existing product with ID=1, we can use the following curl query:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEDQ25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Sample application with Hibernate and Hikari connection pool
&lt;/h2&gt;

&lt;p&gt;Let's now look at &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; application, which uses the Hibernate ORM framework with the Hikari connection pool.&lt;/p&gt;

&lt;p&gt;The code of the SAM template and Java handler to execute the Lambda functions looks similar to the first example above. So we won't cover those parts.&lt;/p&gt;

&lt;p&gt;The source code of 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/entity/Product.java" rel="noopener noreferrer"&gt;Product&lt;/a&gt; entity looks like this:&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="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"products"&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;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Serializable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="nd"&gt;@Id&lt;/span&gt;
  &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SEQUENCE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nd"&gt;@SequenceGenerator&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sequenceName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"product_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allocationSize&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="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;price&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;Product&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;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getId&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="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&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;id&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="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 can't use the Java record for Hibernate entities, that's why we have setters and getters for the attributes like id, name, and price. Additionally, we annotate the class with @ Entity and @Table annotations and provide the table name to store the products. We annotate the attribute id with the @ Id, @GeneratedValue, and @SequenceGenerator to define that we use the generated value by the sequence with the name &lt;em&gt;product_id&lt;/em&gt; to set the id.&lt;/p&gt;

&lt;p&gt;Then we implement &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; to create a Hibernate SessionFactory, which we use in the ProductDao later:&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="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="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we set the same Hikari connection pool properties as in the first example. 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;/p&gt;

&lt;p&gt;The implementation of 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; persistence layer uses the Hibernate session factory to open the session, start, and commit the transaction, and also persist the entities and find them by their id:&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="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;createProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openSession&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;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;beginTransaction&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
     &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;persist&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
     &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;commit&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;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&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="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="nf"&gt;getProductById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openSession&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; 
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;find&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="n"&gt;id&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;Similar to the first example, we now have to build the application with &lt;em&gt;mvn clean package&lt;/em&gt;  and deploy it with &lt;em&gt;sam deploy -g&lt;/em&gt;. We will see our customised Amazon API Gateway URL in the return.  After it, you need to &lt;a href="https://dev.to/aws-heroes/serverless-applications-with-java-and-aurora-dsql-part-3-integrated-query-editor-2o03"&gt;connect to the create Aurora DSQL cluster&lt;/a&gt; and execute these 2 statements to create the table and the sequence:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CREATE TABLE products (id int PRIMARY KEY, name varchar (256) NOT NULL, price int NOT NULL);&lt;br&gt;
CREATE SEQUENCE product_id CACHE 1;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We can use it to create products and retrieve them by ID. The interface is secured with the API key. We have to send the following as an HTTP header: "X-API-Key: a6ZbcDefQW12BN56WEHADQ25", see MyApiKey definition in &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;template.yaml&lt;/a&gt;. To create the product, we can use the following curl query:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -m PUT -d '{"name": "Print 10x13", "price": 0.15 }' -H "X-API-Key: a6ZbcDefQW12BN56WEHADQ25" https://{$API_GATEWAY_URL}/prod/products&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our application uses the next value of the sequence with the name product_id to generate the product id. The output of this request contains this product. To query the existing product with ID=1, we can use the following curl query:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEHADQ25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we introduced our sample applications (with and without the usage of the Hibernate ORM framework). In the next 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;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 using Lambda with Java 25, API Gateway and DynamoDB - Part 1 Sample application</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Mon, 16 Mar 2026 15:31:30 +0000</pubDate>
      <link>https://forem.com/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg</link>
      <guid>https://forem.com/aws-heroes/serverless-applications-on-aws-using-lambda-with-java-25-api-gateway-and-dynamodb-part-1-sample-4hdg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article series, we'll explain how to implement a serverless application on AWS using Lambda with the &lt;a href="https://aws.amazon.com/de/blogs/compute/aws-lambda-now-supports-java-25/" rel="noopener noreferrer"&gt;support of the released Java 25 version&lt;/a&gt;. We'll also use API Gateway, DynamoDB, and AWS SAM for the Infrastructure as Code. After it, we'll measure the performance (cold and warm start times) of the Lambda function without any optimizations. Hereafter, we'll introduce various cold start time reduction approaches like Lambda SnapStart with priming techniques and GraalVM Native Image. In this article, we'll introduce our sample application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample application and its architecture
&lt;/h2&gt;

&lt;p&gt;You can find a code example of our sample application in my GitHub &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/tree/main/aws-lambda-java-25-dynamodb" rel="noopener noreferrer"&gt;aws-lambda-java-25-dynamodb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The architecture of our sample application is shown below:&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%2F8mk85zyi2t91t58wimc7.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%2F8mk85zyi2t91t58wimc7.png" alt=" " width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this application, we will create products and retrieve them by their ID and use &lt;a href="https://aws.amazon.com/dynamodb/" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt; as a NoSQL database for the persistence layer. We use &lt;a href="https://aws.amazon.com/api-gateway/?nc1=h_ls" rel="noopener noreferrer"&gt;Amazon API Gateway&lt;/a&gt;, which makes it easy for developers to create, publish, maintain, monitor, and secure APIs. Of course, we rely on &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; to execute code without the need to provision or manage servers. We also use &lt;a href="https://aws.amazon.com/serverless/sam/?nc1=h_ls" rel="noopener noreferrer"&gt;AWS SAM&lt;/a&gt;, which provides a short syntax optimised for defining infrastructure as code (hereafter IaC) for serverless applications. For this article, I assume a basic understanding of the mentioned AWS services, serverless architectures on AWS, and AWS SAM. The application is intentionally fairly simple. The goal is to demonstrate the general development concepts and cover approaches to reduce the cold start time of the Lambda. Please also watch out for another &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;series&lt;/a&gt; where I use 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 &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;/p&gt;

&lt;p&gt;To build and deploy the sample application, we need the following local installations: &lt;a href="https://docs.aws.amazon.com/corretto/latest/corretto-25-ug/downloads-list.html" rel="noopener noreferrer"&gt;Java 25&lt;/a&gt;, &lt;a href="https://maven.apache.org/download.cgi" rel="noopener noreferrer"&gt;Maven&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt;, and &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html" rel="noopener noreferrer"&gt;SAM CLI&lt;/a&gt;. Later, we'll also need &lt;a href="https://www.graalvm.org/" rel="noopener noreferrer"&gt;GraalVM&lt;/a&gt;, including its &lt;a href="https://www.graalvm.org/latest/reference-manual/native-image/" rel="noopener noreferrer"&gt;Native Image&lt;/a&gt; capabilities. Using it, we'll build a native image of our application to deploy it on AWS Lambda using the Custom Runtime.&lt;/p&gt;

&lt;p&gt;Let's start by covering the IaC part described 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;AWS SAM template.yaml&lt;/a&gt;. We'll focus only on the parts relevant to the definitions of the Lambda functions there.&lt;/p&gt;

&lt;p&gt;In the global section, we define the common properties valid for all defined Lambda functions. To such properties belong code URI, runtime (in our case Java 25), Snapstart usage yes/no, timeout, memory size, and environment variables:&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;....&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;java25&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="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30&lt;/span&gt; 
    &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;
    &lt;span class="na"&gt;Architectures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;x86_64&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;REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;${AWS::Region}&lt;/span&gt;
        &lt;span class="na"&gt;PRODUCT_TABLE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ProductsTable&lt;/span&gt;
        &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is an example of the definition of the Lambda function with the name &lt;em&gt;GetProductByIdJava25WithDynamoDB&lt;/em&gt;. We define the handler: a Java class and method that will be invoked. We also give this Lambda function read access to the DynamoDB table with the name &lt;em&gt;ProductsTable&lt;/em&gt;. At the end, we define the event to invoke this particular Lambda function. As we use a REST application and API Gateway in front, we define the HTTP method &lt;em&gt;get&lt;/em&gt; and the path &lt;em&gt;/products/{id}&lt;/em&gt; for it. This means that the invocation of this Lambda function occurs when an HTTP GET request comes in to retrieve the product by its id.&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;GetProductByIdFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GetProductByIdJava25WithDynamoDB&lt;/span&gt;
      &lt;span class="na"&gt;AutoPublishAlias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;liveVersion&lt;/span&gt; 
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;software.amazonaws.example.product.handler.GetProductByIdHandler::handleRequest&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DynamoDBReadPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ProductsTable&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GetRequestById&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Api&lt;/span&gt;
          &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;RestApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;MyApi&lt;/span&gt;
            &lt;span class="na"&gt;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/products/{id}&lt;/span&gt;
            &lt;span class="na"&gt;Method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The definition of another Lambda function &lt;em&gt;PostProductJava25WithDynamoDB&lt;/em&gt; is similar.&lt;/p&gt;

&lt;p&gt;Now let's look at the source code of 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; Lambda function that will be invoked when the Lambda function with the name &lt;em&gt;GetProductByIdJava25WithDynamoDB&lt;/em&gt; gets invoked. This Lambda function determines the product based on its ID and returns it:&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="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="kt"&gt;var&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="kt"&gt;var&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;if&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;isEmpty&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="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;NOT_FOUND&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="s"&gt;"Product with id = "&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="s"&gt;" not found"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only method &lt;em&gt;handleRequest&lt;/em&gt; receives an object of type APIGatewayProxyRequestEvent as input, as APIGatewayRequest invokes the Lambda function. From this input object, we retrieve the product ID by invoking requestEvent.getPathParameters().get("id"). Then we ask our ProductDao to find the product with this ID in the DynamoDB by invoking productDao.getProduct(id). Depending on whether the product exists or not, we wrap the &lt;a href="https://github.com/FasterXML/jackson" rel="noopener noreferrer"&gt;Jackson&lt;/a&gt; serialised response in an object of type APIGatewayProxyResponseEvent and send it back to Amazon API Gateway as a response. The source code of the Lambda function &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/CreateProductHandler.java" rel="noopener noreferrer"&gt;CreateProductHandler&lt;/a&gt;, which we use to create and persist products, looks similar.&lt;/p&gt;

&lt;p&gt;The source code of 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/entity/Product.java" rel="noopener noreferrer"&gt;Product&lt;/a&gt; entity looks very simple:&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="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Product&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="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;price&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;The implementation of 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/dao/ProductDao.java" rel="noopener noreferrer"&gt;ProductDao&lt;/a&gt; persistence layer uses AWS SDK for Java 2.0 to write to or read from the DynamoDB. Here is an example of the source code of the getProductById method, which we used in the GetProductByIdHandler Lambda function described above:&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="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="nf"&gt;getProduct&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="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;GetItemResponse&lt;/span&gt; &lt;span class="n"&gt;getItemResponse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamoDbClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItem&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GetItemRequest&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;key&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;"PK"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AttributeValue&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;s&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="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;tableName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PRODUCT_TABLE_NAME&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getItemResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasItem&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="nc"&gt;Optional&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="nc"&gt;ProductMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;productFromDynamoDB&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getItemResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&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;Here, we use the instance of the DynamoDbClient to build a GetItemRequest to query the DynamoDB table. We get the name of the table from an environment variable (which we will set in the AWS SAM template) by invoking System.getenv("PRODUCT_TABLE_NAME"), for the product based on its ID. If the product is found, we use the custom-written &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/ProductMapper.java" rel="noopener noreferrer"&gt;ProductMapper&lt;/a&gt; to map the DynamoDB item to the attributes of the product entity.&lt;/p&gt;

&lt;p&gt;Now we have to build the application with &lt;em&gt;mvn clean package&lt;/em&gt;  and deploy it with &lt;em&gt;sam deploy -g&lt;/em&gt;. We will see our customised Amazon API Gateway URL in the return. We can use it to create products and retrieve them by ID. The interface is secured with the API key. We have to send the following as an HTTP header: "X-API-Key: a6ZbcDefQW12BN56WEVDDB25", see MyApiKey definition 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;. To create the product with ID=1, we can use the following curl query:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -m PUT -d '{ "id": 1, "name": "Print 10x13", "price": 0.15 }' -H "X-API-Key: a6ZbcDefQW12BN56WEVDDB25" https://{$API_GATEWAY_URL}/prod/products&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For example, to query the existing product with ID=1, we can use the following curl query:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -H "X-API-Key: a6ZbcDefQW12BN56WEVDDB25" https://{$API_GATEWAY_URL}/prod/products/1&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we introduced our sample application. In the next article, we'll measure the performance (cold and warm start times) of the Lambda function without any optimizations.&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>AWS Lambda Managed Instances with Java 25 and AWS SAM – Part 6 Lambda function performance improvement approaches</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Mon, 09 Mar 2026 15:52:47 +0000</pubDate>
      <link>https://forem.com/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-6-lambda-function-performance-113</link>
      <guid>https://forem.com/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-6-lambda-function-performance-113</guid>
      <description>&lt;h2&gt;
  
  
  Introdcution
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-5-lambda-function-initial-performance-35k9"&gt;part 5&lt;/a&gt; of this series, we measured the initial Lambda function performance (the Java code was intentionally not optimized). Still, we saw a quite noticeable cold start when the request to the Lambda function reached the underlying EC2 instance for the first time. We found that a Lambda function with the Lambda Managed Instances compute type can't eliminate the behavior of the programming language, such as a JVM warm-up period. &lt;br&gt;
In this article, we'll optimize our Lambda function to improve the cold start time significantly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lambda function performance improvement approaches
&lt;/h2&gt;

&lt;p&gt;The first thing that we notice during the deployment phase (in case we initially deploy or re-deploy the Lambda function) is that the handler class, for example, for &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-lmi/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;GetProductByIdHandler&lt;/a&gt; for the GetProductByIdHandler Lambda function will be initialized. We can prove it by adding some log statements to the static initializer block of this Java class. With this, we know that the static code will be executed before we even invoke our Lambda function. So our goal is to preload as much as possible in the static initializer block of the Lambda handler class.&lt;/p&gt;

&lt;p&gt;The first thing that we notice is that we instantiate the ObjectMapper (to convert JSON representation of the Product into the &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-lmi/src/main/java/software/amazonaws/example/product/entity/Product.java" rel="noopener noreferrer"&gt;Product&lt;/a&gt; class) for each invocation (within the handleRequest method) of the 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="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="kt"&gt;var&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;DynamoProductDao&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;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="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is suboptimal because we can reuse this object, so let's move it to the static initializer block of the GetProductByIdHandler :&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="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's redeploy our application and send a first request to this Lambda function, for example, via API Gateway GET HTTP request. Let's then look into the CloudWatch metrics of this Lambda function :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:23:36.676Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform.report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3581bb16-29a9-4a5e-9d7d-119700366693"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1419.352&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"spans"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseLatency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:23:35.258Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1416.915&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseDuration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:23:36.675Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.199&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see that the response latency of this Lambda function is 1579 milliseconds. You might observe a different latency, because it also depends on the type of EC2 instance itself. But it decreased by roughly 165 milliseconds compared to the initial version. Why? Generally speaking, when you create an ObjectMapper for the first time, there will be a lot of classes that will be loaded for the first time, and also many singletons that need to be instantiated only once. It takes, depending on the hardware, more than a hundred milliseconds. If you create the second ObjectMapper in the same Java process, it takes 1 millisecond. By moving the ObjectMapper instantiation to the static initializer block of the Lambda function, which will be executed during the deployment phase, we saved a bunch of time, and the first invocation of the Lambda function on &lt;br&gt;
the EC2 instance became quicker.&lt;/p&gt;

&lt;p&gt;Let's move on. We also so that we instatiate &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-lmi/src/main/java/software/amazonaws/example/product/dao/DynamoProductDao.java" rel="noopener noreferrer"&gt;DynamoProductDao&lt;/a&gt; for each invocation (within the handleRequest method) of the Lambda function (see the code above). With that, we also instantiate DynamoDBClient in each Lambda request. This is suboptimal as well, because we can reuse this object, so let's move it to the static initializer block of the GetProductByIdHandler :&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;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;DynamoProductDao&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="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's redeploy our application and send a first request to this Lambda function, for example, via API Gateway GET HTTP request. Let's then look into the CloudWatch metrics of this Lambda function :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:31:40.163Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform.report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ce83c152-c536-42a0-9462-39d1c0aca3e8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;692.023&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"spans"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseLatency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:31:39.472Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;690.665&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseDuration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:31:40.162Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.204&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This optimization is huge: response latency went down to 691 milliseconds. The key is also preloading a bunch of classes and instantiating DynamoDBClient during the deployment phase.&lt;/p&gt;

&lt;p&gt;Ok, this was an easy part. We're not done yet. I wrote a lot of articles about &lt;a href="https://dev.to/vkazulkin/series/34787"&gt;Lambda SnapStart&lt;/a&gt; and the Lambda cold start optimization strategies using priming techniques. The truth is that we don't require implementing the CRaC API for such priming.  The first priming approach that we'll apply now is DynamoDB (request) priming. The idea is to make a call to the DynamoDB table to do the following.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Preload all classes required for such a request and delivering response.&lt;/li&gt;
&lt;li&gt;To instantiate the HTTP Client (the default one is Apache 4.5, but you can set others). An HTTP client is required to talk to the DynamoDB, and its initialization is an expensive one-time operation.&lt;/li&gt;
&lt;li&gt;To use ObjectMapper to convert the JSON result to Product, which will also involve some expensive one-time operations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How can we do it? Let's look at the example:&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;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;DynamoProductDao&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;static&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="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We simply use the static initialization block to get the product by id 0. We are not even considering the result (the product with id 0 might not even exist in the database), but only in the 3 things I mentioned previously. &lt;/p&gt;

&lt;p&gt;Now let's redeploy our application again and send a first request to this Lambda function, for example, via API Gateway GET HTTP request. Let's then look into the CloudWatch metrics of this Lambda function :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:47:26.281Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform.report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6e608d71-aa35-4784-afa4-c945fb6eebf3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;79.102&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"spans"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseLatency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:47:26.204Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;75.445&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseDuration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:47:26.279Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.415&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is another huge win, and the response latency is now down to 75 milliseconds. We had to write only a few lines of code to achieve this. Please find the full code in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-lmi/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithDynamoDBPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithDynamoDBPrimingHandler&lt;/a&gt;, and I provided an extra /productsWithDynamoDBPriming/{id} endpoint in the API Gateway. We can basically stop here, because the latency is nearly acceptable for most use cases. &lt;/p&gt;

&lt;p&gt;But there is a bit of latency that you can still save. I forward you to 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;, how I came to this idea, and what needs to be done for it. There is a lot more code that we need to write, though, to prime more things. Please find the full code in &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-lmi/src/main/java/software/amazonaws/example/product/handler/GetProductByIdWithFullPrimingHandler.java" rel="noopener noreferrer"&gt;GetProductByIdWithFullPrimingHandler&lt;/a&gt;, and I provided an extra /productsWithFullPriming/{id} endpoint in the API Gateway :&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="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;DynamoProductDao&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;static&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;GetProductByIdWithFullPrimingHandler&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="na"&gt;getClassLoader&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="n"&gt;handleRequestInternal&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="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;APIGatewayProxyResponseEvent&lt;/span&gt;  &lt;span class="nf"&gt;handleRequestInternal&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="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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;handleRequestInternal&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="n"&gt;context&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 gave the explanation in my article mentioned above. Now let's redeploy our application from the last time and send a first request to this Lambda function, for example, via API Gateway GET HTTP request. Let's then look into the CloudWatch metrics of this Lambda function :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:51:33.162Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform.report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"78aa223d-3e0d-4fd1-aa1a-b59f809d2b04"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;62.126&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"spans"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseLatency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:51:33.102Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;60.214&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseDuration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-25T16:51:33.162Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.191&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We saved another 20 milliseconds of the response latency, which is now only 60 milliseconds. There is still some internal work to be done by the Lambda client deployed on EC2 before the handleRequest method is invoked, and we can't optimize it. I'd stop by doing DynamoDB (request priming), as I'm pretty happy to have the responseLatency below 100 milliseconds.&lt;/p&gt;

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

&lt;p&gt;In this article, we optimized our Lambda function in multiple steps to decrease the cold start time (response latency of the first request) significantly.  &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>lambda</category>
    </item>
    <item>
      <title>AWS Lambda Managed Instances with Java 25 and AWS SAM – Part 5 Lambda function initial performance measurements</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Thu, 05 Mar 2026 15:34:54 +0000</pubDate>
      <link>https://forem.com/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-5-lambda-function-initial-performance-35k9</link>
      <guid>https://forem.com/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-5-lambda-function-initial-performance-35k9</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-1-introduction-and-sample-application-1eb7"&gt;part 1&lt;/a&gt; of the series, we explained the ideas behind AWS Lambda Managed Instances and introduced our sample application. In &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-2-create-capacity-provider-4cgo"&gt;part 2&lt;/a&gt;, we explained what a Lambda Capacity Provider is and how to create it using AWS SAM. &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-3-create-lambda-function-with-lmi-1d9g"&gt;Part 3&lt;/a&gt; was about how to create Lambda functions and attach them to a capacity provider. In &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-4-monitoring-unsupported-features-9l6"&gt;part 4&lt;/a&gt;, we talked about monitoring, currently unsupported features, current challenges, and pricing of LMI. In this article, we'll measure the initial Lambda function performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lambda function initial performance measurements
&lt;/h2&gt;

&lt;p&gt;Let's first start by measuring the performance of the GetProductById function, whose implementation we can find &lt;a href="https://github.com/Vadym79/aws-lambda-java-25/blob/main/aws-lambda-java-25-lmi/src/main/java/software/amazonaws/example/product/handler/GetProductByIdHandler.java" rel="noopener noreferrer"&gt;here&lt;/a&gt;. We first implemented this Lambda function in a not very optimal way. More about its optimization potential in the next article. Let's send a first request to this Lambda function, for example, via API Gateway GET HTTP request to products/1 (assuming we've already created a product with id 1). Let's then look into the CloudWatch metrics of this Lambda function. We find something similar to :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-24T06:54:10.882Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform.report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3c16579f-33eb-42b6-aae0-f204872d7ebd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1581.56&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"spans"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseLatency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-24T06:54:09.302Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1579.247&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseDuration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-24T06:54:10.881Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.222&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see the &lt;em&gt;responseLatency&lt;/em&gt; of this Lambda function is 1579 milliseconds. You might observe a different latency, because it also depends on the type EC2 instance itself. In &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-2-create-capacity-provider-4cgo"&gt;part 2&lt;/a&gt;, we defined the following allowed EC2 types within the Capacity Provider: m7a.large, m6a.large. Let's send the same request once again and look into the CloudWatch metrics of this Lambda function. We likely (in case we hit the same EC2 instance behind) find something similar to :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-24T06:57:03.768Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform.report"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"371879fb-588f-4c7f-b80a-8586abaf547d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"metrics"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;41.937&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"spans"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseLatency"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-24T06:57:03.727Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.681&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"responseDuration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-02-24T06:57:03.768Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"durationMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.461&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see the &lt;em&gt;responseLatency&lt;/em&gt; of this Lambda function being 41 milliseconds only. What's going on here? The article &lt;a href="https://aws.amazon.com/de/blogs/aws/introducing-aws-lambda-managed-instances-serverless-simplicity-with-ec2-flexibility/" rel="noopener noreferrer"&gt;Introducing AWS Lambda Managed Instances: Serverless simplicity with EC2 flexibility&lt;/a&gt; clearly states: Lambda automatically routes requests to &lt;strong&gt;preprovisioned&lt;/strong&gt; execution environments on the instances, &lt;strong&gt;eliminating cold starts that can affect first-request latency&lt;/strong&gt;. Do we still have cold starts? They are much lower than &lt;a href="https://dev.to/aws-builders/measuring-lambda-cold-starts-with-aws-snapstart-part-9-measuring-with-java-21-5e52"&gt;measured (with Java 21) for the default Lambda&lt;/a&gt;, which were more than 3 seconds, but still.&lt;/p&gt;

&lt;p&gt;The truth is that if we look into the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html#runtimes-lifecycle" rel="noopener noreferrer"&gt;Lambda execution environment lifecycle&lt;/a&gt;, the usage of the Lambda Managed Instances compute type with the &lt;strong&gt;preprovisioned&lt;/strong&gt; execution environments on the instances eliminates the following initializations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda extension (if you have it)&lt;/li&gt;
&lt;li&gt;Execution runtime (in our case, JVM startup with Java 25 version)&lt;/li&gt;
&lt;li&gt;Lambda Function initialization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What LMI can't eliminate is the behavior of the particular programming language, like a JVM warm-up period. This warm-up period can usually be attributed to lazy class loading and just-in-time compilation, which optimize the JVM for subsequent requests that execute identical code. There are a lot of articles about &lt;a href="https://www.baeldung.com/java-jvm-warmup" rel="noopener noreferrer"&gt;How to Warm Up the JVM&lt;br&gt;
&lt;/a&gt; or talks like &lt;a href="https://www.youtube.com/watch?v=6WBHZPALwVA" rel="noopener noreferrer"&gt;Keeping Your Java Hot by Solving the JVM Warmup Problem&lt;/a&gt; in general, but please keep the following in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Corretto doesn't support [CRaC].(&lt;a href="https://openjdk.org/projects/crac/" rel="noopener noreferrer"&gt;https://openjdk.org/projects/crac/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;LMI doesn't support [SnapStart].(&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;We don't have access to the underlying EC2 instance to experiment with &lt;a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html" rel="noopener noreferrer"&gt;CDS&lt;/a&gt; and &lt;a href="https://openjdk.org/projects/leyden/" rel="noopener noreferrer"&gt;AOT cache&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're already happy with this cold start, which only occurs when the Lambda function first reaches a particular EC2 instance (due to your autoscaling policy, new EC2 instances may be started), you don't need to do anything. Otherwise, in the next article, we'll optimize our Lambda function to improve this cold start time.&lt;/p&gt;

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

&lt;p&gt;In this article, we measured the initial Lambda function performance (the Java code was intentionally not optimized) and still saw a quite noticeable cold start when the request to the Lambda function reached the underlying EC2 instance for the first time. We found out that a Lambda function with the Lambda Managed Instances compute type can't eliminate the behavior of the particular programming language, like a JVM warm-up period. In the next article, we'll optimize our Lambda function to improve this cold start time significantly.&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>lambda</category>
    </item>
    <item>
      <title>AWS Lambda Managed Instances with Java 25 and AWS SAM – Part 4 Monitoring, unsupported features, challenges and pricing</title>
      <dc:creator>Vadym Kazulkin</dc:creator>
      <pubDate>Mon, 02 Mar 2026 15:21:44 +0000</pubDate>
      <link>https://forem.com/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-4-monitoring-unsupported-features-9l6</link>
      <guid>https://forem.com/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-4-monitoring-unsupported-features-9l6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-1-introduction-and-sample-application-1eb7"&gt;part 1&lt;/a&gt; of the series, we explained the ideas behind AWS Lambda Managed Instances and introduced our sample application. In &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-2-create-capacity-provider-4cgo"&gt;part 2&lt;/a&gt;, we explained what a Lambda Capacity Provider is and how to create it using AWS SAM. &lt;a href="https://dev.to/aws-heroes/aws-lambda-managed-instances-with-java-25-and-aws-sam-part-3-create-lambda-function-with-lmi-1d9g"&gt;Part 3&lt;/a&gt; was about how to create Lambda functions and attach them to a capacity provider. In this article, we’ll cover the following topics: monitoring, currently unsupported features, current challenges, and pricing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capacity Provider Monitoring
&lt;/h2&gt;

&lt;p&gt;Capacity Provider provides various metrics. To see them, you can select the Capacity Provider by name and go to the “Monitoring” tab, or select the metrics from CloudWatch:&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%2F6a5fy78zierltd6ofurt.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%2F6a5fy78zierltd6ofurt.png" alt=" " width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following metrics are provided:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Capacity provider CPU utilization&lt;/li&gt;
&lt;li&gt;Capacity provider memory utilization&lt;/li&gt;
&lt;li&gt;Capacity provider allocated utilization&lt;/li&gt;
&lt;li&gt;Execution environment CPU utilization per function&lt;/li&gt;
&lt;li&gt;Execution environment memory utilization per function&lt;/li&gt;
&lt;li&gt;Execution environment count per function&lt;/li&gt;
&lt;li&gt;Capacity provider instance counts&lt;/li&gt;
&lt;/ul&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%2Fh6s1vw8rlga40pnsih93.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%2Fh6s1vw8rlga40pnsih93.png" alt=" " width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Unsupported Features
&lt;/h2&gt;

&lt;p&gt;These Lambda features, which we know from the default compute type, are currently unsupported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SnapStart&lt;/li&gt;
&lt;li&gt;Provisioned concurrency&lt;/li&gt;
&lt;li&gt;Reserved concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They all made sense when used with Firecracker microVM-based execution environment, and don’t make sense with (pre-provisioned) EC2-based LMIs, which don’t suffer from the cold starts.&lt;/p&gt;

&lt;p&gt;Currently, not all instance types are supported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    No GPU support&lt;/li&gt;
&lt;li&gt;    No t-family (t2, t3, t4g) support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a list of supported instance types, go to the &lt;a href="https://aws.amazon.com/de/lambda/pricing/#:~:text=EPU%20pricing%20applies.-,Management%20Fees,-Pricing%20Example%3A%20High" rel="noopener noreferrer"&gt;AWS Lambda Pricing page&lt;/a&gt; and select our AWS Region.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current challenges
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  It’s not possible to switch between Lambda Default and Lambda Managed Instances, also due to different concurrency models. I personally would find it better if there were such an easy switch possibility between both compute types, because at the beginning and at the low scale, Lambda default compute time is nearly always a preferable and cheaper choice (even with the purchased saving plans or reserved instance). The switch to LMI compute type makes sense in the future, at some point in time, and only for certain workloads (for example, for those which have reached high-traffic, steady-state workloads)&lt;/li&gt;
&lt;li&gt;    It’s not possible to cost-effectively set up Lambda with LMI compute type for staging and test environments with low consumption or for test purposes only. The possible workaround is to set min and max execution environments to 0 when starting to invoke a Lambda function, reset those values (for example, to min=1 and max=2), after end using it, set min and max execution environments back to 0. This requirement changes in IaC and re-deploying the stack multiple times, which takes time and impedes the user experience. It would be better if the min execution environment value could be set to 0, independent of the max value, and scale the execution environment down to 0 is possible (with explicit choice in the IaC) in case there was no usage for a long (configurable) period of time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lambda Managed Instance pricing
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/de/lambda/pricing/" rel="noopener noreferrer"&gt;Lambda Managed Instances pricing&lt;/a&gt; has three components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;    Request charges: $0.20 per million requests&lt;/li&gt;
&lt;li&gt;    Compute management fee: 15% premium on the EC2 on-demand instance price for the instances provisioned and managed by Lambda (Premium for each instance type provided below)&lt;/li&gt;
&lt;li&gt;    EC2 instance charges: Standard EC2 instance pricing applies for the instances provisioned in your capacity provider. You can reduce costs by using Compute Savings Plans, Reserved Instances, or other EC2 pricing options&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that Lambda Managed Instances functions will not pay separately for the execution duration of each request, unlike Lambda (default) compute type functions.&lt;/p&gt;

&lt;p&gt;Event Source Mappings: For workloads using provisioned Event Poller Units (EPUs) with event sources like Kafka or SQS, standard EPU pricing applies.&lt;/p&gt;

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

&lt;p&gt;In this article, we covered the following topics: monitoring, currently unsupported features, current challenges, and pricing.&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>lambda</category>
    </item>
  </channel>
</rss>
