rename Trouper->SyncActor & Duct->AsyncActor

pull/9456/head
Thibault Duplessis 2021-07-23 14:08:38 +02:00
parent 5c9b22e389
commit 057495c26f
43 changed files with 230 additions and 227 deletions

View File

@ -80,7 +80,8 @@ final class ChallengeApi(
Bus.publish(Event.Decline(c declineWith reason), "challenge")
}
private val acceptQueue = new lila.hub.DuctSequencer(maxSize = 64, timeout = 5 seconds, "challengeAccept")
private val acceptQueue =
new lila.hub.AsyncActorSequencer(maxSize = 64, timeout = 5 seconds, "challengeAccept")
def accept(
c: Challenge,

View File

@ -13,7 +13,7 @@ import lila.common.Template
import lila.db.dsl._
import lila.game.{ Game, Player }
import lila.hub.actorApi.map.TellMany
import lila.hub.DuctSequencers
import lila.hub.AsyncActorSequencers
import lila.rating.PerfType
import lila.setup.SetupBulk.{ ScheduledBulk, ScheduledGame }
import lila.user.User
@ -40,7 +40,7 @@ final class ChallengeBulkApi(
private val coll = colls.bulk
private val workQueue =
new DuctSequencers(maxSize = 16, expiration = 10 minutes, timeout = 10 seconds, name = "challenge.bulk")
new AsyncActorSequencers(maxSize = 16, expiration = 10 minutes, timeout = 10 seconds, name = "challenge.bulk")
def scheduledBy(me: User): Fu[List[ScheduledBulk]] =
coll.list[ScheduledBulk]($doc("by" -> me.id))

View File

@ -169,7 +169,7 @@ object mon {
object expiration {
val count = counter("round.expiration.count").withoutTags()
}
val ductCount = gauge("round.duct.count").withoutTags()
val asyncActorCount = gauge("round.asyncActor.count").withoutTags()
}
object playban {
def outcome(out: String) = counter("playban.outcome").withTag("outcome", out)
@ -201,8 +201,8 @@ object mon {
)
)
}
object duct {
def overflow(name: String) = counter("duct.overflow").withTag("name", name)
object asyncActor {
def overflow(name: String) = counter("asyncActor.overflow").withTag("name", name)
}
object irc {
object zulip {

View File

@ -20,7 +20,7 @@ final class Analyser(
val maxPlies = 200
private val workQueue = new lila.hub.DuctSequencer(maxSize = 256, timeout = 5 seconds, "fishnetAnalyser")
private val workQueue = new lila.hub.AsyncActorSequencer(maxSize = 256, timeout = 5 seconds, "fishnetAnalyser")
def apply(game: Game, sender: Work.Sender): Fu[Boolean] =
(game.metadata.analysed ?? analysisRepo.exists(game.id)) flatMap {

View File

@ -27,7 +27,7 @@ final class FishnetApi(
import JsonApi.Request.{ CompleteAnalysis, PartialAnalysis }
import BSONHandlers._
private val workQueue = new lila.hub.DuctSequencer(maxSize = 256, timeout = 5 seconds, name = "fishnetApi")
private val workQueue = new lila.hub.AsyncActorSequencer(maxSize = 256, timeout = 5 seconds, name = "fishnetApi")
def keyExists(key: Client.Key) = repo.getEnabledClient(key).map(_.isDefined)

View File

@ -11,11 +11,11 @@ import scala.concurrent.{ ExecutionContext, Promise }
final class AskPipeline[A](compute: () => Fu[A], timeout: FiniteDuration, name: String)(implicit
system: akka.actor.ActorSystem,
ec: scala.concurrent.ExecutionContext
) extends Trouper {
) extends SyncActor {
private var state: State = Idle
protected val process: Trouper.Receive = {
protected val process: SyncActor.Receive = {
case Get(promise) =>
state match {

View File

@ -9,9 +9,9 @@ import scala.concurrent.Promise
* Sequential like an actor, but for async functions,
* and using an atomic backend instead of akka actor.
*/
abstract class Duct(implicit ec: scala.concurrent.ExecutionContext) extends lila.common.Tellable {
abstract class AsyncActor(implicit ec: scala.concurrent.ExecutionContext) extends lila.common.Tellable {
import Duct._
import AsyncActor._
// implement async behaviour here
protected val process: ReceiveAsync
@ -33,13 +33,13 @@ abstract class Duct(implicit ec: scala.concurrent.ExecutionContext) extends lila
private[this] val stateRef: AtomicReference[State] = new AtomicReference(None)
private[this] def run(msg: Any): Unit =
process.applyOrElse(msg, Duct.fallback) onComplete postRun
process.applyOrElse(msg, AsyncActor.fallback) onComplete postRun
private[this] val postRun = (_: Any) =>
stateRef.getAndUpdate(postRunUpdate) flatMap (_.headOption) foreach run
}
object Duct {
object AsyncActor {
type ReceiveAsync = PartialFunction[Any, Fu[Any]]
@ -53,7 +53,7 @@ object Duct {
}
private val fallback = { msg: Any =>
lila.log("Duct").warn(s"unhandled msg: $msg")
lila.log("asyncActor").warn(s"unhandled msg: $msg")
funit
}
}

View File

@ -6,21 +6,21 @@ import ornicar.scalalib.Zero
import scala.concurrent.{ ExecutionContext, Promise }
import scala.jdk.CollectionConverters._
final class DuctConcMap[D <: Duct](
mkDuct: String => D,
final class AsyncActorConcMap[D <: AsyncActor](
mkAsyncActor: String => D,
initialCapacity: Int
) extends TellMap {
def getOrMake(id: String): D = ducts.computeIfAbsent(id, loadFunction)
def getOrMake(id: String): D = asyncActors.computeIfAbsent(id, loadFunction)
def getIfPresent(id: String): Option[D] = Option(ducts get id)
def getIfPresent(id: String): Option[D] = Option(asyncActors get id)
def tell(id: String, msg: Any): Unit = getOrMake(id) ! msg
def tellIfPresent(id: String, msg: => Any): Unit = getIfPresent(id) foreach (_ ! msg)
def tellAll(msg: Any) =
ducts.forEachValue(16, _ ! msg)
asyncActors.forEachValue(16, _ ! msg)
def tellIds(ids: Seq[String], msg: Any): Unit = ids foreach { tell(_, msg) }
@ -34,21 +34,21 @@ final class DuctConcMap[D <: Duct](
def askIfPresentOrZero[A: Zero](id: String)(makeMsg: Promise[A] => Any): Fu[A] =
askIfPresent(id)(makeMsg) dmap (~_)
def exists(id: String): Boolean = ducts.get(id) != null
def exists(id: String): Boolean = asyncActors.get(id) != null
def foreachKey(f: String => Unit): Unit =
ducts.forEachKey(16, k => f(k))
asyncActors.forEachKey(16, k => f(k))
def tellAllWithAck(makeMsg: Promise[Unit] => Any)(implicit ec: ExecutionContext): Fu[Int] =
ducts.values.asScala
asyncActors.values.asScala
.map(_ ask makeMsg)
.sequenceFu
.map(_.size)
def size: Int = ducts.size()
def size: Int = asyncActors.size()
def terminate(id: String, lastWill: Duct => Unit): Unit =
ducts
def terminate(id: String, lastWill: AsyncActor => Unit): Unit =
asyncActors
.computeIfPresent(
id,
(_, d) => {
@ -58,10 +58,10 @@ final class DuctConcMap[D <: Duct](
)
.unit
private[this] val ducts = new ConcurrentHashMap[String, D](initialCapacity)
private[this] val asyncActors = new ConcurrentHashMap[String, D](initialCapacity)
private val loadFunction = new Function[String, D] {
def apply(k: String) = mkDuct(k)
def apply(k: String) = mkAsyncActor(k)
}
// used to remove entries

View File

@ -0,0 +1,67 @@
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])
}

View File

@ -9,11 +9,13 @@ import scala.concurrent.Promise
* Sequential like an actor, but for async functions,
* and using an atomic backend instead of akka actor.
*/
final class BoundedDuct(maxSize: Int, name: String, logging: Boolean = true)(process: Duct.ReceiveAsync)(
implicit ec: scala.concurrent.ExecutionContext
final class BoundedAsyncActor(maxSize: Int, name: String, logging: Boolean = true)(
process: AsyncActor.ReceiveAsync
)(implicit
ec: scala.concurrent.ExecutionContext
) {
import BoundedDuct._
import BoundedAsyncActor._
def !(msg: Any): Boolean =
stateRef.getAndUpdate { state =>
@ -30,8 +32,8 @@ final class BoundedDuct(maxSize: Int, name: String, logging: Boolean = true)(pro
case Some(q) =>
val success = q.size < maxSize
if (!success) {
lila.mon.duct.overflow(name).increment()
if (logging) lila.log("duct").warn(s"[$name] queue is full ($maxSize)")
lila.mon.asyncActor.overflow(name).increment()
if (logging) lila.log("asyncActor").warn(s"[$name] queue is full ($maxSize)")
}
success
}
@ -39,7 +41,7 @@ final class BoundedDuct(maxSize: Int, name: String, logging: Boolean = true)(pro
def ask[A](makeMsg: Promise[A] => Any): Fu[A] = {
val promise = Promise[A]()
val success = this ! makeMsg(promise)
if (!success) promise failure new EnqueueException(s"The $name duct queue is full ($maxSize)")
if (!success) promise failure new EnqueueException(s"The $name asyncActor queue is full ($maxSize)")
promise.future
}
@ -59,12 +61,12 @@ final class BoundedDuct(maxSize: Int, name: String, logging: Boolean = true)(pro
stateRef.getAndUpdate(postRunUpdate) flatMap (_.headOption) foreach run
private[this] lazy val fallback = { msg: Any =>
lila.log("duct").warn(s"[$name] unhandled msg: $msg")
lila.log("asyncActor").warn(s"[$name] unhandled msg: $msg")
funit
}
}
object BoundedDuct {
object BoundedAsyncActor {
final class EnqueueException(msg: String) extends Exception(msg)

View File

@ -1,66 +0,0 @@
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 DuctSequencer(maxSize: Int, timeout: FiniteDuration, name: String, logging: Boolean = true)(
implicit
system: akka.actor.ActorSystem,
ec: ExecutionContext
) {
import DuctSequencer._
def apply[A](fu: => Fu[A]): Fu[A] = run(() => fu)
def run[A](task: Task[A]): Fu[A] = duct.ask[A](TaskWithPromise(task, _))
private[this] val duct = new BoundedDuct(maxSize, name, logging)({ case TaskWithPromise(task, promise) =>
promise.completeWith {
task()
.withTimeout(timeout)
.transform(
identity,
{
case LilaTimeout(msg) =>
val fullMsg = s"$name DuctSequencer $msg"
if (logging) lila.log("duct").warn(fullMsg)
LilaTimeout(fullMsg)
case e => e
}
)
}.future
})
}
// Distributes tasks to many sequencers
final class DuctSequencers(
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, DuctSequencer] =
lila.common.LilaCache
.scaffeine(mode)
.expireAfterAccess(expiration)
.build(key => new DuctSequencer(maxSize, timeout, s"$name:$key", logging))
}
object DuctSequencer {
private type Task[A] = () => Fu[A]
private case class TaskWithPromise[A](task: Task[A], promise: Promise[A])
}

View File

@ -9,11 +9,10 @@ import scala.concurrent.{ ExecutionContext, Future, Promise }
* Like an actor, but not an actor.
* Uses an Atomic Reference backend for sequentiality.
* Has an unbounded (!) Queue of messages.
* Like Duct, but for synchronous message processors.
*/
abstract class Trouper(implicit ec: ExecutionContext) extends lila.common.Tellable {
abstract class SyncActor(implicit ec: ExecutionContext) extends lila.common.Tellable {
import Trouper._
import SyncActor._
// implement async behaviour here
protected val process: Receive
@ -58,7 +57,7 @@ abstract class Trouper(implicit ec: ExecutionContext) extends lila.common.Tellab
}
}
object Trouper {
object SyncActor {
type Receive = PartialFunction[Any, Unit]
@ -72,7 +71,7 @@ object Trouper {
}
def stub(implicit ec: ExecutionContext) =
new Trouper {
new SyncActor {
val process: Receive = { case msg =>
lila.log("trouper").warn(s"stub trouper received: $msg")
}

View File

@ -8,20 +8,20 @@ import scala.concurrent.duration.FiniteDuration
import scala.concurrent.Promise
import scala.jdk.CollectionConverters._
final class TrouperMap[T <: Trouper](
mkTrouper: String => T,
final class SyncActorMap[T <: SyncActor](
mkActor: String => T,
accessTimeout: FiniteDuration
)(implicit mode: Mode) {
def getOrMake(id: String): T = troupers get id
def getOrMake(id: String): T = actors get id
def getIfPresent(id: String): Option[T] = Option(troupers getIfPresent id)
def getIfPresent(id: String): Option[T] = Option(actors getIfPresent id)
def tell(id: String, msg: Any): Unit = getOrMake(id) ! msg
def tellIfPresent(id: String, msg: => Any): Unit = getIfPresent(id) foreach (_ ! msg)
def tellAll(msg: Any) = troupers.asMap.asScala.foreach(_._2 ! msg)
def tellAll(msg: Any) = actors.asMap.asScala.foreach(_._2 ! msg)
def tellIds(ids: Seq[String], msg: Any): Unit = ids foreach { tell(_, msg) }
@ -35,32 +35,32 @@ final class TrouperMap[T <: Trouper](
def askIfPresentOrZero[A: Zero](id: String)(makeMsg: Promise[A] => Any): Fu[A] =
askIfPresent(id)(makeMsg) dmap (~_)
def exists(id: String): Boolean = troupers.getIfPresent(id) != null
def exists(id: String): Boolean = actors.getIfPresent(id) != null
def size: Int = troupers.estimatedSize().toInt
def size: Int = actors.estimatedSize().toInt
def kill(id: String): Unit = troupers invalidate id
def kill(id: String): Unit = actors invalidate id
def killAll(): Unit = troupers.invalidateAll()
def killAll(): Unit = actors.invalidateAll()
def touch(id: String): Unit = troupers.getIfPresent(id).unit
def touch(id: String): Unit = actors.getIfPresent(id).unit
def touchOrMake(id: String): Unit = troupers.get(id).unit
def touchOrMake(id: String): Unit = actors.get(id).unit
private[this] val troupers: LoadingCache[String, T] =
private[this] val actors: LoadingCache[String, T] =
lila.common.LilaCache
.caffeine(mode)
.recordStats
.expireAfterAccess(accessTimeout.toMillis, TimeUnit.MILLISECONDS)
.removalListener(new RemovalListener[String, T] {
def onRemoval(id: String, trouper: T, cause: RemovalCause): Unit =
trouper.stop()
def onRemoval(id: String, actor: T, cause: RemovalCause): Unit =
actor.stop()
})
.build[String, T](new CacheLoader[String, T] {
def load(id: String): T = mkTrouper(id)
def load(id: String): T = mkActor(id)
})
def monitor(name: String) = lila.mon.caffeineStats(troupers, name)
def monitor(name: String) = lila.mon.caffeineStats(actors, name)
def keys: Set[String] = troupers.asMap.asScala.keySet.toSet
def keys: Set[String] = actors.asMap.asScala.keySet.toSet
}

View File

@ -23,7 +23,7 @@ final private class InsightIndexer(
) {
private val workQueue =
new lila.hub.DuctSequencer(maxSize = 128, timeout = 2 minutes, name = "insightIndexer")
new lila.hub.AsyncActorSequencer(maxSize = 128, timeout = 2 minutes, name = "insightIndexer")
def all(userId: User.ID): Funit =
workQueue {

View File

@ -5,7 +5,7 @@ import lila.game.{ Pov, Source }
final private class AbortListener(
userRepo: lila.user.UserRepo,
seekApi: SeekApi,
lobbyTrouper: LobbyTrouper
lobbyTrouper: LobbySyncActor
)(implicit ec: scala.concurrent.ExecutionContext) {
def apply(pov: Pov): Funit =

View File

@ -8,7 +8,7 @@ import scala.concurrent.duration._
import lila.common.Bus
final class BoardApiHookStream(
trouper: LobbyTrouper
trouper: LobbySyncActor
)(implicit ec: scala.concurrent.ExecutionContext, system: ActorSystem) {
private case object SetOnline

View File

@ -38,11 +38,11 @@ final class Env(
lazy val boardApiHookStream = wire[BoardApiHookStream]
private lazy val lobbyTrouper = LobbyTrouper.start(
private lazy val lobbySyncActor = LobbySyncActor.start(
broomPeriod = 2 seconds,
resyncIdsPeriod = 25 seconds
) { () =>
wire[LobbyTrouper]
wire[LobbySyncActor]
}
private lazy val abortListener = wire[AbortListener]

View File

@ -7,7 +7,7 @@ import scala.concurrent.Promise
import lila.game.Pov
import lila.hub.actorApi.timeline._
import lila.hub.Trouper
import lila.hub.SyncActor
import lila.i18n.defaultLang
import lila.pool.{ PoolApi, PoolConfig }
import lila.rating.RatingRange
@ -22,7 +22,7 @@ final class LobbySocket(
biter: Biter,
userRepo: lila.user.UserRepo,
remoteSocketApi: lila.socket.RemoteSocket,
lobby: LobbyTrouper,
lobby: LobbySyncActor,
relationApi: lila.relation.RelationApi,
poolApi: PoolApi,
system: akka.actor.ActorSystem
@ -35,14 +35,14 @@ final class LobbySocket(
private var lastCounters = LobbyCounters(0, 0)
def counters = lastCounters
val trouper: Trouper = new Trouper {
val trouper: SyncActor = new SyncActor {
private val members = scala.collection.mutable.AnyRefMap.empty[SriStr, Member]
private val idleSris = collection.mutable.Set[SriStr]()
private val hookSubscriberSris = collection.mutable.Set[SriStr]()
private val removedHookIds = new collection.mutable.StringBuilder(1024)
val process: Trouper.Receive = {
val process: SyncActor.Receive = {
case GetMember(sri, promise) => promise success members.get(sri.value)
@ -141,7 +141,7 @@ final class LobbySocket(
}
// solve circular reference
lobby ! LobbyTrouper.SetSocket(trouper)
lobby ! LobbySyncActor.SetSocket(trouper)
private val poolLimitPerSri = new lila.memo.RateLimit[SriStr](
credits = 14,

View File

@ -9,11 +9,11 @@ import scala.concurrent.Promise
import lila.common.config.Max
import lila.common.{ AtMost, Bus, Every }
import lila.game.Game
import lila.hub.Trouper
import lila.hub.SyncActor
import lila.socket.Socket.{ Sri, Sris }
import lila.user.User
final private class LobbyTrouper(
final private class LobbySyncActor(
seekApi: SeekApi,
biter: Biter,
gameCache: lila.game.Cached,
@ -22,17 +22,17 @@ final private class LobbyTrouper(
poolApi: lila.pool.PoolApi,
onStart: lila.round.OnStart
)(implicit ec: scala.concurrent.ExecutionContext)
extends Trouper {
extends SyncActor {
import LobbyTrouper._
import LobbySyncActor._
private val hookRepo = new HookRepo
private var remoteDisconnectAllAt = DateTime.now
private var socket: Trouper = Trouper.stub
private var socket: SyncActor = SyncActor.stub
val process: Trouper.Receive = {
val process: SyncActor.Receive = {
// solve circular reference
case SetSocket(trouper) => socket = trouper
@ -194,9 +194,9 @@ final private class LobbyTrouper(
}
}
private object LobbyTrouper {
private object LobbySyncActor {
case class SetSocket(trouper: Trouper)
case class SetSocket(trouper: SyncActor)
private case class Tick(promise: Promise[Unit])
@ -206,7 +206,7 @@ private object LobbyTrouper {
broomPeriod: FiniteDuration,
resyncIdsPeriod: FiniteDuration
)(
makeTrouper: () => LobbyTrouper
makeTrouper: () => LobbySyncActor
)(implicit ec: scala.concurrent.ExecutionContext, system: akka.actor.ActorSystem) = {
val trouper = makeTrouper()
Bus.subscribe(trouper, "lobbyTrouper")

View File

@ -16,7 +16,7 @@ final class PerfStatIndexer(
) {
private val workQueue =
new lila.hub.DuctSequencer(maxSize = 64, timeout = 10 seconds, name = "perfStatIndexer")
new lila.hub.AsyncActorSequencer(maxSize = 64, timeout = 10 seconds, name = "perfStatIndexer")
private[perfStat] def userPerf(user: User, perfType: PerfType): Fu[PerfStat] =
workQueue {

View File

@ -18,7 +18,7 @@ final private class GameStarter(
import PoolApi._
private val workQueue = new lila.hub.DuctSequencer(maxSize = 32, timeout = 10 seconds, name = "gameStarter")
private val workQueue = new lila.hub.AsyncActorSequencer(maxSize = 32, timeout = 10 seconds, name = "gameStarter")
def apply(pool: PoolConfig, couples: Vector[MatchMaking.Couple]): Funit =
couples.nonEmpty ?? {

View File

@ -22,7 +22,7 @@ final private class FirebasePush(
) {
private val workQueue =
new lila.hub.DuctSequencer(maxSize = 512, timeout = 10 seconds, name = "firebasePush")
new lila.hub.AsyncActorSequencer(maxSize = 512, timeout = 10 seconds, name = "firebasePush")
def apply(userId: User.ID, data: => PushApi.Data): Funit =
credentialsOpt ?? { creds =>

View File

@ -60,7 +60,7 @@ final class PuzzleApi(
object vote {
private val sequencer =
new lila.hub.DuctSequencers(
new lila.hub.AsyncActorSequencers(
maxSize = 16,
expiration = 5 minutes,
timeout = 3 seconds,

View File

@ -22,7 +22,7 @@ final private[puzzle] class PuzzleFinisher(
import BsonHandlers._
private val sequencer =
new lila.hub.DuctSequencers(
new lila.hub.AsyncActorSequencers(
maxSize = 64,
expiration = 5 minutes,
timeout = 5 seconds,

View File

@ -47,7 +47,7 @@ final class RacerApi(colls: RacerColls, selector: StormSelector, userRepo: UserR
}
private val rematchQueue =
new lila.hub.DuctSequencer(
new lila.hub.AsyncActorSequencer(
maxSize = 32,
timeout = 20 seconds,
name = "racer.rematch"

View File

@ -17,7 +17,7 @@ final class RacerLobby(api: RacerApi)(implicit ec: ExecutionContext, system: akk
}
private val workQueue =
new lila.hub.DuctSequencer(
new lila.hub.AsyncActorSequencer(
maxSize = 128,
timeout = 20 seconds,
name = "racer.lobby"

View File

@ -536,7 +536,7 @@ final class ReportApi(
object inquiries {
private val workQueue =
new lila.hub.DuctSequencer(
new lila.hub.AsyncActorSequencer(
maxSize = 32,
timeout = 20 seconds,
name = "report.inquiries"

View File

@ -2,7 +2,7 @@ package lila.room
import lila.chat.{ BusChan, Chat, ChatApi, ChatTimeout, UserLine }
import lila.hub.actorApi.shutup.PublicSource
import lila.hub.{ Trouper, TrouperMap }
import lila.hub.{ SyncActor, SyncActorMap }
import lila.log.Logger
import lila.socket.RemoteSocket.{ Protocol => P, _ }
import lila.socket.Socket.{ makeMessage, GetVersion, SocketVersion }
@ -22,11 +22,11 @@ object RoomSocket {
final class RoomState(roomId: RoomId, send: Send)(implicit
ec: ExecutionContext
) extends Trouper {
) extends SyncActor {
private var version = SocketVersion(0)
val process: Trouper.Receive = {
val process: SyncActor.Receive = {
case GetVersion(promise) => promise success version
case SetVersion(v) => version = v
case nv: NotifyVersion[_] =>
@ -48,8 +48,8 @@ object RoomSocket {
ec: ExecutionContext,
mode: play.api.Mode
) =
new TrouperMap(
mkTrouper = roomId =>
new SyncActorMap(
mkActor = roomId =>
new RoomState(
RoomId(roomId),
send
@ -58,7 +58,7 @@ object RoomSocket {
)
def roomHandler(
rooms: TrouperMap[RoomState],
rooms: SyncActorMap[RoomState],
chat: ChatApi,
logger: Logger,
publicSource: RoomId => PublicSource.type => Option[PublicSource],
@ -93,7 +93,7 @@ object RoomSocket {
}
}: Handler) orElse minRoomHandler(rooms, logger)
def minRoomHandler(rooms: TrouperMap[RoomState], logger: Logger): Handler = {
def minRoomHandler(rooms: SyncActorMap[RoomState], logger: Logger): Handler = {
case Protocol.In.KeepAlives(roomIds) =>
roomIds foreach { roomId =>
rooms touchOrMake roomId.value
@ -109,7 +109,7 @@ object RoomSocket {
private val chatMsgs = Set("message", "chat_timeout", "chat_reinstate")
def subscribeChat(rooms: TrouperMap[RoomState], busChan: BusChan.Select) = {
def subscribeChat(rooms: SyncActorMap[RoomState], busChan: BusChan.Select) = {
import lila.chat.actorApi._
lila.common.Bus.subscribeFun(busChan(BusChan).chan, BusChan.Global.chan) {
case ChatLine(id, line: UserLine) =>

View File

@ -84,7 +84,7 @@ final class Env(
private lazy val proxyDependencies =
new GameProxy.Dependencies(gameRepo, scheduler)
private lazy val roundDependencies = wire[RoundDuct.Dependencies]
private lazy val roundDependencies = wire[RoundAsyncActor.Dependencies]
lazy val roundSocket: RoundSocket = wire[RoundSocket]
@ -159,7 +159,7 @@ final class Env(
lazy val getSocketStatus = (game: Game) => roundSocket.rounds.ask[SocketStatus](game.id)(GetSocketStatus)
private def isUserPresent(game: Game, userId: lila.user.User.ID): Fu[Boolean] =
roundSocket.rounds.askIfPresentOrZero[Boolean](game.id)(RoundDuct.HasUserId(userId, _))
roundSocket.rounds.askIfPresentOrZero[Boolean](game.id)(RoundAsyncActor.HasUserId(userId, _))
lazy val jsonView = wire[JsonView]

View File

@ -107,7 +107,7 @@ private object GameProxy {
val scheduler: Scheduler
)
// must be way under the round duct termination delay (60s)
// must be way under the round asyncActor termination delay (60s)
private val scheduleDelay = 30.seconds
private val emptyCancellable = new Cancellable {

View File

@ -21,7 +21,7 @@ final private class Player(
private case object Flagged extends MoveResult
private case class MoveApplied(progress: Progress, move: MoveOrDrop) extends MoveResult
private[round] def human(play: HumanPlay, round: RoundDuct)(
private[round] def human(play: HumanPlay, round: RoundAsyncActor)(
pov: Pov
)(implicit proxy: GameProxy): Fu[Events] =
play match {
@ -47,7 +47,7 @@ final private class Player(
}
}
private[round] def bot(uci: Uci, round: RoundDuct)(pov: Pov)(implicit proxy: GameProxy): Fu[Events] =
private[round] def bot(uci: Uci, round: RoundAsyncActor)(pov: Pov)(implicit proxy: GameProxy): Fu[Events] =
pov match {
case Pov(game, _) if game.turns > Game.maxPlies =>
round ! TooManyPlies
@ -67,7 +67,7 @@ final private class Player(
}
private def postHumanOrBotPlay(
round: RoundDuct,
round: RoundAsyncActor,
pov: Pov,
progress: Progress,
moveOrDrop: MoveOrDrop
@ -110,7 +110,7 @@ final private class Player(
)
)
private[round] def requestFishnet(game: Game, round: RoundDuct): Funit =
private[round] def requestFishnet(game: Game, round: RoundAsyncActor): Funit =
game.playableByAi ?? {
if (game.turns <= fishnetPlayer.maxPlies) fishnetPlayer(game)
else fuccess(round ! actorApi.round.ResignAi)

View File

@ -21,23 +21,23 @@ import lila.hub.actorApi.round.{
RematchYes,
Resign
}
import lila.hub.Duct
import lila.hub.AsyncActor
import lila.room.RoomSocket.{ Protocol => RP, _ }
import lila.socket.Socket.{ makeMessage, GetVersion, SocketVersion }
import lila.socket.UserLagCache
import lila.user.User
final private[round] class RoundDuct(
dependencies: RoundDuct.Dependencies,
final private[round] class RoundAsyncActor(
dependencies: RoundAsyncActor.Dependencies,
gameId: Game.ID,
socketSend: String => Unit
)(implicit
ec: scala.concurrent.ExecutionContext,
proxy: GameProxy
) extends Duct {
) extends AsyncActor {
import RoundSocket.Protocol
import RoundDuct._
import RoundAsyncActor._
import dependencies._
private var takebackSituation: Option[TakebackSituation] = None
@ -109,7 +109,7 @@ final private[round] class RoundDuct(
def getGame: Fu[Option[Game]] = proxy.game
def updateGame(f: Game => Game): Funit = proxy update f
val process: Duct.ReceiveAsync = {
val process: AsyncActor.ReceiveAsync = {
case SetGameInfo(game, (whiteGoneWeight, blackGoneWeight)) =>
fuccess {
@ -541,7 +541,7 @@ final private[round] class RoundDuct(
def roomId = RoomId(gameId)
}
object RoundDuct {
object RoundAsyncActor {
case class HasUserId(userId: User.ID, promise: Promise[Boolean])
case class SetGameInfo(game: lila.game.Game, goneWeights: (Float, Float))

View File

@ -17,7 +17,7 @@ import lila.hub.actorApi.map.{ Exists, Tell, TellAll, TellIfExists, TellMany }
import lila.hub.actorApi.round.{ Abort, Berserk, RematchNo, RematchYes, Resign, TourStanding }
import lila.hub.actorApi.socket.remote.TellSriIn
import lila.hub.actorApi.tv.TvSelect
import lila.hub.DuctConcMap
import lila.hub.AsyncActorConcMap
import lila.room.RoomSocket.{ Protocol => RP, _ }
import lila.socket.RemoteSocket.{ Protocol => P, _ }
import lila.socket.Socket.{ makeMessage, SocketVersion }
@ -25,7 +25,7 @@ import lila.user.User
final class RoundSocket(
remoteSocketApi: lila.socket.RemoteSocket,
roundDependencies: RoundDuct.Dependencies,
roundDependencies: RoundAsyncActor.Dependencies,
proxyDependencies: GameProxy.Dependencies,
scheduleExpiration: ScheduleExpiration,
tournamentActor: lila.hub.actors.TournamentApi,
@ -43,8 +43,8 @@ final class RoundSocket(
Lilakka.shutdown(shutdown, _.PhaseServiceUnbind, "Stop round socket") { () =>
stopping = true
rounds.tellAllWithAck(RoundDuct.LilaStop.apply) map { nb =>
Lilakka.logger.info(s"$nb round ducts have stopped")
rounds.tellAllWithAck(RoundAsyncActor.LilaStop.apply) map { nb =>
Lilakka.logger.info(s"$nb round asyncActors have stopped")
}
}
@ -69,24 +69,24 @@ final class RoundSocket(
_ updateGame f
}
val rounds = new DuctConcMap[RoundDuct](
mkDuct = id => {
val rounds = new AsyncActorConcMap[RoundAsyncActor](
mkAsyncActor = id => {
val proxy = new GameProxy(id, proxyDependencies)
val duct = new RoundDuct(
val asyncActor = new RoundAsyncActor(
dependencies = roundDependencies,
gameId = id,
socketSend = sendForGameId(id)
)(ec, proxy)
terminationDelay schedule Game.Id(id)
duct.getGame dforeach {
asyncActor.getGame dforeach {
_ foreach { game =>
scheduleExpiration(game)
goneWeightsFor(game) dforeach { w =>
duct ! RoundDuct.SetGameInfo(game, w)
asyncActor ! RoundAsyncActor.SetGameInfo(game, w)
}
}
}
duct
asyncActor
},
initialCapacity = 65536
)
@ -150,16 +150,16 @@ final class RoundSocket(
case P.In.Ping(id) => send(P.Out.pong(id))
case P.In.WsBoot =>
logger.warn("Remote socket boot")
// schedule termination for all game ducts
// schedule termination for all game asyncActors
// until players actually reconnect
rounds foreachKey { id =>
terminationDelay schedule Game.Id(id)
}
rounds.tellAll(RoundDuct.WsBoot)
rounds.tellAll(RoundAsyncActor.WsBoot)
}
private def finishRound(gameId: Game.Id): Unit =
rounds.terminate(gameId.value, _ ! RoundDuct.Stop)
rounds.terminate(gameId.value, _ ! RoundAsyncActor.Stop)
private lazy val send: Sender = remoteSocketApi.makeSender("r-out", parallelism = 8)
@ -204,10 +204,10 @@ final class RoundSocket(
}
system.scheduler.scheduleWithFixedDelay(25 seconds, tickInterval) { () =>
rounds.tellAll(RoundDuct.Tick)
rounds.tellAll(RoundAsyncActor.Tick)
}
system.scheduler.scheduleWithFixedDelay(60 seconds, 60 seconds) { () =>
lila.mon.round.ductCount.update(rounds.size).unit
lila.mon.round.asyncActorCount.update(rounds.size).unit
}
private val terminationDelay = new TerminationDelay(system.scheduler, 1 minute, finishRound)

View File

@ -5,7 +5,7 @@ import lila.common.Bus
import lila.game.{ Event, Game, GameRepo, Pov, Progress, Rewind, UciMemo }
import lila.pref.{ Pref, PrefApi }
import lila.i18n.{ I18nKeys => trans, defaultLang }
import RoundDuct.TakebackSituation
import RoundAsyncActor.TakebackSituation
final private class Takebacker(
messenger: Messenger,

View File

@ -30,7 +30,7 @@ final class SimulApi(
) {
private val workQueue =
new lila.hub.DuctSequencers(
new lila.hub.AsyncActorSequencers(
maxSize = 128,
expiration = 10 minutes,
timeout = 10 seconds,

View File

@ -3,7 +3,7 @@ package lila.study
import ornicar.scalalib.Zero
import scala.concurrent.duration._
import lila.hub.DuctSequencers
import lila.hub.AsyncActorSequencers
final private class StudySequencer(
studyRepo: StudyRepo,
@ -15,7 +15,7 @@ final private class StudySequencer(
) {
private val workQueue =
new DuctSequencers(maxSize = 64, expiration = 1 minute, timeout = 10 seconds, name = "study")
new AsyncActorSequencers(maxSize = 64, expiration = 1 minute, timeout = 10 seconds, name = "study")
def sequenceStudy[A: Zero](studyId: Study.Id)(f: Study => Fu[A]): Fu[A] =
workQueue(studyId.value) {

View File

@ -140,7 +140,7 @@ final class StudyTopicApi(topicRepo: StudyTopicRepo, userTopicRepo: StudyUserTop
private def docTopic(doc: Bdoc): Option[StudyTopic] =
doc.getAsOpt[StudyTopic]("_id")
private val recomputeWorkQueue = new lila.hub.DuctSequencer(
private val recomputeWorkQueue = new lila.hub.AsyncActorSequencer(
maxSize = 1,
timeout = 61 seconds,
name = "studyTopicAggregation",
@ -149,7 +149,7 @@ final class StudyTopicApi(topicRepo: StudyTopicRepo, userTopicRepo: StudyUserTop
def recompute(): Unit =
recomputeWorkQueue(Future.makeItLast(60 seconds)(recomputeNow)).recover {
case _: lila.hub.BoundedDuct.EnqueueException => ()
case _: lila.hub.BoundedAsyncActor.EnqueueException => ()
case e: Exception => logger.warn("Can't recompute study topics!", e)
}.unit

View File

@ -39,7 +39,7 @@ final class SwissApi(
) {
private val sequencer =
new lila.hub.DuctSequencers(
new lila.hub.AsyncActorSequencers(
maxSize = 1024, // queue many game finished events
expiration = 20 minutes,
timeout = 10 seconds,

View File

@ -48,7 +48,7 @@ final class TournamentApi(
) {
private val workQueue =
new lila.hub.DuctSequencers(
new lila.hub.AsyncActorSequencers(
maxSize = 256,
expiration = 1 minute,
timeout = 10 seconds,

View File

@ -6,18 +6,18 @@ import scala.concurrent.Promise
import lila.common.LightUser
import lila.game.Game
import lila.hub.Trouper
import lila.hub.SyncActor
final private[tv] class ChannelTrouper(
final private[tv] class ChannelSyncActor(
channel: Tv.Channel,
onSelect: TvTrouper.Selected => Unit,
onSelect: TvSyncActor.Selected => Unit,
proxyGame: Game.ID => Fu[Option[Game]],
rematchOf: Game.ID => Option[Game.ID],
lightUserSync: LightUser.GetterSync
)(implicit ec: scala.concurrent.ExecutionContext)
extends Trouper {
extends SyncActor {
import ChannelTrouper._
import ChannelSyncActor._
// games featured on this channel
// first entry is the current game
@ -30,7 +30,7 @@ final private[tv] class ChannelTrouper(
private val candidateIds = new lila.memo.ExpireSetMemo(3 minutes)
protected val process: Trouper.Receive = {
protected val process: SyncActor.Receive = {
case GetGameId(promise) => promise success oneId
@ -39,10 +39,10 @@ final private[tv] class ChannelTrouper(
case GetGameIds(max, promise) => promise success manyIds.take(max)
case SetGame(game) =>
onSelect(TvTrouper.Selected(channel, game))
onSelect(TvSyncActor.Selected(channel, game))
history = game.id :: history.take(2)
case TvTrouper.Select =>
case TvSyncActor.Select =>
candidateIds.keys
.map(proxyGame)
.sequenceFu
@ -110,7 +110,7 @@ final private[tv] class ChannelTrouper(
.flatMap(Tv.titleScores.get)
}
object ChannelTrouper {
object ChannelSyncActor {
case class GetGameId(promise: Promise[Option[Game.ID]])
case class GetGameIds(max: Int, promise: Promise[List[Game.ID]])

View File

@ -15,11 +15,11 @@ final class Env(
rematches: lila.game.Rematches
)(implicit ec: scala.concurrent.ExecutionContext) {
private val tvTrouper = wire[TvTrouper]
private val tvSyncActor = wire[TvSyncActor]
lazy val tv = wire[Tv]
system.scheduler.scheduleWithFixedDelay(12 seconds, 3 seconds) { () =>
tvTrouper ! TvTrouper.Select
tvSyncActor ! TvSyncActor.Select
}
}

View File

@ -2,24 +2,24 @@ package lila.tv
import lila.common.LightUser
import lila.game.{ Game, GameRepo, Pov }
import lila.hub.Trouper
import lila.hub.SyncActor
final class Tv(
gameRepo: GameRepo,
trouper: Trouper,
trouper: SyncActor,
gameProxyRepo: lila.round.GameProxyRepo
)(implicit ec: scala.concurrent.ExecutionContext) {
import Tv._
import ChannelTrouper._
import ChannelSyncActor._
private def roundProxyGame = gameProxyRepo.game _
def getGame(channel: Tv.Channel): Fu[Option[Game]] =
trouper.ask[Option[Game.ID]](TvTrouper.GetGameId(channel, _)) flatMap { _ ?? roundProxyGame }
trouper.ask[Option[Game.ID]](TvSyncActor.GetGameId(channel, _)) flatMap { _ ?? roundProxyGame }
def getGameAndHistory(channel: Tv.Channel): Fu[Option[(Game, List[Pov])]] =
trouper.ask[GameIdAndHistory](TvTrouper.GetGameIdAndHistory(channel, _)) flatMap {
trouper.ask[GameIdAndHistory](TvSyncActor.GetGameIdAndHistory(channel, _)) flatMap {
case GameIdAndHistory(gameId, historyIds) =>
for {
game <- gameId ?? roundProxyGame
@ -40,14 +40,14 @@ final class Tv(
}
def getGameIds(channel: Tv.Channel, max: Int): Fu[List[Game.ID]] =
trouper.ask[List[Game.ID]](TvTrouper.GetGameIds(channel, max, _))
trouper.ask[List[Game.ID]](TvSyncActor.GetGameIds(channel, max, _))
def getBestGame = getGame(Tv.Channel.Best) orElse gameRepo.random
def getBestAndHistory = getGameAndHistory(Tv.Channel.Best)
def getChampions: Fu[Champions] =
trouper.ask[Champions](TvTrouper.GetChampions.apply)
trouper.ask[Champions](TvSyncActor.GetChampions.apply)
}
object Tv {

View File

@ -7,23 +7,23 @@ import scala.concurrent.Promise
import lila.common.{ Bus, LightUser }
import lila.game.{ Game, Pov }
import lila.hub.Trouper
import lila.hub.SyncActor
final private[tv] class TvTrouper(
final private[tv] class TvSyncActor(
renderer: lila.hub.actors.Renderer,
lightUserSync: LightUser.GetterSync,
recentTvGames: lila.round.RecentTvGames,
gameProxyRepo: lila.round.GameProxyRepo,
rematches: lila.game.Rematches
)(implicit ec: scala.concurrent.ExecutionContext)
extends Trouper {
extends SyncActor {
import TvTrouper._
import TvSyncActor._
Bus.subscribe(this, "startGame")
private val channelTroupers: Map[Tv.Channel, ChannelTrouper] = Tv.Channel.all.map { c =>
c -> new ChannelTrouper(c, onSelect = this.!, gameProxyRepo.game, rematches.of, lightUserSync)
private val channelTroupers: Map[Tv.Channel, ChannelSyncActor] = Tv.Channel.all.map { c =>
c -> new ChannelSyncActor(c, onSelect = this.!, gameProxyRepo.game, rematches.of, lightUserSync)
}.toMap
private var channelChampions = Map[Tv.Channel, Tv.Champion]()
@ -31,16 +31,16 @@ final private[tv] class TvTrouper(
private def forward[A](channel: Tv.Channel, msg: Any) =
channelTroupers get channel foreach { _ ! msg }
protected val process: Trouper.Receive = {
protected val process: SyncActor.Receive = {
case GetGameId(channel, promise) =>
forward(channel, ChannelTrouper.GetGameId(promise))
forward(channel, ChannelSyncActor.GetGameId(promise))
case GetGameIdAndHistory(channel, promise) =>
forward(channel, ChannelTrouper.GetGameIdAndHistory(promise))
forward(channel, ChannelSyncActor.GetGameIdAndHistory(promise))
case GetGameIds(channel, max, promise) =>
forward(channel, ChannelTrouper.GetGameIds(max, promise))
forward(channel, ChannelSyncActor.GetGameIds(max, promise))
case GetChampions(promise) => promise success Tv.Champions(channelChampions)
@ -52,7 +52,7 @@ final private[tv] class TvTrouper(
} foreach (_ addCandidate g)
}
case s @ TvTrouper.Select => channelTroupers.foreach(_._2 ! s)
case s @ TvSyncActor.Select => channelTroupers.foreach(_._2 ! s)
case Selected(channel, game) =>
import lila.socket.Socket.makeMessage
@ -99,12 +99,12 @@ final private[tv] class TvTrouper(
}
}
private[tv] object TvTrouper {
private[tv] object TvSyncActor {
case class GetGameId(channel: Tv.Channel, promise: Promise[Option[Game.ID]])
case class GetGameIds(channel: Tv.Channel, max: Int, promise: Promise[List[Game.ID]])
case class GetGameIdAndHistory(channel: Tv.Channel, promise: Promise[ChannelTrouper.GameIdAndHistory])
case class GetGameIdAndHistory(channel: Tv.Channel, promise: Promise[ChannelSyncActor.GameIdAndHistory])
case object Select
case class Selected(channel: Tv.Channel, game: Game)