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

io.chrisdavenport.rediculous.RedisPipeline.scala Maven / Gradle / Ivy

The newest version!
package io.chrisdavenport.rediculous

import cats._
import cats.implicits._
import cats.data._
import cats.effect._
import scodec.bits.ByteVector

/**
 * For When you don't trust automatic pipelining. 
 * 
 * ClusterMode: Multi Key Operations Will use for the first key
 * provided.
 * 
 * [[pipeline]] method converts the Pipeline state to the Redis Monad. This will error
 * if you pipeline and have not actually enterred any redis commands.
 **/
final case class RedisPipeline[A](value: RedisTransaction.RedisTxState[RedisTransaction.Queued[A]]){
  def pipeline[F[_]: Concurrent]: Redis[F, A] = RedisPipeline.pipeline[F](this)
}

object RedisPipeline {

  implicit val ctx: RedisCtx[RedisPipeline] =  new RedisCtx[RedisPipeline]{
    def keyedBV[A: RedisResult](key: ByteVector, command: NonEmptyList[ByteVector]): RedisPipeline[A] = 
      RedisPipeline(RedisTransaction.RedisTxState{for {
        s1 <- State.get[(Int, List[NonEmptyList[ByteVector]], Option[ByteVector])]
        (i, base, value) = s1
        _ <- State.set((i + 1, command :: base, value.orElse(Some(key))))
      } yield RedisTransaction.Queued(l => RedisResult[A].decode(l(i)))})

    def unkeyedBV[A: RedisResult](command: NonEmptyList[ByteVector]): RedisPipeline[A] = RedisPipeline(RedisTransaction.RedisTxState{for {
      out <- State.get[(Int, List[NonEmptyList[ByteVector]], Option[ByteVector])]
      (i, base, value) = out
      _ <- State.set((i + 1, command :: base, value))
    } yield RedisTransaction.Queued(l => RedisResult[A].decode(l(i)))})
  }

  implicit val applicative: Applicative[RedisPipeline] = new Applicative[RedisPipeline]{
    def pure[A](a: A) = RedisPipeline(Monad[RedisTransaction.RedisTxState].pure(Monad[RedisTransaction.Queued].pure(a)))

    override def ap[A, B](ff: RedisPipeline[A => B])(fa: RedisPipeline[A]): RedisPipeline[B] =
      RedisPipeline(RedisTransaction.RedisTxState(
        Nested(ff.value.value).ap(Nested(fa.value.value)).value
      ))
  }

  val fromTransaction = new (RedisTransaction ~> RedisPipeline){
    def apply[A](fa: RedisTransaction[A]): RedisPipeline[A] = RedisPipeline(fa.value)
  }

  val toTransaction = new (RedisPipeline ~> RedisTransaction){
    def apply[A](fa: RedisPipeline[A]): RedisTransaction[A] = RedisTransaction(fa.value)
  }

  def pipeline[F[_]] = new SendPipelinePartiallyApplied[F]


  class SendPipelinePartiallyApplied[F[_]]{
    def apply[A](tx: RedisPipeline[A])(implicit F: Concurrent[F]): Redis[F, A] = {
      Redis(Kleisli{(c: RedisConnection[F]) => 
        val ((_, commandsR, key), RedisTransaction.Queued(f)) = tx.value.value.run((0, List.empty, None)).value
        val commands = commandsR.reverse.toNel
        commands.traverse(nelCommands => RedisConnection.runRequestInternal(c)(fs2.Chunk.seq(nelCommands.toList), key) // We Have to Actually Send A Command
          .flatMap{nel => 
            val l  = nel.toList
            val c = fs2.Chunk.seq(l)
            val resp = f(c)
            RedisConnection.closeReturn[F, A](resp)}
        ).flatMap{
          case Some(a) => a.pure[F]
          case None => F.raiseError(RedisError.Generic("Rediculous: Attempted to Pipeline Empty Command"))
        }
      })
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy