Connection pooling is a technique used to improve performance in applications with dynamic database driven content. Opening and closing database connections may not seem like a costly expense but it can add up rather quickly. Let's assume it takes 5ms to establish a connection and 5ms to execute your query (Completely made up numbers), 50% of the time is establishing the connection. Extend this to thousands or tens of thousands of requests and there is a lot of wasted network time. Connection pools are essentially a cache of open database connections. Once you open and use a database connection instead of closing it you add it back to the pool. When you go to fetch a new connection if there is one available in the pool it will use that connection instead of establishing another.
Why use a connection pool?
- Constantly opening and closing connections can be expensive. Cache and reuse.
- When activity spikes you can limit the number of connections to the database. This will force code to block until a connection is available. This is especially helpful in distributed environments.
- Split out common operations into multiple pools. For instance you can have a pool designated for OLAP connections and a pool for OLTP connections each with different configurations.
HikariCP
HikariCP is a very fast lightweight Java connection pool. The API and overall codebase is relatively small (A good thing) and highly optimized. It also does not cut corners for performance like many other Java connection pool implementations. The Wiki is highly informative and dives really deep. If you are not as interested in the deep dives you should at least read and watch the video on connection pool sizing.
Creating Connection pools
Let's create two connections pools one for OLTP (named transactional) queries and one for OLAP (named processing). We want them split so we can have a queue of reporting queries back up but allow critical transactional queries to still get priority (This is up to the database of course but we can help a bit). We can also easily configure different timeouts or transaction isolation levels. For now we just just change their names and pool sizes.
Configuring the Pools
HikariCP offers several options for configuring the pool. Since we are fans of roll your own and already created our own Typesafe Configuration we will reuse that. Notice we are using some of Typesafe's configuration inheritance.
ConnectionPool Factory
Since we don't need any additional state a static factory method passing our config, MetricRegistry, and HealthCheckRegistry is sufficient. Once again Dropwizard Metrics makes an appearance hooking into our connection pool now. This will provide us with some very useful pool stats in the future.
Interested in knowing when your application cannot connect to the database? Take a look at Monitoring your Java Services with Dropwizard Health Checks
ConnectionPool Implementation
Now that we have two separate configs for our transactional and processing pools lets initialize them. Once again we are not using DI and instead are using the enum singleton pattern for lazy initialized singletons. Feel free to use DI in your own implementations. We now have two different pools with different configs that each lazily wire themselves up on demand.
ConnectionPool Implementation Log
Notice how the config properly inherited its values and each config is lazily loaded.