lila/modules/evalCache/src/main/EvalCacheApi.scala

106 lines
3.3 KiB
Scala

package lila.evalCache
import chess.format.FEN
import chess.variant.Variant
import org.joda.time.DateTime
import play.api.libs.json.JsObject
import scala.concurrent.duration._
import lila.db.AsyncCollFailingSilently
import lila.db.dsl._
import lila.memo.CacheApi._
import lila.memo.SettingStore
import lila.socket.Socket
import lila.user.User
final class EvalCacheApi(
coll: AsyncCollFailingSilently,
truster: EvalCacheTruster,
upgrade: EvalCacheUpgrade,
cacheApi: lila.memo.CacheApi,
setting: SettingStore[Boolean]
)(implicit ec: scala.concurrent.ExecutionContext) {
import EvalCacheEntry._
import BSONHandlers._
def getEvalJson(variant: Variant, fen: FEN, multiPv: Int): Fu[Option[JsObject]] =
getEval(
id = Id(variant, SmallFen.make(variant, fen)),
multiPv = multiPv
) map {
_.map { JsonHandlers.writeEval(_, fen) }
} addEffect { res =>
fen.ply foreach { ply =>
lila.mon.evalCache.request(ply, res.isDefined).increment()
}
}
def put(trustedUser: TrustedUser, candidate: Input.Candidate, sri: Socket.Sri): Funit =
candidate.input ?? { put(trustedUser, _, sri) }
def shouldPut(user: User) = setting.get() && truster.shouldPut(user)
def getSinglePvEval(variant: Variant, fen: FEN): Fu[Option[Eval]] =
getEval(
id = Id(variant, SmallFen.make(variant, fen)),
multiPv = 1
)
private[evalCache] def drop(variant: Variant, fen: FEN): Funit = {
val id = Id(variant, SmallFen.make(variant, fen))
coll(_.delete.one($id(id)).void) >>- cache.invalidate(id)
}
private val cache = cacheApi[Id, Option[EvalCacheEntry]](65536, "evalCache") {
_.expireAfterAccess(5 minutes)
.buildAsyncFuture(fetchAndSetAccess)
}
private def getEval(id: Id, multiPv: Int): Fu[Option[Eval]] =
getEntry(id) map {
_.flatMap(_ makeBestMultiPvEval multiPv)
}
private def getEntry(id: Id): Fu[Option[EvalCacheEntry]] = cache get id
private def fetchAndSetAccess(id: Id): Fu[Option[EvalCacheEntry]] = coll { c =>
c.one[EvalCacheEntry]($id(id)) addEffect { res =>
if (res.isDefined) c.updateFieldUnchecked($id(id), "usedAt", DateTime.now)
}
}
private def put(trustedUser: TrustedUser, input: Input, sri: Socket.Sri): Funit = coll { c =>
Validator(input) match {
case Some(error) =>
logger.info(s"Invalid from ${trustedUser.user.username} $error ${input.fen}")
funit
case None =>
getEntry(input.id) flatMap {
case None =>
val entry = EvalCacheEntry(
_id = input.id,
nbMoves = destSize(input.fen),
evals = List(input.eval),
usedAt = DateTime.now,
updatedAt = DateTime.now
)
c.insert.one(entry).recover(lila.db.ignoreDuplicateKey).void >>-
cache.put(input.id, fuccess(entry.some)) >>-
upgrade.onEval(input, sri)
case Some(oldEntry) =>
val entry = oldEntry add input.eval
!(entry similarTo oldEntry) ?? {
c.update.one($id(entry.id), entry, upsert = true).void >>-
cache.put(input.id, fuccess(entry.some)) >>-
upgrade.onEval(input, sri)
}
}
}
}
private def destSize(fen: FEN): Int =
chess.Game(chess.variant.Standard.some, fen.some).situation.moves.view.map(_._2.size).sum
}