All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.profunktor.redis4cats.transactions.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018-2021 ProfunKtor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dev.profunktor.redis4cats

import scala.util.control.NoStackTrace

import cats.{ Applicative, ApplicativeThrow }
import cats.effect.kernel._
import cats.syntax.all._
import dev.profunktor.redis4cats.effect.Log
import dev.profunktor.redis4cats.hlist._

object transactions {

  sealed trait TransactionError extends NoStackTrace
  case object TransactionAborted extends TransactionError
  case object TransactionDiscarded extends TransactionError

  case class RedisTransaction[F[_]: Async: Log, K, V](
      cmd: RedisCommands[F, K, V]
  ) {

    private val ops =
      Runner.Ops(
        name = "Transaction",
        mainCmd = cmd.multi,
        onComplete =
          (f: Runner.CancelFibers[F]) => cmd.exec.handleErrorWith(e => f(e) >> ApplicativeThrow[F].raiseError(e)),
        onError = cmd.discard,
        afterCompletion = Applicative[F].unit,
        mkError = () => TransactionAborted
      )

    /**
      * Same as @exec, except it filters out values of type Unit
      * from its result.
      */
    def filterExec[T <: HList](commands: T)(implicit w: WitnessFilter[T]): F[w.S] =
      Runner[F].filterExec(ops)(commands)

    /***
      * Exclusively run Redis commands as part of a transaction.
      *
      * Every command needs to be forked (`.start`) to be sent to the server asynchronously.
      * After a transaction is complete, either successfully or with a failure, the spawned
      * fibers will be treated accordingly.
      *
      * It should not be used to run other computations, only Redis commands. Fail to do so
      * may end in unexpected results such as a dead lock.
      *
      * @return `F[R]` or it raises a @TransactionError in case of failure.
      */
    def exec[T <: HList](commands: T)(implicit w: Witness[T]): F[w.R] =
      Runner[F].exec(ops)(commands)

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy