2

I'm using Play 2.5 with Slick. The docs on this topic simply state that everything is managed by Slick and Play's Slick module. This example however, prints Dispatcher[akka.actor.default-dispatcher]:

class MyDbioImpl @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)(implicit ec: ExecutionContext)
  with HasDatabaseConfigProvider[JdbcProfile] {
  import profile.api._
  def selectSomeStuff(): Future[MyResult] = db.run {
    println(ec)
    [...]
  }
}

Since the execution context is printed inside db.run, it seems like all of my database access will also be executed on the default execution context.

I found this answer to an older question which, at the time, solved the problem. But this solution is since deprecated, it is suggested to use dependency injection to acquire the application context. When I try to do this, I get an error saying that play.akka.actor.slick-context does not exist...

class MyDbioProvider @Inject()(actorSystem: ActorSystem,
                               protected val dbConfigProvider: DatabaseConfigProvider)
  extends Provider[MyDbioImpl] {
  override def get(): MyDbioImpl = {
    val ec = actorSystem.dispatchers.lookup("play.akka.actor.slick-context")
    new MyDbioImpl(dbConfigProvider)(ec)
  }
}

Edit: Is Slick's execution context a "normal" execution context which is defined in a config file somewhere? Where does the context switch take place? I assumed the entry point to the "database world" is at db.run.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Paco1
  • 758
  • 6
  • 18

2 Answers2

3

According to Slick:

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 (see About Pool Sizing in the HikariCP documentation for further information). 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.

Basically it says you don't need to create an isolated ExecutionContext since Slick already isolates a thread pool internally. Any call you make to Slick is non-blocking thus you should use the default ExecutionContext.

  • So as long as I don't specify any settings to change the default config, Slick will use its own execution context. I wonder, where does the actual context change take place if not in db.run? And I should be able to see the configuration of this context at runtime, right? – Paco1 Jun 12 '18 at 10:07
  • I don't think I follow what you meant, @Paco1, but I'll try to answer you. When you say the "actual context change" do you mean the `ExecutionContext` used by Slick? Well, yes, roughly speaking it's created and used inside the `db.run` function. You should be able to see the configuration at runtime since Slick exposes the [`Database` object via JMX](http://slick.lightbend.com/doc/3.2.0/config.html). But also you could just check what the default configuration that Slick uses or even override it yourself (increase thread number, queue size, etc) – Gabriel Francisco Jun 13 '18 at 03:25
  • Thanks for your answer, that's exactly what I was asking. The docs say that, given a database named "default", Slick will create a thread pool with that name to run its IO in it. So I went ahead and created some more in-memory DBs, orders and customers, but no corresponding config shows up during runtime. Furthermore, when I inject an ActorSystem somewhere and access it's dispatchers there are no additional dispatchers beyond the default-dispatcher from Play. Does Slick register the ExecCtx there, or does it hide them? Or do I understand something completely wrong? Thanks for your help. – Paco1 Jun 14 '18 at 06:51
  • I don't think Slick adds a new dispatcher to Akka, it just create a new `ExecutionContext` inside its own context and hide it from outside. – Gabriel Francisco Jun 15 '18 at 15:24
0

Slick's implementation of this can be seen in the BasicBackend.scala file: the runInContextSafe method. The code is as follows:

val promise = Promise[R]
val runnable = new Runnable {
   override def run() = {
      try {
         promise.completeWith(runInContextInline(a, ctx, streaming, topLevel, stackLevel = 1))
      } catch {
              case NonFatal(ex) => promise.failure(ex)
      }
   }
}
DBIO.sameThreadExecutionContext.execute(runnable)
promise.future

As shown above, Promise is used here, and then its code is executed quickly using its internal thread pool, and the Future object of the Promise is returned. Therefore, when Await.result/ready is executed, the Promise here is probably already executed by Slick's internal thread, so it is enough to get the result, and it is possible to execute Await.result/ready in an environment such as Play. Of non-blocking.

For details, please refer to Scala's documentation on Future and Promise: https://docs.scala-lang.org/overviews/core/futures.html