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

io.chrisdavenport.rediculous.concurrent.RedisQueue.scala Maven / Gradle / Ivy

package io.chrisdavenport.rediculous.concurrent

import io.chrisdavenport.rediculous.RedisConnection
import cats.effect.std.Queue

import cats._
import cats.syntax.all._
import cats.effect._
import fs2.Chunk
import io.chrisdavenport.rediculous.RedisCommands
import scala.concurrent.duration.FiniteDuration
import io.chrisdavenport.rediculous.RedisPipeline
import io.chrisdavenport.rediculous.RedisCtx.syntax.all._

object RedisQueue {

  /** rpush/lpop no size bound */
  def unboundedQueue[F[_]: Async](
    redisConnection: RedisConnection[F],
    queueKey: String,
    pollingInterval: FiniteDuration
  ): Queue[F, String] = new RedisQueueUnboundedImpl[F](redisConnection, queueKey, pollingInterval,
    {s => RedisCommands.rpush(queueKey, List(s)).run(redisConnection)} 
  )

  /** lpush/lpop no size bound */
  def unboundedStack[F[_]: Async](
    redisConnection: RedisConnection[F],
    queueKey: String,
    pollingInterval: FiniteDuration
  ): Queue[F, String] = new RedisQueueUnboundedImpl[F](redisConnection, queueKey, pollingInterval,
    {s => RedisCommands.lpush(queueKey, List(s)).run(redisConnection)} 
  )

  /** rpush/lpop size bound, polls on insert when full, can overfill */
  def boundedQueue[F[_]: Async](
    redisConnection: RedisConnection[F],
    queueKey: String,
    maxSize: Long, 
    pollingInterval: FiniteDuration
  ): Queue[F, String] = new BoundedQueue[F](redisConnection, queueKey, maxSize, pollingInterval, 
    {s => RedisCommands.rpush(queueKey, List(s)).run(redisConnection)}
  )

    /** lpush/lpop size bound, polls on insert when full, can overfill */
  def boundedStack[F[_]: Async](
    redisConnection: RedisConnection[F],
    queueKey: String,
    maxSize: Long, 
    pollingInterval: FiniteDuration
  ): Queue[F, String] = new BoundedQueue[F](redisConnection, queueKey, maxSize, pollingInterval, 
    {s => RedisCommands.lpush(queueKey, List(s)).run(redisConnection)}
  )

  private class RedisQueueUnboundedImpl[F[_]: Async](
    redisConnection: RedisConnection[F],
    queueKey: String,
    pollingInterval: FiniteDuration,
    pushEntry: String => F[Long]
  ) extends Queue[F, String] {

    def offer(a: String): F[Unit] = pushEntry(a).void
    def tryOffer(a: String): F[Boolean] = offer(a).as(true)
  
    // Members declared in cats.effect.std.QueueSource
    def size: F[Int] = RedisCommands.llen(queueKey).run(redisConnection).map(_.toInt)
    def take: F[String] = tryTake.flatMap{
      case Some(s) => s.pure[F]
      case _ => Temporal[F].sleep(pollingInterval) >> take
    }
      
    def tryTake: F[Option[String]] = RedisCommands.lpop(queueKey).run(redisConnection)

  
  }

  private class BoundedQueue[F[_]: Async](
    redisConnection: RedisConnection[F],
    queueKey: String,
    maxSize: Long,
    pollingInterval: FiniteDuration,
    pushEntry: String => F[Long]
  ) extends Queue[F, String]{
    def offer(a: String): F[Unit] = tryOffer(a).ifM(
      Applicative[F].unit,
      Temporal[F].sleep(pollingInterval) >> offer(a)
    )
    
    def tryOffer(a: String): F[Boolean] = 
      RedisCommands.llen(queueKey).run(redisConnection).flatMap{
        case exists if exists >= maxSize => false.pure[F]
        case otherwise => 
          pushEntry(a).as(true)
      }

    def size: F[Int] = RedisCommands.llen(queueKey).run(redisConnection).map(_.toInt)
    
    def take: F[String] = 
      tryTake.flatMap{
        case Some(s) => s.pure[F]
        case _ => Temporal[F].sleep(pollingInterval) >> take
      }
    
    def tryTake: F[Option[String]] = 
      RedisCommands.lpop(queueKey).run(redisConnection)  
  }




}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy