Slick (3) – connection pool

Before talking into detailed knowledge about connection pool in Slick, we need to figure out two things: One is about Slick. Slick does not provide a connection pool implementation of its own. The other thing is about JDBC, which not provides asynchronous database driver for JVM. So we need to thread pool in Play Framework application to help improve performance.

1. Add connection pool to Slick:

Event Database contains an AsyncExecutor that manages the thread pool for asynchronous execution of Database I/O Actions. Its size is the main parameter to tune for the best performance of the Database object. It should be set to the value that we would use for the size of the connection pool in traditional, blocking application. For Slick, its default connection pool is HikariCP, which is a “zero-overhead” production-quality connection pool.

import play.api.db.DB
import play.api.Play.current
import slick.driver.MySQLDriver.simple._
object Contexts {
  def withSession[Res](f: Session => Res) = {
    Database.forDataSource(DB.getDataSource(),
      AsyncExecutor(<executor_name>, <num_threads>, <queue_size>)) withSession f
  }
}

2. connection pool configuration

There’re some important default configuration which we need to know. Of course, we can override them to optimize.

# HikariCP connection pool configure
play.db.hikaricp.connectionTimeout=30 seconds
play.db.hikaricp.maxLifetime=30 minutes
play.db.hikaricp.maximumPoolSize=10

3. thread pool vs connection pool

There is dependency between these two pool setting is in maximum attribute.Let us assume that your database can support maximum of 50 connections then it is straight forward to configure the connection pool maximum attribute as 50. But how this setting going to impact thread pool setting? Configure application server thread pool as maximum of 100. In this scenario application server will allow 100 request to be processed simultaneously however database have only 50 connection. So only 50 thread will get connection another 50 thread will fight to get database connection hence connections will be switched between threads frequently that slow down overall application performance.

Just adding database connections will not improve performance, and can compromise availability because of the need to reestablish all the connections during failover.

Let us assume we set thread pool maximum setting same as connection pool maximum setting of 500. Application server will allow 50 thread to be processed simultaneously the remaining thread will be in wait state. All the 50 thread will get database connection immediately hence it will processed quickly.

The above example assumed each thread will use one database connection (may be multiple connections but sequentially); if your application uses two database connections parallels by each thread then configure thread pool maximum setting as half of the connection pool maximum.

Setting the thread pool size too large can cause performance problems because if there are too many concurrent threads, task switching overhead becomes a serious bottleneck. Any more threads than the number of connections available could be wasteful given contention for the connections. Fewer threads will not consume the number of connections available. 

4. number of processor core vs the size of a thread pool

  • More threads mean more memory usage. Each thread requires a thread stack. For recent HotSopt JVMs, the minimum thread stack size is 64KB, and the default can be as much as 1MB. That can be significant. In addition, any thread that is alive is likely to own or share objects in the heap whether or not it is currently runnable. Therefore it is reasonable to expect that more threads mean a larger memory working set.
  • A JVM cannot have more threads actually running than their cores (or hyperthreaded cores) on the execution hardware.

5. Involve connection pool to database querying

def delete(userID: Int): Boolean = Contexts.withSession { implicit session =>
  val users = TableQuery[Users]
  val filterInfo = users.filter(_.id === userID)

  if (filterInfo.exists.run) {
    filterInfo.delete
    true
  } else false
}

6. Reuse session within one logic

We know the more sessions we use, the much easier the connection pool uses up. So within one logic, we suggestion to pass session around to finish one purpose. In order to reuse session, we need to pass session as implicit.

def save(implicit session: Session, traitInfo: TraitInfo): Int = {
  val traits = TableQuery[Traits]
  (traits returning traits.map(_.id)) += traitInfo
}

7. Additional important things to know

Some Concepts on Slick we need to know/understand:

  1. For every database session (withSession blocks), Slick is opening a new connection to the database server.
  2. Session Handling:
    1. The Database object’s withSession method creates a Session, passes it to a given function and closes it afterward. If we use a connection pool, closing the Session returns the connection to the pool.
    2. Only the method actually executing the query in the database requires a Session. The executing methods are made available via implicit conversions.
    3. It is not recommended to handle the lifetime of a Session manually.
  3. All methods that execute a query take an implicit Session value. Of course, you can also pass a session explicit if you prefer.

Basic Knowledge on connection pool:

  1. Connection pools are common in network applications and they’re often tricky to implement. For example, it is often desirable to have timeouts on acquisition from the pool since various clients have different latency requirement.
  2. Pools are simple in principle: we maintain a queue of connections and we satisfy waiters as they come in. With additional synchronization primitives this typically involves keeping two queues: one of waiters (when there are no connections), and one of connections (when there are no waiters).

8. Todo In Future:

There are some points we can optimize.

  1. Slick uses prepared statements wherever possible but it does not cache them on its own. We should therefore enable prepared statement caching in the connection pool’s configuration and select a sufficiently large pool size.
Advertisements

One thought on “Slick (3) – connection pool

  1. Pingback: Scala – Performance Optimization | Play Harder, Work Harder

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s