JCON EUROPE

Serverless Java Web Applications on AWS Lambda with Micronaut Quarkus & Spring Boot | Vadym Kazulkin

49:27 · 20 Apr 2026 – 23 Apr 2026 · YouTube

About this talk

This talk covers the implementation of serverless Java web applications using AWS Lambda alongside frameworks such as Spring Boot, Micronaut, and Quarkus. The speaker, Vadim, explores the optimization of Java applications running in a serverless architecture, addressing common challenges like cold starts and performance issues. He illustrates the process of building a REST application that utilizes AWS services like API Gateway and DynamoDB. The session includes technical discussions on framework differences, dependency injection, and the impact of these frameworks on latency. Vadim introduces concepts like AWS SnapStart to improve cold start performance and compares the benefits of using GraalVM native images for faster execution.

Full transcript

Yeah. Hi everyone. Thanks for being here for my talk serverless Java web applications on AWS Lambda with different frameworks like Spring Boot, Micronut, and Quirus. Yeah, I know that's kind of a bit niche topic uh because it requires like dealing with AWS Lambda serverless which is not for every organization. It's not the basic topic about serverless and how to write lambda functions. That's why I would like

to know who is already using lambda function API gateway something like this. Uh okay only several people because there might be knowledge gaps. Um but maybe we are looking for just to see how that what are the challenges how that might be optimized. Yeah, my name is Vadim and Java and serverless is uh are two my main passions. uh and that's why I just investigate how to

um to run Java on on AWS Lambda as effective as possible I would say and just how you can optimize it and where where are the constraints where you cannot go probably any further okay maybe the current state is um you you have profound Java skills and and skills in one of those frameworks and you have an existing REST application in AWS Yes will be better because

we are dealing with that and or you would like to develop a new one and you would like to use AWS Lambda it has its advantages um because it's just ser or less offers you certain things like manage services also scalability at least certain point and you focus more more on on your code and less on operational stuff but it's for sure not for every organization and

uh I totally understand that the people go to contain containers and so on. So there are there is no no one choice for that and basically what we will be doing there is we will look at very simple application just to see what how so what are the the latency numbers and how to deal with that. So there will be an application with managed API gateway in

front of us and we will basically create the product and get the product by ID and we will store it in NoSQL Dynamob database that will remain serverless. I will share some words about if you are using other databases in the end because there are differences but basic principles will remain the same. So basically if you have this API gateway it works like if you send requests

and API gateway sends more or less huge JSON requests to AP to lambda. Yeah, there is certain type of mapping. You will get all the path parameters, query parameters, the whole body if you use the the put or post request. And basically this is how normal lambda function without any usage of the frameworks or framework looks like. You see here we implement request handler and we have

here input and output basically because we are using API gateway. There is a a request and response and this handle request method and this is the example of get product by ID. This basically how it looks like. You have this request event. You can get the parameters. In that case there this is um the the ID of the product and then you have some some persistence layer

in this case is Dynamob. You ask for that and then you you reply with the response. This is kind of how you would like you would write the lambda function in Java without any framework. So what I often hear and I presented a lot of talks and you can find them on YouTube how you can optimize this application without any usage of any frameworks and it was

more or less yeah you can achieve more or less acceptable performance by using the pure Java but not many companies using only pure Java that's always some framework uh there um and especially if you use spring boot uh the other the things are happening on the on the runtime the performance might not very well. Yeah, because of this runtime annotation processing and so on. Even if Quarkos

and Micron not doing it differently, it's still framework on top or frameworks on top. And it's that was kind of my motivation more or less to to investigate what we can do about that. So that if somebody kind of tries it out uh then there is awareness how far you can go with that. Uh so basically you can use lambda function do what I showed you and

simply inject auto dependencies like here product dow this is repository you can inject it you can inject object mapper for for dealing with Jackson there is nothing wrong with that yeah but it's not the huge benefit what I wanted to show you is more focus on portable solutions portable of course not completely portable but in the sense that you can try the things out and switch maybe

to other AWS offerings classical um instances or container based applications with as less effort as possible. It's not without effort because you need to adjust dependency you need to write a different infrastructure as a code part because lambda is lambda. If you use containers, you need infrastructure code for containers. And yes, we because of Dynamob, we are kind of locked, but I will show to into AWS,

