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