Knows Not Much
Knows Not Much

Reputation: 31546

Combine Two Slick Futures and then execute them together

I have written this code and I am trying to combine two futures obtained from separate SQL operations.

package com.example

import tables._
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import slick.backend.DatabasePublisher
import slick.driver.H2Driver.api._

object Hello {
  def main(args: Array[String]): Unit = {
    val db = Database.forConfig("h2mem1")
    try {
      val people = TableQuery[Persons]
      val setupAction : DBIO[Unit] = DBIO.seq(
        people.schema.create
        )
      val setupFuture : Future[Unit] = db.run(setupAction)

      val populateAction: DBIO[Option[Int]] = people ++= Seq(
          (1, "test1", "user1"),
          (2, "test2", "user2"),
          (3, "test3", "user3"),
          (4, "test4", "user4")
        )

      val populateFuture : Future[Option[Int]] = db.run(populateAction)

      val combinedFuture : Future[Option[Int]] = setupFuture >> populateFuture

      val r = combinedFuture.flatMap { results =>
        results.foreach(x => println(s"Number of rows inserted $x"))
      }
      Await.result(r, Duration.Inf)
    } 
    finally db.close
  }
}

But I get an error when I try to compile this code

[error] /Users/abhi/ScalaProjects/SlickTest2/src/main/scala/Hello.scala:29: 
value >> is not a member of scala.concurrent.Future[Unit]
[error]       val combinedFuture : Future[Option[Int]] = setupFuture >>
populateFuture
[error]                                                              ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed

The same code works, If I nest the populateFuture inside the map function of the setupFuture. But I don't want to write nested code because it will become very messy once there are more steps to do.

So I need a way to combine all futures into a single future and then execute it.

Edit:: I also tried combining the two actions

  val combinedAction = setupAction.andThen(populateAction)

  val fut1 = combinedAction.map{result =>
    result.foreach{x =>println(s"number or rows inserted $x")}
  }
  Await.result(fut1, Duration.Inf)

but got error

/Users/abhi/ScalaProjects/SlickTest/src/main/scala/com/example/Hello.scala:31: type mismatch;
[error]  found   : scala.concurrent.Future[Option[Int]]
[error]  required: PartialFunction[scala.util.Try[Unit],?]
[error]       val combinedAction = setupAction.andThen(populateAction)
[error]                                                ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 3 s, completed Jun 26, 2015 3:50:51 PM
Mohitas-MBP:SlickTest abhi$ 

Upvotes: 1

Views: 3444

Answers (1)

Roman
Roman

Reputation: 5699

According to http://slick.typesafe.com/doc/3.0.0/api/index.html#slick.dbio.DBIOAction, andThen() is what you are looking for:

val combinedAction = setupAction.andThen(populateAction)
val results = db.run(combinedAction)

populateAction will only run after setupAction completed successfully. This is crucial in your case since slick is fully non-blocking. The code you have now will cause problems at runtime. Both actions in your code will run asynchronously at the same time. There is no way to determine which action is executed first. But because populateAction depends on setupAction, you must ensure setupAction is executed first. Therefore use andThen.

Upvotes: 2

Related Questions