but I will show you later at least the links to my articles where I describe how you can use posgress based databases and basically any database with the same stuff. So that will be the focus more how just that if you try something out and you see it doesn't work, you can go back to more familiar approaches. This will be the agenda for today. So we will

this simple application we will develop that uh using all these three three frameworks um and then we will measure the the cold starts. I will explain what is that uh and and performance in general. You will see uh how it looks like and we will be talking about how to optimize it basically. Yeah. And how far we can optimize it with snap start and and grow native

image. So let's start. I currently use spring boot 3.4. You can use spring boot 4. There will be even no change, no big change required. I used Java 21 to because I need to update the frameworks. But the first test with Java 25 showed me that the numbers are basically the same with certain uh things that I will point out but uh basically you can use that.

So in order to write the portable application spring boot application that you can execute on AWS Lambda there are different ways but there is an project on AWS site which is called um serverless uh Java serverless container the AWS serverless Java container and they offer the containers for different things for example Strats. Does somebody use Strats? Okay, I'm probably the oldest guy here, but anyway, you can

use spring, spring boot 2, three, even even four for this. Yeah. So, you can simply select the container for spring boot three or four and and and do the stuff and all the things that will be see they all look the same. Basically, there is a mapper between lambda function and and and the paradigm of of the framework and we will be using controllers. Yeah. So the

lambda function will needs to be mapped to the controller method and in this case the serverless Java container for spring boot will do that for you. Um that's more or less it how you can write it normal spring boot application. First of all this main class then you have this Dynamob persistence layer. If you're not familiar with that simply glor this there there is AWS SDK how

to store and retrieve things from Dynamob. Yeah, as I use it, you you need to use it. You can annotate it with repository if you would like or you can use uh posgress stuff here uh or whatever you would like. And then we will use the controller. You see here product controller basically with two methods um get product by ID and create and product is a record.

So I don't use actively lambda function. I would like to reuse my controller here and you see I annotated with the rest controller. So how I can do this or how the mapping first of all works. So there is an infrastructure as a code part as always and you see here on the left side uh if we define lambda function and we need to define lambda function

uh in infrastructure as code there is directly visible mapping here you see this is event this is what infrastructure create the product basically you see this is the path/ product the method is put you can use post that's in this case the same and the controller has basically the same request mapping yeah this is this the same path here products for for get product by ID products

ID you have here the the HTTP method and that's basically it so with that uh spring container can do that mapping they look into your infrastructure as a code and then you they know more or less okay we can map that stuff to the controller but who is mapping that so there is lambda requires lambda function we don't write lambda function so first of all we need

to import this AWS or as Java container spring boot yeah you can port here the fourth version it it's already released and then we need to define the lambda function this is this spring delegating lambda container handler spring uh names you know they are quite long this lambda functions come from this uh dependency this is by default there and this lambda function you simply need to define

it will take care of this mapping for you they will read the infrastructure as a code part they will see what are what controllers are there you can map them in the main class or they will be simply discovered by annotation and they will do this magic and mapping on your site. You don't need uh don't need to define this lambda function on your own. You can

do this and we will see why. Sometimes you need to override things but basically then you can define lambda function implement request stream handler define this big object with spring boot lambda container handler and basically implement handle request and proxy the request. This is what this lambda function that that comes from the package will do for you. But basically in if you can optimize and you would

like to optimize you need to write something and then you need to declare exactly this lambda function as a lambda function which deals as a proxy basically it's all like adapter logic they all we will see the others they they work the same basically. So the there is no lambda function or not too much logic there. We're using basically the whole spring application and it it works.

You don't need to do to do anything else. Basically, you can deploy it on lambda invoke via API gateway and it works. I will show you the metrics later. Let's cover the the quirkus part and the quirus part is basically similar the same repository there. There are only specific quirkus specific annotation here application scope for example. Uh and the controller looks the same. Once again this this

this all are then quirkus or jakarta e specific annotation you have then the record you can register that for reflection because we more or less uh pass uh that as a JSON back and forth but basically looks exactly the same. The mapping is completely the same. The infrastructure code is the same and the mapping will be resolved the same. Uh what are the differences? The differences are

