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

io.chrisdavenport.rediculous.cluster.ClusterCommands.scala Maven / Gradle / Ivy

The newest version!
package io.chrisdavenport.rediculous.cluster

import cats.implicits._
import io.chrisdavenport.rediculous._
import cats.data.NonEmptyList
import cats.effect._
import com.comcast.ip4s._
import scodec.bits.ByteVector
import RedisCtx.syntax.all._
import cats.effect.std.Random
import cats.MonadThrow

object ClusterCommands {

  final case class ClusterServer(host: Host, port: Port, id: String)
  object ClusterServer {
    implicit val result: RedisResult[ClusterServer] = new RedisResult[ClusterServer]{
      def decode(resp: Resp): Either[Resp,ClusterServer] = {
        def decodeBVString(bv: ByteVector): Either[Resp, String] = bv.decodeUtf8.leftMap(_ => resp)
        resp match {
          case Resp.Array(Some(Resp.BulkString(Some(hostBV)) :: Resp.Integer(l) :: Resp.BulkString(Some(idBV)) :: Nil)) => 
            for {
              hostS <- decodeBVString(hostBV)
              host <- Host.fromString(hostS).toRight(resp)
              port <- Port.fromInt(l.toInt).toRight(resp)
              id <- decodeBVString(idBV)
            } yield ClusterServer(host, port, id)
          case e => e.asLeft
        }
      }
    }
  }
  final case class ClusterSlot(start: Int, end: Int, replicas: List[ClusterServer])
  object ClusterSlot {
    
    implicit val result: RedisResult[ClusterSlot] = new RedisResult[ClusterSlot]{
      def decode(resp: Resp): Either[Resp,ClusterSlot] = resp match {
        case Resp.Array(Some(Resp.Integer(start) :: Resp.Integer(end) :: rest)) => 
          rest.traverse(RedisResult[ClusterServer].decode).map{l => 
            ClusterSlot(start.toInt, end.toInt, l)
          }
        case other => other.asLeft
      }
    }
  }
  final case class ClusterSlots(l: List[ClusterSlot]){
    def served(bucket: Int): Option[(Host, Port)] = 
      l.collectFirst{
        case ClusterSlot(start, end, master :: _) if start <= bucket && end >= bucket => (master.host, master.port) 
      }
    def random[F[_]: Sync] = Sync[F].delay{
      val base = l.flatMap(_.replicas)
      if (base.size > 0) {
        val i = scala.util.Random.nextInt(base.size)
        val server = base(i)
        Some((server.host, server.port))
      } else None
    }.flatMap{
      case Some(a) => Sync[F].pure(a)
      case None => Sync[F].raiseError[(Host, Port)](RedisError.Generic("Rediculous: No Servers Available"))
    }
  }
  object ClusterSlots {
    implicit val result: RedisResult[ClusterSlots] = new RedisResult[ClusterSlots]{
      def decode(resp: Resp): Either[Resp,ClusterSlots] = 
        RedisResult[List[ClusterSlot]].decode(resp).map(ClusterSlots(_))
    }
  }

  def clusterslots[F[_]: RedisCtx]: F[ClusterSlots] = 
    RedisCtx[F].unkeyed(NonEmptyList.of("CLUSTER", "SLOTS"))

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy