endless.runtime.pekko.EntityPassivator.scala Maven / Gradle / Ivy
package endless.runtime.pekko
import org.apache.pekko.actor.Cancellable
import org.apache.pekko.actor.typed.scaladsl.ActorContext
import org.apache.pekko.cluster.sharding.typed.scaladsl.{ClusterSharding, EntityContext}
import cats.Applicative
import cats.effect.kernel.{Ref, Sync}
import cats.syntax.eq.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import endless.core.entity.Effector.PassivationState
import scala.concurrent.duration.{Duration, FiniteDuration}
private[pekko] class EntityPassivator[F[_]: Sync](upcomingPassivation: Ref[F, Option[Cancellable]])(
implicit
entityContext: EntityContext[?],
actorContext: ActorContext[?]
) {
private lazy val passivateMessage = ClusterSharding.Passivate(actorContext.self)
private lazy val passivate = Sync[F].delay(entityContext.shard.tell(passivateMessage))
private lazy val disablePassivation =
upcomingPassivation.modify(maybeCancellable => {
(None, maybeCancellable.foreach(_.cancel()))
})
def apply(passivationState: PassivationState): F[Unit] = passivationState match {
case PassivationState.After(duration) => enablePassivation(duration)
case PassivationState.Disabled => disablePassivation
case PassivationState.Unchanged => Applicative[F].unit
}
private def enablePassivation(after: FiniteDuration) =
if (after === Duration.Zero) passivate else schedulePassivation(after)
private def schedulePassivation(after: FiniteDuration) =
disablePassivation >> upcomingPassivation.set(
Some(
actorContext.scheduleOnce(after, entityContext.shard, passivateMessage)
)
)
}
object EntityPassivator {
def apply[F[_]: Sync](implicit
entityContext: EntityContext[?],
actorContext: ActorContext[?]
): F[EntityPassivator[F]] =
Ref.of[F, Option[Cancellable]](Option.empty[Cancellable]).map(new EntityPassivator(_))
}