

# Slick Scheduling Algorithm
For the [new scheduling algorithm in #1461](https://github.com/slick/slick/pull/1461) to work correctly without deadlocks
it is critical that Slicks knows the connection pool size. It is set automatically [when you let Slick configure HikariCP](https://github.com/szeiger/slick/commit/353a6e41f389fbe776f1c38166bbe1a3f0a3f2e0)
by calling `Database.forDatabase(...)` or `Database.forDataSource` where the default maxConnections is set to `1` connection if you don't override it yourself which
for most cases if too low, in all other cases you have to do it on your own by using

Introduction of priority levels:

- __HighPriority__: a DBIO which already has a connection associated (due to running in a transaction or with pinned session)
- __MediumPriority__: the old highPrio = true case: a continuation of another DBIO action, it should always be able to be enqueued
- __LowPriority__: any other DBIO, if the queue is full it will not be enqueued

The queue backing Slick's ThreadPoolExecutor now consists internally of two backing queues:

- A __HighPriority__ queue which contains only the HighPriority runnables
- The old queue containing the other priority levels.

AsyncExecutor

HighPriority items are always taken first, until this queue is exhausted and only then Low- or MediumPriority items are considered.

We also do connection counting in the HikariCPJdbcDataSource:
When there are no more connections in the pool, we prevent low/medium priority items from being processed from the queue.
Only HighPriority items are able to make progress, because they already have a connection.

## Database thread pool
Every 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
you would use for the size of the connection pool in a traditional, blocking application.

When using Database.forConfig, the thread pool is configured directly in the external configuration file together with
the connection parameters. If you use any other factory method to get a Database, you can either use a default configuration
or specify a custom AsyncExecutor:

```scala
val db = Database.forURL("jdbc:h2:mem:test1;DB_CLOSE_DELAY=-1", driver="org.h2.Driver",
  executor = AsyncExecutor("test1", numThreads=10, queueSize=1000))
```

## Configuration
The object `org.apache.pekko.persistence.jdbc.util.SlickDatabase` reads the HikariJdbcDataSource configuration from typesafe
configuration from the object `slick.db`. It expects a `connectionPool` field that has been set to `HikariCP`
The class `slick.jdbc.hikaricp.HikariCPJdbcDataSource` reads the configured Hikari Connection Pool configuration.

```scala
def forConfig(c: Config, driver: Driver, name: String, classLoader: ClassLoader): HikariCPJdbcDataSource = {
    val hconf = new HikariConfig()

    // Connection settings
    if (c.hasPath("dataSourceClass")) {
      hconf.setDataSourceClassName(c.getString("dataSourceClass"))
    } else {
      Option(c.getStringOr("driverClassName", c.getStringOr("driver"))).map(hconf.setDriverClassName _)
    }
    hconf.setJdbcUrl(c.getStringOr("url", null))
    c.getStringOpt("user").foreach(hconf.setUsername)
    c.getStringOpt("password").foreach(hconf.setPassword)
    c.getPropertiesOpt("properties").foreach(hconf.setDataSourceProperties)

    // Pool configuration
    hconf.setConnectionTimeout(c.getMillisecondsOr("connectionTimeout", 1000))
    hconf.setValidationTimeout(c.getMillisecondsOr("validationTimeout", 1000))
    hconf.setIdleTimeout(c.getMillisecondsOr("idleTimeout", 600000))
    hconf.setMaxLifetime(c.getMillisecondsOr("maxLifetime", 1800000))
    hconf.setLeakDetectionThreshold(c.getMillisecondsOr("leakDetectionThreshold", 0))
    hconf.setInitializationFailFast(c.getBooleanOr("initializationFailFast", false))
    c.getStringOpt("connectionTestQuery").foreach(hconf.setConnectionTestQuery)
    c.getStringOpt("connectionInitSql").foreach(hconf.setConnectionInitSql)
    val numThreads = c.getIntOr("numThreads", 20)
    hconf.setMaximumPoolSize(c.getIntOr("maxConnections", numThreads * 5))
    hconf.setMinimumIdle(c.getIntOr("minConnections", numThreads))
    hconf.setPoolName(c.getStringOr("poolName", name))
    hconf.setRegisterMbeans(c.getBooleanOr("registerMbeans", false))

    // Equivalent of ConnectionPreparer
    hconf.setReadOnly(c.getBooleanOr("readOnly", false))
    c.getStringOpt("isolation").map("TRANSACTION_" + _).foreach(hconf.setTransactionIsolation)
    hconf.setCatalog(c.getStringOr("catalog", null))

    val ds = new HikariDataSource(hconf)
    new HikariCPJdbcDataSource(ds, hconf)
  }
```

## Resources
- [Transactionally deadlock still there on 3.2.0-M1?](https://github.com/slick/slick/issues/1614)
- [Slick deadlock #1461](https://github.com/slick/slick/pull/1461)
- [connection leak detected #1678](https://github.com/slick/slick/issues/1678)
