Spring Boot vs Quarkus Performance

Introduction

One of my main concerns about the Spring ecosystem is the amount of resources that a simple Spring Boot microservice consumes. As I mentioned in my previous post, Event Sourcing & CQRS: Learning points, when you have a considerable amount of Spring Boot microservices running in the cloud your cloud provider bill could be very expensive.

A few months ago, a colleague introduce me to Quarkus. There are many reasons to use Quarkus, but I was particularly interested in memory consumption optimisation.

That is the reason why I created a simple project to test by myself how Quarkus compares to Spring in terms of memory consumption and response time.

Spring Boot Application

The Spring Boot application has been generated from https://start.spring.io/. It is a simple application that contains the following dependencies:

  • Spring Boot version 2.3.3.RELEASE
  • Spring Web
  • Spring Data JPA
  • Spring Actuator
  • Lombok
  • Java 11

Quarkus Application

The Quarkus application has been built using the Quarkus code generator https://code.quarkus.io/. It is an out of the box application that contains the following dependencies:

  • Quarkus platform version 1.6.1.Final
  • Quarkus Resteasy
  • Quarkus Smallrye health
  • Quarkus Hibernate Panache
  • Java 11

One of the benefits of using Quarkus is that you can generate, out of the box, a native docker image using GraalVM. Therefore, I will be comparing Spring boot against both native and non-native images.

Infrastructure

This project uses Terraform for creating and deploying resources to AWS.
Both applications will be using exactly the same infrastructure:

  • 1 Application Load Balancer (ALB)
  • 1 AWS ECS cluster
  • 2 tasks running in the cluster. Each task has memory = 512MiB and CPU = 256.
  • A MySQL 8.0.17 engine managed by AWS RDS

For more information, please check the terraform folder which contains all the modules and resources used.

Rest Controller

The idea of this project was to build a simple API that implements create and read operations. Both applications have implemented the same endpoints:

  • GET /products: Returns the last 20 products.
  • GET /products/{id}: Returns one single product for a given Id.
  • POST /products: Saves a new product.

Load tests

Load testing has been done with Gatling. Gatling is a great developer tool to load test web applications.

The load test tries to simulate a common use case. Each user will perform the following actions:

  1. Gets all the latest products.
  2. Save a new product.
  3. Retrieve the product saved on step 2.

The test ramps up 200 users during the first minute and then keeps constantly 200 users for 2 hours.

Response time results

Quarkus Response time results
Quarkus Native Response time results
Spring Boot Response time results

Resources results

Quarkus metrics results
Quarkus Native metrics results
Spring Boot metrics results

Conclusion

The above results show better response times by Quarkus, particularly the native version. I usually pay more attention to the 99th percentile and the mean and in both cases, Quarkus perform better than Spring Boot.

On the other hand, I have to admit that I was surprised by the amount of memory Quarkus used. I was expecting the native version to have a lower memory consumption that Spring but that was not the case, while the non-native version was just slightly better than the Spring one.

The Spring Boot memory consumption kept growing quite sharp at some points, while Quarkus applications were more stable. That’s why I still have an outstanding question unanswered: What would have happened if I had run the tests for more than 2 hours?

Source Code

As usual, all source code can be found over on GitHub.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.