7

I'm using Slick 3.1.1 and i would like to implement my own stuff on Slick transaction.

def findSomeProducts = table.getSomeProducts() //db operation
def productTableUpdate = doSomeStuff1() //db operation
def priceTableUpdate = doSomeStuff2() //db operation

def updateElasticCache = updateIndexOfProduct() //this is not a database operation

I have these sample functions. Firstly, I'm getting some products from db and after i'm updating the tables. At the end I need to run updateElasticCache method. If updateElasticCache method fails, I would like to rollback whole db procceses.

I can't use (for { ... } yield ()).transactionally this code because it's not applicable for my cases. This 'transactionally' is waiting a db action. But I want to add another functionality which is not a db-process.

Is it possible? How can I achieve it?

sbb
  • 529
  • 3
  • 25

1 Answers1

6

DBIO.from

Yes !!! its possible to add non db logic in between db logic in slick using DBIO actions composition and DBIO.from

Note that "your own stuff" should return a future, future can be converted to DBIO and can be composed along with usual db actions.

DBIO.from can help you with this. Here is how it works. DBIO.from takes a future and converts it into a DBIOAction. now you can compose these actions with usual db actions to do non-db operations along with db operations in a transaction.

def updateElasticCache: Future[Unit] = Future(doSomething())

now lets say we have some db actions

def createUser(user: User): DBIO[Int] = ???

I want createUser to rollback if updating cache fails. so i do the following

val action = createUser.flatMap { _ => DBIO.from(updateElasticCache()) }.transactionally
db.run(action)

now if updateElasticCache fails whole tx will fail and everything will rollback to normal state.

Example

You can use for comprehension to make it look good

def updateStats: DBIO[Int] = ???
val rollbackActions =
  (for {
     cStatus <- createUser()
     uStatus <- updateStats()
     result <- DBIO.from(updateElasticCache())
   } yield result).transactionally
 db.run(rollbackActions)

everything roll backs if updateElasticCache future fails

Nagarjuna Pamu
  • 14,737
  • 3
  • 22
  • 40
  • Thanks for your answer. I'm trying to refactor my functions. My db-operation function's return type is different from DBIO. Instead of DBIO[T], it returns JdbcProfile.this.DriverAction[Int, NoStream, Write]. ` def testUpdate(productId: Int, text: String): JdbcProfile.this.DriverAction[Int, NoStream, Write] = { val q = for { p <- Products if p.id === productId } yield (p.text) q.update(text) } ` Sorry, I've just started to learn Slick today. I don't know is it a logical question? – sbb Sep 02 '16 at 18:49
  • @sydboraa `updateElasticCache()` should return `Future[_]` and then you could wrap it in `DBIO.from(updateElasticCache())`. or `def updateElasticCache: DBIO[_] = DBIO.from { doSomething() }` `doSomething` in turn returns `Future[_]`. – Nagarjuna Pamu Sep 02 '16 at 18:52
  • @sydboraa `DBIO` is the type alias for `DriverAction` – Nagarjuna Pamu Sep 02 '16 at 18:53
  • what kind of import is necessary for .transactionally method? – michele Jul 22 '20 at 10:35