JDBC has been the de-facto standard for accessing relational databases for a long time. Times are however changing. In cloud environments the pay-per-use model is popular. If you can use resources more efficiently, you can save money! In addition, when running applications at cloud-scale, the number of concurrent requests which hit your services can skyrocket. Can JDBC handle such concurrency efficiently? Answer: No. The time has come to look beyond JDBC!
For services, reactive frameworks are becoming more popular. These frameworks can make more efficient use of resources due to their non-blocking nature, especially at high concurrency. Now with R2DBC relational databases can also be accessed using a reactive API! This means more efficient use of CPU and memory and better response times and throughput at high concurrency.
A tempting story but there are of course many questions
- Is R2DBC mature enough to implement?
- Which R2DBC drivers are available?
- Is framework support available?
- What do you need to do in order to implement R2DBC?
- Does it improve performance enough to make the switch worthwhile?
- Do I need to have a completely non-blocking stack to benefit from using R2DBC?
To answer these questions and more, I've created several implementations using R2DBC and JDBC with Spring Web MVC and Spring WebFlux and put them to the test. I looked at how to implement R2DBC and measured resource usage, throughput, and responsetimes. Interested in the results? Hint: R2DBC is pretty cool!
2. @MaartenSmeetsNL
Who am I?
Who is Maarten?
• Software architect at AMIS / Conclusion
• Active community member
(e.g. blogging, presenting, writing articles)
@MaartenSmeetsNL
https://nl.linkedin.com/in/smeetsm
Maarten.Smeets@amis.nl
3. @MaartenSmeetsNL
What is the CJIB
• The Central Judicial Collection Agency
part of the Ministry of Justice and Security in the Netherlands
• The CJIB is responsible for collecting a range of different fines, such as traffic
fines and punitive orders.
• Works together with EU Member States when it comes to collecting fines.
• Plays a key enforcement role in decisions relating to criminal matters, such as
• court rulings
• decisions made by one of the Public Prosecution Service’s public
prosecutors
• Located in Leeuwarden, Friesland
Where do I work?
4. @MaartenSmeetsNL
The CJIB and Java
• 1400 people. ICT department of around 325 people. 100 Java developers
• 30 teams using Scrum and SAFe. Tight integration between business and IT
• Solid CI/CD pipelines and release train
• Automated testing using Cucumber, Gherkin
• Code quality checks using SonarQube
• Bamboo, Jenkins, Puppet, Git, Maven, Vault
• Running on
• CentOS 7
• OpenJDK 11, Spring Boot in containers on Kubernetes
CJIB ICT
5. @MaartenSmeetsNL
Disclaimer
• The performance tests mentioned in this presentation were conducted with intention to obtain information on
what performance differences can be expected from running R2DBC vs JDBC. A best effort has been made to
conduct an unbiased test. Still, performance depends on many parameters such as hardware, specifics of the
framework implementation, the usage of different back-ends, concurrency, versions of libraries, OS, virtualization
and various other factors. I cannot provide any guarantees that the same or similar results will be achieved with
other than tested configurations. The use of these results is at your own risk. I shall in no case accept liability for
any loss resulting from the use of the results or decisions based on them.
7. @MaartenSmeetsNL
JDBC. What is it?
• Java Database Connectivity
• Released in 1997 by Sun Microsystems in JDK 1.1
• Provides an API to access relational databases
8. @MaartenSmeetsNL
JDBC. What is it?
• Java Database Connectivity
• Released in 1997 by Sun Microsystems in JDK 1.1
• Provides an API to access relational databases
Don’t do this!
Use a framework
9. @MaartenSmeetsNL
JDBC driver and connection pool
Java Persistence API (JPA)
Service
Controller
Client
JVM
HTTP
Business logic
Object Relational Mapping (ORM)
Repositories, DAOs, transaction management
Exposed endpoints
Database access
10. @MaartenSmeetsNL
Java from service to database.
A Spring JDBC stack
Service
Spring
WebMVCorWebflux
SpringDataJPA
Business
logic
Repository
ORM
Database
access
SQL
HTTP
Controller
Client
JDBCdriver
connectionpool
11. @MaartenSmeetsNL
Java from service to database.
A Spring JDBC stack
Service
Spring
WebMVCorWebflux
SpringDataJPA
Business
logic
Repository
ORM
Database
access
SQL
HTTP
Controller
Client
JDBCdriver
connectionpool
JDBC specific
12. @MaartenSmeetsNL
A relational database
Department
# deptNo
*name
Employee
# empNo
*name
* deptNo
SELECT emp.name
FROM Employee emp, Department dept
WHERE emp.deptNo=dept.deptNo
AND dept.name=‘Administration’
SQL
Primary key
Foreign key
14. @MaartenSmeetsNL
Repository
Bridge the gap between datasource and business logic
• Predefined operations on entities
CrudRepository: FindById, Save, Count, Delete
• Query derivation based on interface method name
countByDepartment, listByDepartment, deleteByDepartment
findByLastnameOrderByFirstnameAsc
• Define named queries
using JPQL or native SQL using @Query
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
15. @MaartenSmeetsNL
JDBC and threads
• A thread per database call
JDBC consumes a thread per database call
• Blocks while waiting
JDBC blocks the thread until the call is done or it is interrupted
• Connections consume threads
• The connection pool can block threads
If more connections than the pool size are required,
the connection pool blocks the requesting thread
17. @MaartenSmeetsNL
CPU cost
• Few threads can run in parallel
Motherboard sockets have CPUs. CPUs have cores. Cores handle threads.
A single core handles a single thread or 2 threads with hyperthreading (HT)
E.g. my laptop: 1 socket, 1 CPU, 12 cores, 24 threads (with HT)
• More threads might require blocking and switching
If the system requires more threads than can be accommodated,
e.g. if many threads are blocked due to waiting for a database result or connection
it might have to switch between threads (storing and restoring state)
Switching between threads is called context switching
this is computationally expensive
Thread 1 Thread 2
User code
Kernel code
User code
Kernel code
User code
Kernel code
User code
Context switch
Context switch
Context switch
18. @MaartenSmeetsNL
Memory cost
Reserved memory is virtual (but process private)
Committed memory is actually mapped to RAM or swap
• Java 8
Reserved and Committed memory is close to a number of threads * 1MB
• Java 11
No longer aggressively allocates up to Reserved Memory at the time of thread creation
Committed memory is much lower. Other threads of the same process can use it!
• Java 12
JEP 346: Promptly return unused committed memory from G1
after a period of low application activity
https://dzone.com/articles/how-much-memory-does-a-java-thread-take
20. @MaartenSmeetsNL
• Long running operations
– Stuck threads
– Thread starvation
– Application responsiveness
Consequences
Of how JDBC and JPA use threads
28. @MaartenSmeetsNL
Java from service to database.
A Spring R2DBC stack
Service
Spring
WebMVCorWebflux
SpringDataR2DBC
Business
logic
Repository
ORM
Database
access
SQL
HTTP
Controller
Client
R2DBCdriver
connectionpool
30. @MaartenSmeetsNL
Spring Data R2DBC
• No JPA
Reactive Repositories are not supported by JPA
So no JPA! No Hibernate, EclipseLink, OpenJPA
• No Spring Data REST
Reactive Repositories are not supported by Spring Data REST
You have to manually write your controller implementation
31. @MaartenSmeetsNL
Spring Data R2DBC
• No JPA
Reactive Repositories are not supported by JPA
So no JPA! No Hibernate, EclipseLink, OpenJPA
• No Spring Data REST
Reactive Repositories are not supported by Spring Data REST
You have to manually write your controller implementation
• No JPA annotations
Spring Data R2DBC uses Spring Data annotations on entities instead of JPA
– org.springframework.data.annotation.* instead of javax.persistence.*
32. @MaartenSmeetsNL
Spring Data R2DBC
• No JPA
Reactive Repositories are not supported by JPA
So no JPA! No Hibernate, EclipseLink, OpenJPA
• No Spring Data REST
Reactive Repositories are not supported by Spring Data REST
You have to manually write your controller implementation
• No JPA annotations
Spring Data R2DBC uses Spring Data annotations on entities instead of JPA
– org.springframework.data.annotation.* instead of javax.persistence.*
• Less functionality
Spring Data R2DBC lacks some functionality
– Generate a schema from entities
– Executing an SQL script on startup
33. @MaartenSmeetsNL
Spring Data R2DBC
Spring Data R2DBC Spring Data JPA (JDBC)
Uses Hibernate or other JPA
Hibernate Reactive (Vert.X)
Object Relational Mapping X X X
Transaction management X X X
Reactive repositories X
Blocking repositories X
Caching X X
Lazy loading X X
Write behind X X
Reactive interfaces Reactor (Flux, Mono) Mutiny (Multi, Uni)
‘The Quarkus team is exploring various alternatives to bridging the gap between the JPA and Reactive worlds.’
https://quarkus.io/guides/spring-data-jpa
43. @MaartenSmeetsNL
Some details about the measures
• GET requests
Simple GET requests using wrk
to fetch data from a Postgres database
• Priming
First 10 seconds priming at high load.
To initialize the connection pool and load classes.
GET
44. @MaartenSmeetsNL
Some details about the measures
• GET requests
Simple GET requests using wrk
to fetch data from a Postgres database
• Priming
First 10 seconds priming at high load.
To initialize the connection pool and load classes.
• Dedicated resources
4 CPU threads for the load generator
4 CPU threads for the service
4 CPU threads for the database
OpenJDK 11 on Ubuntu 18.04, 2Gb heap for the JVM
GET
47. @MaartenSmeetsNL
About the data processing
Python!
• Parsing output
JupyterLab
• Data processing
Pandas, Numpy
• Visualisation
Matplotlib
48. @MaartenSmeetsNL
Latency / response times
Lower is better!
• At high concurrency R2DBC gives better response times
• WebFlux gives better response times than Web MVC
• Even one reactive component (R2DBC or Webflux)
improves response times
49. @MaartenSmeetsNL
Throughput
Higher is better!
• Throughput of R2DBC is better than JDBC
at high concurrency
• Web MVC + JDBC
does best at low concurrency
starts to perform worse at high concurrency
• R2DBC and WebFlux show more stable throughput
• WebFlux with JDBC gives worst throughput
at any concurrency
50. @MaartenSmeetsNL
Memory usage per request
Lower is better!
• Web MVC + JDBC uses least memory
at low concurrency
• R2DBC uses less memory per request than JDBC
at high concurrency
51. @MaartenSmeetsNL
CPU usage per request
Lower is better!
• Web MVC + JDBC does best
at low concurrency
• R2DBC uses less CPU than JDBC
at high concurrency
• Web MVC + JDBC uses more CPU per request
as concurrency increases
• A single non-blocking component
causes a more stable CPU usage per request
53. @MaartenSmeetsNL
Summary performance tests
• At low concurrency
Web MVC + JDBC gives best response times and throughput
Uses resources more efficiently than R2DBC
• At high concurrency
R2DBC gives better response times and throughput
R2DBC uses resources more efficiently
54. @MaartenSmeetsNL
A look into the future
Oracle 20c Oracle Database JDBC drivers are fiber-ready. Fibers impose some
restrictions on user code. The Oracle Database JDBC drivers to be released in
Oracle 20c have been tweaked to fully support fibers. As fibers are not yet part
of the JDK, the 20c JDBC drivers do not use fibers. They will work properly with
any user code that does use fibers though. So when Loom is integrated into the
JDK, you can use the 20c or later Oracle Database JDBC drivers and get the full
benefit.
Douglas Surber, JDBC architect at Oracle
55. @MaartenSmeetsNL
References
• Spring: Blocking vs non-blocking: R2DBC vs JDBC and WebFlux vs Web MVC
https://bit.ly/R2DBC_performance
• Sample code and test scripts
https://bit.ly/R2DBC_sample
• Homepage R2DBC
https://r2dbc.io/
• Spring Data R2DBC
https://spring.io/projects/spring-data-r2dbc