of course uh we need to declare other dependencies and in case of Quarkus there is Quarkus rest Jackson we need that uh for this reflection and there is this mapping or this dependency which provides the proxy Quarkus Amazon lambda rest it comes directly from Quarkus they they have the stuff they have this for Azure they have this for Google cloud so basically you can reuse your controller

and by defining that dependency you can say okay this is now Asia function Google function and all the stuff. Of course, infrastructure code will be different uh depending on the on the cloud provider and basically you do the same. There is a generic um lambda function coming from this dependency with the name Quarkus stream handler and you define it and this stream handler will map basically your

request. Yeah, because that's the lambda function. Yeah. Will map it to this Quarkus controllers and that's that's all you need to do basically. Yeah, you see that they're completely similar approaches completely the same will be micronode. Yeah, this is the repository singleton annotation coming from micronote. Micronode uses their own annotations. They don't like this Jakarta ones but they are named the same. The same situation controller uh

record. The difference is here we need this sa annotations micronode comes with a very thin um converters marshala from JSON to objects and they require to use it. So you need this micron set the Jackson dependencies. It's really thinner than than Jackson and all others and interestingly doesn't work without that. And the same situation there is a mapping micronode AWS lambda events s and this dependency more

or less says okay this is more or less the adept adapter between the controller and AWS lambda there are the same also there are others like for uh for Azure and for Google cloud. So basically it also allows us to reuse the controller. The Controller looks the same. Infrastructure is code is is framework independent anyway. And we need to include another dependency micronode functions AWS API proxy.

A bit strange a name but this this dependency bring gives us that uh lambda function which will deal as will act as a proxy on this. this comes with a dependency with the API gateway proxy request event and this exactly this lambda function will route that to the the correct um controller method. So basically you see we inject some adapter coming from uh from the framework and

more or less can reuse uh the controller side. Now that's more or less the easier part. Yeah, you just can look into the documentation. The more interesting part probably why why people are skeptical of using Java in in the serverless world are so-called cold starts and I will explain because uh lambda is a function as a service it's different from container as a service platform as a

service basically one execution environment of lambda and this is the microvm it's only executing one request at a time there is no parallelism in function as a as a service so in order to execute the second request there should be another microVM for that coming and this is exactly where the problem is because there are costs to start this microVM it's not only microvm the code of

your lambda function needs to be downloaded GVM needs to be started because you are using manage Java AWS provides also for all other nearly all other programming languages and that has a cost and in case you're using something like spring boot all this annotation process resolving that needs to happen so basically you have the situation If you deploy the lambda function or change the code, there will

be no environments because all all that will be gone and then there is kind of the cost associated with with starting those environments. And if you have for example five there and you have 10 requests in parallel then another five needs to be started by AWS basically. And um yeah this is something then that you that that that that leads to this situation that there are cold

starts and um what exactly happens in this cold start you see the code will be downloaded the new execution environment will be started. This is the more or less microvm. Yeah. And then the class loading kicks in. GVM will be started. Class class loading kicks in. Every class that is reachable Yeah. will be loaded. Static initializer block will be in executed of this reachable classes. Transitive runtime

dependency injection especially if you're using spring just in time compilation and so on. And this all happens before this lambda function itself will be invoked. And all these three steps before they are kind of cold starts in the in that sense. And if the container is there and it's not more or less gone then if if there is a warm container then then the next request only

execute the lambda function. All those steps are only needed more or less during the cold start and then there is a need for a new environment. So now let's measure a bit uh the performance without any optimizations. So once again I used Java 21 with 25. You will have similar results. I will give lambda function 1 1 GB of memory. This is more or less something that

you can start with. You can give a half or a bit more. uh um DynamoB if you're talking to Dynamob you talk via HTTP client and the default one we start with the default one is is Apache and there are certain defaults and I simply did the last test for stress test for one hour just putting a bit of stress uh like too many requests sometimes even

more than thousand in parallel and what I experienced was within this hour 100,000 warm starts uh and only 100 call starts. So you see the call starts are not very frequent but it's only in this pattern how I did the stress test. So the situation is it highly depends on your application. If you have lambda function for example for the register user and the register user the

new user comes every five six minutes then you will probably experience every time the call starts because the environment is gone after 5 minutes AWS will duplicate throw it away for the cost reason. Yeah. If you have steady traffic then you will experience the call starts in less far less than 1% of the cases. If you have unsteady uh pattern for certain um functions then you might

experience this even each time. Yeah. And sometimes you have the situation that one micros service invokes another and then it's another lambda function and maybe that in this chain several lambda functions will have a call starts and then the the total latency is a sum of that. Yeah. So it's highly I have here very simple case with only lambda one lambda function but your case might be

much more complicated just simply to demonstrate. So now let's measure this cold start and if you see that that means we will measure the cold start. So if we need a new environment which might be very rare but then you the impact did somebody visit the talk at 9:00 about the trading platform So they their goal was to to do something in one millisecond. Yeah. Yeah. I

cannot present such a results as you see it's the cold start and you see exactly this one is spring boot container. This is the the quus. This is the micronote. You see the cold start. I I usually look into this yellow one. This is P90. In 90% of the cases you have that one. You see 7 seconds, 5 seconds, 6 seconds. Quirkus is more or less the

best but still 6 seconds. Probably nobody would like to have that in production. Yeah, it's really affordable without optimization. Even if it's raw, 6 seconds, you can lose the customers. Yeah. And it's only the cold start. And this is by the way the measurements if I have the lambda function without the usage of any framework, the poor lambda functions as I show you in the beginning still

more than 3 seconds. So this is how it is. It cost to start JVM. It cost to to load the classes for the first time. It's optimized for long living applications but the but what you see that the adding frameworks on top adds on latency because they are doing the stuff this annotation processing and that it's the bigger artifact more classes to be loaded because of that

that that has a cost the worm starts which happens frequently there Java is a very quick programming language I simply don't want to show you as you see 20 milliseconds this is the container is there you create the product or get product by ID. I basically measured get product by ID stuff. So this more or less you see here 30 milliseconds that's negligible. Sometimes you will see

the maximum value being bigger and the maximum value I will explain later why is that but it's only the maximum value from 100,000 invocation. It might might be bigger because Java has a lazy loading and even if you're executing something the the the handler of the lambda function for the first time there may be classes that needs to be loaded for the first time because in the

static initializer block you did not reach everything because your your business logic required uh doing the stuff and maybe the frameworks need to do the stuff but it's the the maximum value only even p9.9 is 50 milliseconds. So it's it's it's warm starts are not the problem. The cold starts are okay. Now let's start optimization because probably if we will leave the values as they are we

can go home everybody and and you will probably using that only for certain asynchronous operations where the latency is is not an issue at all. Yeah. But uh many applications are synchronous and that's that's why we need to do something. So let's start with AWS own offering snap start and what it does the goal is to to improve the startup performance and startup performance is the synonym

for the cold start more or less the startup performance. Yeah it's available only for lambda so it's not available on EC2 containers and all other stuff. So you can only use it for lambda and it's fully managed. I will uh explain what it is a bit later. It's currently available for Java runtimes nearly for all starting for 11. It's and snapstart was first available for Java and

then it was added for Python and net and of course yeah you cannot use it on docker and if you will be showing later also gravium native image it's also not more or less how it works is you enable snap start on the lambda function and you decide on which lambda function you would like to have it generally you can enable it on all all of them

it's more or less here configuration on the infrastructure structure as a code two liner like snap start true that's basically it and you will have it but now what does it mean basically how it works if you enable lambda function snap start for the lambda function then there will be differences if you deploy first your lambda function in normal case if you don't have snapstart you deploy

it and that's it and then you invoke it but if you deploy the lambda function with after the deployment they will run the static initializer block of the lambda function. So they will start the uh microVM for that with the Java start the GVM and go through all the steps required for the cold start. They cannot invoke your lambda function because they need the payload for this

like product ID one or two they cannot do this but they can do the the preload the static initializer things. So everything is which is a part of the call start they can do and what they do with that they more or less create the snapshot of microVM after kind of the cold start phase is done and with that there are classes preloaded and all that stuff

so they if you know what is crack creo if you heard this it's similar it's not the same technology yeah because crack is the snapshot on the process level here is this the snapshot on the whole microVM level including the um operating system. So they snapshot everything. Yeah. And GVM is only part of it. Yeah. And all this say that this is the whole state. They store

it and in the invocation phase if you have enabled snap start they will restore the snapshot for you and then the function will be invoked. Logically the um uh kind of the the the the hope here is that the res the the restore of the snapshot is quicker than the call start because otherwise doesn't make any sense but you can measure and decide yeah that's the hope

that the restore phase will be quicker and we will measure and see that uh so basically you can enable snap start and and do the measurements I will show you to them but it's not the whole story and I will show you how you can optimize that even with the snapart there is so-called priming and as you saw the snaps the the snapshot will be created and

it generally general intention is to put as much as possible into the snapshot as many classes loaded as possible because it's then will be quicker because they are kind of there and um you can do certain stuff um additionally preload this uh the things um but but priming require requires kind of a bit of knowledge of your application. What can be done? For example, if we are

using Dynamob once again, we are more or less doing two things. We establish HTTP connection and we are marshalling and marshalling object. You talk to Dynamob is a JSON. So we need product to be serialized into the JSON impration Java class to JSON and back depending what is it you are reading the the object or you are storing the object. And there are things that are known.

For example, if you initialize Apache client, it uses a lot of caching. The first time initialization of this client is expensive. Yeah. The same is if you initialize and in in in serialize the things for the first time. You can test on your PC. The performance of new creation of new object mapper. This is Jackson. Yeah. The first invocation if you do new object mapper will be

depending on your hardware between 300 and 500 milliseconds. If you do the second call new object mapper you will be below 1 milliseconds because this is the first invocation it uses so many singletons and so many things that they are caching for the lifetime of GVM that it's really expensive and those things can be preloaded into the snapshot. Yeah, just that they are already there. The question

is how? And here comes this optional hooks that we can implement. Yeah, lambda uses this this this this is called crack API and with that we can optionally define hook and and do this stuff and let's do the first thing we can prime dynamob request and the same you can do priming of basically um posgress request. So what's happening here? If we define here Dynamob client, first

of all the default is always HTTP client Apach HTTP client. We can overwrite it. We can here say override HTTP client and do something else. But the the principle is the same here. If we do get item from Dynamob, yeah, then the item will come as a JSON and we will need to convert it to the product uh Java object. And this is where this JSON kicks

in. So the thing is can we kind of preload the stuff and fake this call to the Dynamob and yes it's possible with that basically with that uh uh crack API. What is crack API? You more or less put one dependency to to to the crack uh into into your dependency management and then you for the lambda function that you do it you implement the resources interface.

This is the example for spring boot. All others are the same. You implement the resource interface in the constructor. You declare this resource as a crack resource. Basically, you copy this line core get global context register this. You register this class or this object as a cracker. And then the the most important method is before checkpoint. So the before checkpoint is the possibility to do as much

as possible so that becomes a part of the snapshot. And what we do here, we fake the execution the invocation to the um to the Dynamob um table. We simply fake get product by ID zero. I'm not even interesting the result. For me, it's interesting to preform HTTP client to preform the JAXA object and that's it. Yeah, they will be there and it will be part of

the snapshot. Yeah, and this before checkpoint will run before the snapshot is taken. There is another method after restore. For example, if you have uh D if you have relational database, you can restore connection here because it will be gone in the snapshot. Yeah, it will not survive the hours in the snapshot. Yeah, but here is HTTP and then the connection magically will be restored for us.

But this is basically the one of the primings if you know you simply put this is one liner. It's a bit of code but you can uh more or less prime. This is the same is for here for quarkus. We implement here resource do the same get product by ID zero uh works and I will show you what does it bring uh later there is more complicated

priming and there always not not sure because it requires a lot of code but additional code but I can show you um once again the thing is here that um we what we can prime is we only prime the first priming only show Dynamo DB that we talk to Dynamob DB. Uh what we can also prime more or less um the whole mapping between the framework and

the lambda and the framework because if we we have all those adapters the first of all lambda function even this this default one will be kicked in. It needs to to find the mapping between controller and uh and its infrastructure as a code staff and then more or less it will be cached and and invoked and this is additional step and this is can all be also

primed. For example, I show you how how with additional code I can prime this for for spring boot. Um in the before checkpoint I more or less fake this request to this get product by ID. There are additional APIs for that. Here you see AWS proxy request. I said HTTP method get. This is the path. This is additional lambda function for this. And I more or less

say the parameters ID is zero. And then um say proxy and and say send this request as a proxy. Yeah. And with that uh if I send this request this default lambda function will be kicked in. It will load the mapping. it will store the mapping and call the controller method. And this this is kind of advanced priming because it primes lambda function logic more or less

as uh with a with a framework. it I know that might be difficult to understand but uh it's just additional stuff the same there are APIs for caucus they are doing the same um but sometimes differently here the input stream is required so I basically take this request serialize it to JSON and put it into bite array more or less I fake the invocation of the lambda

function and then it will prime the whole life cycle and micronode more or less the same now let's uh more or less look into the results of that what what does it bring? So this on the left side um so then the red ones this is what I showed you without any optimization. The green one is if you only activate snap start without any priming more or

less check. Yeah. The the yellow one is this Dynamo DB priming where we fake get product by ID call and this I would say uh yeah u cherry or whatever the color is. This is advanced priming where I prime the whole invocation and you see alone by activating snap start you gain a lot but it's not we are not quite in the good results you see here

this is P90 I only show you 90 because if I will show you all percentiles then they will be overloaded but what you see here especially if you start to apply priming you will be below 2 seconds yeah we are like here seven here five you go below 2 seconds and if you use this advanced priming you will be somewhere a bit more than 1 second. Yeah.

Uh but with additional code which is a bit complicated. Yeah. Sometimes people will stay maybe on this uh Dynamob priming because it's really oneliner. Yeah. You fake the execution of of of more or less uh the the call against the database and it's really easy. Yeah. So you see you can go up to yeah I would say one and a half seconds with that frameworks but you

cannot go below that might be okay because the difference between five six seconds and one and a half is there but it might be the case that you say okay uh my application requires a bit more bit less latency and then on lambda it will not be possible yeah you probably need containers pre-loading and all that stuff yeah the more or less the warm start is um

not the GL uh the warm start will basically remain the same because snap start is optimizing the cold start times. But there is one thing that you need to take to to to to uh to pay attention to. there is a cache for uh for this snapshot and it's a bit complicated technology and it deserves probably its own talk but the thing is the if you first

of all uh deploy uh the the first lambda function the cache is empty they then store the bits and these bits coming also from operating system from JVM and they try to feel they try to predict the path ais in the lambda function and fill this cache. And I also more or less wrote about that stuff but there is a beautiful talk of from people from AWS

who developed this snapstart that they uh explain that. Um so basically the the the functionality is the more frequent you invoke the lambda function with a snap start the better the latency will be. And uh I I will probably show you those results where they are. Interestingly, just let me maybe go here. Okay. And so basically uh what I showed you there have been the results for

all invocations. If you will be looking into the um call starts of the and there there have been 100 call starts and I showed you the P90 of this 100 which was a bit more than 1 second or go went up to 2 seconds. If you will be looking only on the call starts of the last 70 exec uh cold call starts then um the latency will

be much less that will be between 900 and one and a half seconds. So the first 30 call starts had a bigger latency because the cache was not filled. Yeah. And then the snap start cache began to fill and then the performance even increased. So you need to measure also like you will see that with the snap start the first invocations will be a bit slower and

then it will become better. So there is online also guide on snapstart that you need to look what can be optimized because every application is different and uh there are different things but if you're using like Jackson and all that stuff that there is always potential or or HTTP client there is always potential for optimization you can also use AWS profiler extension by the way uh also

comes from AWS it it shows you something like heat map and with that you can uh to see for example where your application spends time and sometimes it gives you the idea where you can maybe do some priming yeah for example I identified a certain potential I don't want to overwhelm you but that helped me it was released end of the last year it helped me to

to to improve the star so what you can experiment with is giving lambda more or less memory there is now 5.5 client that you can set so by default is 4.5 Apache but you can use Apache 5.5 or you can use URL connection HTTP client AWS provides also native HTTP client so you can experiment what's the best for your use case you can give different memory setting

once again you can give also um compilation options that's really advanced you can look into this would you like how far you would like to compile because lambda is a shortlived environment and sometimes you have time to wait for optimization after 10,000 invocation of of the function. Yeah, which is kind of the server compilation option. So once again, challenges and limitations. Yeah, not all programming languages. Deployment

with the snap start takes a bit of time because the snapshots needs to be taken secure secured and stored across three availability zones in AWS. It takes two and between two two and a half minutes if you uh um enable snapstart. But if you enabled it on many lambda function and deploy um them together then it the snapshot will be taken in parallel for many. So it

will not increase but anyway it's there uh the latency is there. It breaks a bit um uh yeah experience. So maybe for the uh test environment don't activate it because you would like to test more quickly only if you take uh test u performance then it makes sense. And there are things that it doesn't work with like um elastic file system if you use that it works

with a three but if you would like to use an FS system it doesn't so that was the um snap snap start part there is a gravm native image probably everybody should know what is that and the promise is here more or less with this ahead of time compilation yeah because it's native optimized image and uh the promise is it's the smaller package and It should be

quicker and yeah we can prove that the thing is AWS doesn't offer manage grow VM. Yeah GV VM is a different VM. Yeah there is a manage Java but there is no manage grav. But they allow you to deploy the custom runtime and with custom runtime you can more or less create native image and deploy zip it and deploy it as a as a custom runtime. uh

and then you have this bootstrap which is more or less the this native application. The thing is of course um uh that the ahead of time compilation requires everything to be part of the native image directly. Yeah, it's ahead of time compilation and spring normally use things like um on runtime. Yeah, class loading this all that stuff with annotations and all this stuff that cannot work and

that's why um Spring Boot 3 provides also IoT optimizations. Yeah, there is Spring IoT ahead of time processing that you need to run before the the the native image is created. Um Micronaut and um Quarkus do things u on the compile time so they don't suffer from that. So this is how things look like. And now I need to to do stuff. I need to to to

tell now this is the spring example. I need to to tell the classes that needs to be part of the of the native image in advance because if you have classes which will be loaded by reflection like for example also lambda functions you defined in infrastructure as a code. Yeah. If you will create native image they will not be there. So you need to to tell certain

things to spring that uh or this is more or less the hint to to gravium native image to package that. So there is additional work of defining this register reflection for binding image runtime hits uh a lot lot of smaller things uh that you needs to define and then basically there are plugins this is spring boot maven plug-in here with process IoT it runs as a part

of more or less compilation process and you will see a lot of uh things in subfolders of IoT and this is basically we need native image plugins here as a part of Maven that will uh more or less do build the native image uh for us here which will then need to zip because uh AWS uh custom runtime requires that as a zip and uh yeah this

is what will happen if you invoke IoT stuff uh that things will be pre-ompiled where the they will normally run on at runtime then the spring pre-ompileines all that stuff in IoT and all the sub packages you will see uh lot lots fun stuff and quirkus and and micronode they support it more or less better natively I would say but nevertheless this is the quarkus you need

similar annotation that's the name register for reflection and you will need to provide classes that that will be loaded at uh uh kind of more or less per reflection in the same time the thing with um with quarkus that I define that they uh pre-instantiate all the classes this uh um during the build of the native image and sometimes it's not okay. I would like to provide

the environment variables and I can't uh if I do that in the static initializer block they are not set they are set via the lambda function then I deploy so there there have been tricks to do that uh and this is the example uh with micronode the same reflection config and uh similar stuff so a bit different logic but but uh in the end it's about hinting

what needs needs to be bundled uh within the um native image there is this tracing agent that you can run as a part of your test suite from growvm itself it will preconfigure that for you basically there they see what is required what is reachable v during the test phase and will do the job for you I will jump over this what I would like to show

you now the the results with the growv native image cold starts you see there are this is p90 I think less than a second. Yeah. So, GrowVM provides they are comparable to the last 70 with snap start when caching caching kicks in but nevertheless grow VM you can go down from 7 5 seconds. This is P90. It's 90% of the cases below 1 second and maybe you

can optimize it more. I'm not a huge expert in grow VM. Snapstart cannot be optimized. There is no configuration of something but grow VM maybe we can do something here even to put it aside. So you can go like to 1 second cold start. The warm starts are also by the way uh better. Yeah. So they also optimize for um warm start. So the you see here

even the the max values are only 150 milliseconds not seconds anymore. Okay. I see they have so there are other optimizations you can try this this native HTTP client in that case. It can provide you the stuff but also growvm is not for free. Yeah, it's powerful. It improved the call starts and even memory footprint which is also the cost factor but you need the custom runtime

is is Linux executable. So you need to run this on Linux and the whole c CI/CD pipeline is on your own. Snap start this all snapshot creation is done by AWS. They start the infrastructure they create the snapshot they store it for you. here the ICD pipeline and uh building uh image requires like also 3 minutes and 6 to 10 GB of memory for lambda function that

that it's on your own and it will cost you also money to run those CI/CD depending how you do it but that's yeah build time once again similar to snap start and there are lots of cavits um uh how you can run into errors and runtime if you use frameworks like hibernate it becomes hell. Uh with growvm there's a lot of things that are not working and

so on but there is at least the list of uh frameworks that are ready growvm ready. Yeah. So you know but sometimes you have one dependency it brings 20 others and something is not growvm ready or you update one dependency and then there are versions that are not growvm ready. So it it's also a kind of a pain. You need to be very careful if you update

something. I'm running out of time. I wrote the articles on dev tune. You can read it step by step. There are other things how you can use spring boot on on lambda. There is also AWS lambda web adapter. I have the article serial for quosus micronote. So then if you would like I will I will uh share this on slides share and I will also upload the

slides so you can I explain this step by step. I now also started the or even six articles on using Java 25 and Dynamob and the same using the posgress database. I used here Dynamob not everybody uses that Aurora this Aurora DSQL is managed serverless posgress compatible but not 100% compatible database similar steps but there are differences there is no HTP client this is pure GDBC there

is no JSON conversion but you can optimize uh you can preload um for example the um data source pool that you will be using with the relational databases so there are examples so you can do this there also Lambda managed instances. If you if Lambda itself is expensive, you can now kind of reserve normal EC2 instances, but you have Lambda uh programming model. Yeah, you can try

it out. The call starts are much less because AWS would manage instances, they pre-scale that like in the container. It's a bit different model, but it might be better suited. But you can use lambda functions on that and and you you don't have access to the EC2 instances anyway. But it gives you the possibility. Maybe the last thing project laden maybe you heard this IoT cache all

that stuff. Uh it even released partially with the same goal similar goal as as as um gravm. The thing is it's not possible to run it on lambda because this IoT cache it caches also the JVM and the JVM version. And if you use lambda managed version uh of Java then um lambda team will patch it beyond beyond the so you you don't have um impact on

that and so that will more or less destroy your IoT cache so they say don't use IoT cache with manage Java on lambda but you can use it for example for containers but the call starts will be much bigger as G gravs snap start currently I see only 50% performance improvement with ahead of time compilation. So the gravm uh is superior here. This is the thing that

was sent. No, I don't understand what is the future of gravm because they say that the more or less they would like to focus on other stuff. So I personally don't know can we now use gravium native image or not that we need to ask the people from from Oracle uh what's really the strategy there because that's I'm currently a bit of hesitant and more or less

if I use it I use snap start. So reme-measure once again you can have other results because there there might be improvements on AWS on snapstart and so on. There are examples there are there are links on GitHub you can check out you can measure but there might be certain differences my personal preference is I always start with snap start because it's managed but if you need

the last millisecond try out GLVM yeah beyond the question about the future it's yeah that's that's something that I I I feel I need to do more job with with GVM I need to test more I need to be careful with updates it's not the case with a snap start I don't have any problems here. And that's it from my side. I know I probably took a

bit more time or 3 minutes more, but but I think we have a pause here. Do I have Thank you very much. Uh do I have time for questions or I should No, no, no questions. Okay, I could check, but I I'm here. If you have questions, please simply come to me.

From event

JCON EUROPE

20 Apr 2026 – 23 Apr 2026

All event videos
Back to Watch