lila/modules/hub/src/main/AsyncActorSequencer.scala

68 lines
1.8 KiB
Scala

package lila.hub
import com.github.blemale.scaffeine.LoadingCache
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ ExecutionContext, Promise }
import lila.base.LilaTimeout
final class AsyncActorSequencer(maxSize: Int, timeout: FiniteDuration, name: String, logging: Boolean = true)(
implicit
system: akka.actor.ActorSystem,
ec: ExecutionContext
) {
import AsyncActorSequencer._
def apply[A](fu: => Fu[A]): Fu[A] = run(() => fu)
def run[A](task: Task[A]): Fu[A] = asyncActor.ask[A](TaskWithPromise(task, _))
private[this] val asyncActor =
new BoundedAsyncActor(maxSize, name, logging)({ case TaskWithPromise(task, promise) =>
promise.completeWith {
task()
.withTimeout(timeout)
.transform(
identity,
{
case LilaTimeout(msg) =>
val fullMsg = s"$name AsyncActorSequencer $msg"
if (logging) lila.log("asyncActor").warn(fullMsg)
LilaTimeout(fullMsg)
case e => e
}
)
}.future
})
}
// Distributes tasks to many sequencers
final class AsyncActorSequencers(
maxSize: Int,
expiration: FiniteDuration,
timeout: FiniteDuration,
name: String,
logging: Boolean = true
)(implicit
system: akka.actor.ActorSystem,
ec: ExecutionContext,
mode: play.api.Mode
) {
def apply[A](key: String)(task: => Fu[A]): Fu[A] =
sequencers.get(key).run(() => task)
private val sequencers: LoadingCache[String, AsyncActorSequencer] =
lila.common.LilaCache
.scaffeine(mode)
.expireAfterAccess(expiration)
.build(key => new AsyncActorSequencer(maxSize, timeout, s"$name:$key", logging))
}
object AsyncActorSequencer {
private type Task[A] = () => Fu[A]
private case class TaskWithPromise[A](task: Task[A], promise: Promise[A])
}