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

com.devsisters.shardcake.ShardManagerClient.scala Maven / Gradle / Ivy

package com.devsisters.shardcake

import caliban.client.Operations.IsOperation
import caliban.client.SelectionBuilder
import com.devsisters.shardcake.internal.GraphQLClient
import com.devsisters.shardcake.internal.GraphQLClient.PodAddressInput
import sttp.client3.SttpBackend
import sttp.client3.asynchttpclient.zio.AsyncHttpClientZioBackend
import zio.{ Config => _, _ }

/**
 * An interface to communicate with the Shard Manager API
 */
trait ShardManagerClient {
  def register(podAddress: PodAddress): Task[Unit]
  def unregister(podAddress: PodAddress): Task[Unit]
  def notifyUnhealthyPod(podAddress: PodAddress): Task[Unit]
  def getAssignments: Task[Map[Int, Option[PodAddress]]]
}

object ShardManagerClient {

  /**
   * A layer that returns a client for the Shard Manager API.
   * It requires an sttp backend. If you don't want to use your own backend, simply use `liveWithSttp`.
   */
  val live: ZLayer[Config with SttpBackend[Task, Any], Nothing, ShardManagerClientLive] =
    ZLayer {
      for {
        sttpClient <- ZIO.service[SttpBackend[Task, Any]]
        config     <- ZIO.service[Config]
      } yield new ShardManagerClientLive(sttpClient, config)
    }

  /**
   * A layer that returns a client for the Shard Manager API.
   * It contains its own sttp backend so you don't need to provide one.
   */
  val liveWithSttp: ZLayer[Config, Throwable, ShardManagerClient] =
    AsyncHttpClientZioBackend.layer() >>> live

  /**
   * A layer that mocks the Shard Manager, useful for testing with a single pod.
   */
  val local: ZLayer[Config, Nothing, ShardManagerClient] =
    ZLayer {
      for {
        config <- ZIO.service[Config]
        pod     = PodAddress(config.selfHost, config.shardingPort)
        shards  = (1 to config.numberOfShards).map(_ -> Some(pod)).toMap
      } yield new ShardManagerClient {
        def register(podAddress: PodAddress): Task[Unit]           = ZIO.unit
        def unregister(podAddress: PodAddress): Task[Unit]         = ZIO.unit
        def notifyUnhealthyPod(podAddress: PodAddress): Task[Unit] = ZIO.unit
        def getAssignments: Task[Map[Int, Option[PodAddress]]]     = ZIO.succeed(shards)
      }
    }

  class ShardManagerClientLive(sttp: SttpBackend[Task, Any], config: Config) extends ShardManagerClient {
    private def send[Origin: IsOperation, A](query: SelectionBuilder[Origin, A]): Task[A] =
      sttp.send(query.toRequest(config.shardManagerUri)).map(_.body).absolve

    def register(podAddress: PodAddress): Task[Unit] =
      send(
        GraphQLClient.Mutations.register(PodAddressInput(podAddress.host, podAddress.port), config.serverVersion)
      ).unit

    def unregister(podAddress: PodAddress): Task[Unit] =
      send(
        GraphQLClient.Mutations.unregister(PodAddressInput(podAddress.host, podAddress.port), config.serverVersion)
      ).unit

    def notifyUnhealthyPod(podAddress: PodAddress): Task[Unit] =
      ZIO.logWarning(s"Notifying Shard Manager about unhealthy pod $podAddress") *>
        send(GraphQLClient.Mutations.notifyUnhealthyPod(PodAddressInput(podAddress.host, podAddress.port)))

    def getAssignments: Task[Map[Int, Option[PodAddress]]] =
      send(
        GraphQLClient.Queries
          .getAssignments(
            GraphQLClient.Assignment.shardId ~ GraphQLClient.Assignment
              .pod((GraphQLClient.PodAddress.host ~ GraphQLClient.PodAddress.port).map { case (host, port) =>
                PodAddress(host, port)
              })
          )
          .map(_.toMap)
      )
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy