Состав Doobie и доступа к БД в пределах 1 транзакции - PullRequest
0 голосов
/ 22 мая 2018

Книга Дуби говорит, что рекомендуется возвращать ConnectionIO из уровня хранилища.Это дает возможность связывать звонки и выполнять их за одну транзакцию.Красиво и понятно.

Теперь давайте представим, что мы работаем над службой REST API, и наш сценарий:

  1. Найти объект в базе данных
  2. Выполнить некоторые асинхронные манипуляции (используя cats.effect.IO или monix.eval.Task) с этим объектом.
  3. Сохранение объекта в базе данных.

И мы хотим выполнить все эти шаги в одной транзакции.Проблема в том, что без естественной трансформации, которую нам дает transactor.trans(), мы работаем внутри 2 монад - Task и ConnectionIO.Это невозможно.

Вопрос в том, как смешать doobie ConnectionIO с любой монадой эффектов в 1 композиции, такой как мы работаем в 1 транзакции и можем зафиксировать / откатить все мутации БД в конце света?

Спасибо!

UPD: маленький пример

def getObject: ConnectionIO[Request]                      = ???
def saveObject(obj: Request): ConnectionIO[Request]       = ???
def processObject(obj: Request): monix.eval.Task[Request] = ???

val transaction:??? = for {
    obj       <- getObject             //ConnectionIO[Request]
    processed <- processObject(obj)    //monix.eval.Task[Request]
    updated   <- saveObject(processed) //ConnectionIO[Request]
  } yield updated

UPD2: правильный ответ, предоставленный @ oleg-pyzhcov, состоит в том, чтобы поднять типы данных эффекта до ConnectionIO, например так:

def getObject: ConnectionIO[Request]                      = ???
def saveObject(obj: Request): ConnectionIO[Request]       = ???
def processObject(obj: Request): monix.eval.Task[Request] = ???

val transaction: ConnectionIO[Request] = for {
    obj       <- getObject                                           //ConnectionIO[Request]
    processed <- Async[ConnectionIO].liftIO(processObject(obj).toIO) //ConnectionIO[Request]
    updated   <- saveObject(processed)                               //ConnectionIO[Request]
} yield updated
val result: Task[Request] = transaction.transact(xa)

1 Ответ

0 голосов
/ 23 мая 2018

ConnectionIO в doobie имеет cats.effect.Async экземпляр , который, помимо прочего, позволяет вам превращать любой cats.effect.IO в ConnectionIO с помощью метода liftIO:

import doobie.free.connection._
import cats.effect.{IO, Async}
val catsIO: IO[String] = ???
val cio: ConnectionIO[String] = Async[ConnectionIO].liftIO(catsIO)

Для monix.eval.Task ваша лучшая ставка - использовать Task#toIO и выполнять тот же трюк, но вам понадобится моникс Scheduler в области действия.

...