One option is to use alternative SQL clients that are not completely blocked. Some examples include: https://github.com/mauricio/postgresql-async or https://github.com/finagle/roc . Of course, none of these drivers are officially supported by database providers. In addition, functionality is much less attractive compared to mature JDBC-based abstractions like Hibernate or jOOQ.
An alternative idea came to me from the world of Scala. The idea is to send blocking calls to an isolated ThreadPool so as not to mix blocking and non-blocking calls together. This will allow us to control the total number of threads and allow the processor to perform non-blocking tasks in the main execution context with some potential optimizations. Assuming we have a JDBC-based implementation, such as Spring Data JPA, that really blocks, we can make its execution asynchronous and send it to the dedicated thread pool.
@RestController public class HomeController { private final MeasurementRepository repository; private final Scheduler scheduler; public HomeController(MeasurementRepository repository, @Qualifier("jdbcScheduler") Scheduler scheduler) { this.repository = repository; this.scheduler = scheduler; } @GetMapping(value = "/v1/measurements") public Flux<Measurement> getMeasurements() { return Mono.fromCallable(() -> repository.findByFromDateGreaterThanEqual(new Date(1486980000L))).publishOn(scheduler); } }
Our scheduler for JDBC must be configured using a dedicated thread pool with a size equal to the number of connections.
@Configuration public class SchedulerConfiguration { private final Integer connectionPoolSize; public SchedulerConfiguration(@Value("${spring.datasource.maximum-pool-size}") Integer connectionPoolSize) { this.connectionPoolSize = connectionPoolSize; } @Bean public Scheduler jdbcScheduler() { return Schedulers.fromExecutor(Executors.newFixedThreadPool(connectionPoolSize)); } }
However, difficulties arise with this approach. The main thing is transaction management. In JDBC, transactions are possible in only one java.sql.Connection connection. To perform multiple operations in a single transaction, they must share the connection. If we want to do some calculations between them, we need to keep in touch. This is not very effective, since we keep a limited number of connections in standby mode when performing calculations between them.
This JDBC asynchronous shell idea is not new and has already been implemented in the Scala Slick 3 library. Finally, non-blocking JDBC may appear in the Java roadmap. As announced in JavaOne in September 2016, it is possible that we will see it in Java 10.
Grygoriy gonchar
source share