working POC of server-side eval cache

eval-cache
Thibault Duplessis 2017-01-31 14:39:57 +01:00
parent ecb4f910f6
commit 02202c75ab
8 changed files with 29 additions and 24 deletions

View File

@ -173,5 +173,4 @@ object Env {
def coach = lila.coach.Env.current def coach = lila.coach.Env.current
def pool = lila.pool.Env.current def pool = lila.pool.Env.current
def practice = lila.practice.Env.current def practice = lila.practice.Env.current
def evalCache = lila.evalCache.Env.current
} }

View File

@ -542,7 +542,7 @@ challenge {
uid.timeout = 7 seconds uid.timeout = 7 seconds
max_playing = ${setup.max_playing} max_playing = ${setup.max_playing}
} }
eval_cache { evalCache {
collection.eval_cache = eval_cache collection.eval_cache = eval_cache
} }
study { study {

View File

@ -4,6 +4,8 @@ import scala.concurrent.duration._
import chess.format.{ FEN, Uci } import chess.format.{ FEN, Uci }
import lila.db.dsl._ import lila.db.dsl._
import lila.socket.Handler.Controller
import lila.user.User
final class EvalCacheApi(coll: Coll) { final class EvalCacheApi(coll: Coll) {
@ -14,6 +16,15 @@ final class EvalCacheApi(coll: Coll) {
def put(candidate: Input.Candidate): Funit = candidate.input ?? put def put(candidate: Input.Candidate): Funit = candidate.input ?? put
def socketHandler(user: Option[User]): Controller =
user.filter(canPut).fold(lila.socket.Handler.emptyController)(makeController)
private def canPut(user: User) = true
private def makeController(user: User): Controller = {
case ("evalPut", o) => EvalCacheParser.parsePut(user, o.pp).pp foreach put
}
private def getEval(id: Id): Fu[Option[Eval]] = getEntry(id) map { private def getEval(id: Id): Fu[Option[Eval]] = getEntry(id) map {
_.flatMap(_.bestEval) _.flatMap(_.bestEval)
} }

View File

@ -6,22 +6,12 @@ import play.api.libs.json.JsObject
import chess.format.Uci import chess.format.Uci
import EvalCacheEntry._ import EvalCacheEntry._
import lila.common.PimpedJson._ import lila.common.PimpedJson._
import lila.socket.Handler.Controller
import lila.tree.Eval._ import lila.tree.Eval._
import lila.user.User import lila.user.User
final class SocketHandler(api: EvalCacheApi) { private object EvalCacheParser {
def controller(user: User): Controller = def parsePut(user: User, o: JsObject): Option[Input.Candidate] = for {
if (canPut(user)) makeController(user)
else lila.socket.Handler.emptyController
private def makeController(user: User): Controller = {
case ("evalPut", o) => parsePut(user, o) foreach api.put
}
private def parsePut(user: User, o: JsObject): Option[Input.Candidate] = for {
d <- o obj "d" d <- o obj "d"
variant = chess.variant.Variant orDefault ~d.str("variant") variant = chess.variant.Variant orDefault ~d.str("variant")
if variant.standard if variant.standard
@ -40,7 +30,7 @@ final class SocketHandler(api: EvalCacheApi) {
date = DateTime.now)) date = DateTime.now))
private def parsePv(d: JsObject): Option[Pv] = for { private def parsePv(d: JsObject): Option[Pv] = for {
movesStr <- d str "pv" movesStr <- d str "moves"
moves <- movesStr.split(' ').take(EvalCacheEntry.MAX_PV_SIZE).toList.foldLeft(List.empty[Uci].some) { moves <- movesStr.split(' ').take(EvalCacheEntry.MAX_PV_SIZE).toList.foldLeft(List.empty[Uci].some) {
case (Some(ucis), str) => Uci(str) map (_ :: ucis) case (Some(ucis), str) => Uci(str) map (_ :: ucis)
case _ => None case _ => None
@ -49,6 +39,4 @@ final class SocketHandler(api: EvalCacheApi) {
mate = d int "mate" map Mate.apply mate = d int "mate" map Mate.apply
score <- cp.map(Score.cp) orElse mate.map(Score.mate) score <- cp.map(Score.cp) orElse mate.map(Score.mate)
} yield Pv(score, moves) } yield Pv(score, moves)
private def canPut(user: User) = true
} }

View File

@ -18,6 +18,7 @@ final class Env(
lightUserApi: lila.user.LightUserApi, lightUserApi: lila.user.LightUserApi,
gamePgnDump: lila.game.PgnDump, gamePgnDump: lila.game.PgnDump,
importer: lila.importer.Importer, importer: lila.importer.Importer,
evalCache: lila.evalCache.EvalCacheApi,
system: ActorSystem, system: ActorSystem,
hub: lila.hub.Env, hub: lila.hub.Env,
db: lila.db.Env) { db: lila.db.Env) {
@ -57,7 +58,8 @@ final class Env(
socketHub = socketHub, socketHub = socketHub,
chat = hub.actor.chat, chat = hub.actor.chat,
destCache = destCache, destCache = destCache,
api = api) api = api,
evalCache = evalCache)
lazy val studyRepo = new StudyRepo(coll = db(CollectionStudy)) lazy val studyRepo = new StudyRepo(coll = db(CollectionStudy))
lazy val chapterRepo = new ChapterRepo(coll = db(CollectionChapter)) lazy val chapterRepo = new ChapterRepo(coll = db(CollectionChapter))
@ -129,6 +131,7 @@ object Env {
lightUserApi = lila.user.Env.current.lightUserApi, lightUserApi = lila.user.Env.current.lightUserApi,
gamePgnDump = lila.game.Env.current.pgnDump, gamePgnDump = lila.game.Env.current.pgnDump,
importer = lila.importer.Env.current.importer, importer = lila.importer.Env.current.importer,
evalCache = lila.evalCache.Env.current.api,
system = lila.common.PlayApp.system, system = lila.common.PlayApp.system,
hub = lila.hub.Env.current, hub = lila.hub.Env.current,
db = lila.db.Env.current) db = lila.db.Env.current)

View File

@ -23,7 +23,8 @@ private[study] final class SocketHandler(
socketHub: ActorRef, socketHub: ActorRef,
chat: ActorSelection, chat: ActorSelection,
destCache: LoadingCache[AnaDests.Ref, AnaDests], destCache: LoadingCache[AnaDests.Ref, AnaDests],
api: StudyApi) { api: StudyApi,
evalCache: lila.evalCache.EvalCacheApi) {
import Handler.AnaRateLimit import Handler.AnaRateLimit
import JsonView.shapeReader import JsonView.shapeReader
@ -40,7 +41,8 @@ private[study] final class SocketHandler(
studyId: Study.Id, studyId: Study.Id,
uid: Uid, uid: Uid,
member: Socket.Member, member: Socket.Member,
owner: Boolean): Handler.Controller = ({ owner: Boolean,
user: Option[User]): Handler.Controller = ({
case ("p", o) => o int "v" foreach { v => case ("p", o) => o int "v" foreach { v =>
socket ! PingVersion(uid.value, v) socket ! PingVersion(uid.value, v)
} }
@ -233,7 +235,7 @@ private[study] final class SocketHandler(
byUserId <- member.userId byUserId <- member.userId
v <- (o \ "d" \ "liked").asOpt[Boolean] v <- (o \ "d" \ "liked").asOpt[Boolean]
} api.like(studyId, byUserId, v, socket, uid) } api.like(studyId, byUserId, v, socket, uid)
}: Handler.Controller) orElse lila.chat.Socket.in( }: Handler.Controller) orElse evalCache.socketHandler(user) orElse lila.chat.Socket.in(
chatId = studyId.value, chatId = studyId.value,
member = member, member = member,
socket = socket, socket = socket,
@ -264,7 +266,7 @@ private[study] final class SocketHandler(
join = Socket.Join(uid = uid, userId = user.map(_.id), troll = user.??(_.troll), owner = owner) join = Socket.Join(uid = uid, userId = user.map(_.id), troll = user.??(_.troll), owner = owner)
handler Handler(hub, socket, uid.value, join) { handler Handler(hub, socket, uid.value, join) {
case Socket.Connected(enum, member) => case Socket.Connected(enum, member) =>
(controller(socket, studyId, uid, member, owner = owner), enum, member) (controller(socket, studyId, uid, member, owner = owner, user = user), enum, member)
} }
} yield handler.some } yield handler.some
} }

View File

@ -240,7 +240,8 @@ object ApplicationBuild extends Build {
libraryDependencies ++= provided(play.api, reactivemongo.driver) libraryDependencies ++= provided(play.api, reactivemongo.driver)
) )
lazy val study = project("study", Seq(common, db, hub, socket, game, round, importer, notifyModule, relation)).settings( lazy val study = project("study", Seq(
common, db, hub, socket, game, round, importer, notifyModule, relation, evalCache)).settings(
libraryDependencies ++= provided(play.api, reactivemongo.driver) libraryDependencies ++= provided(play.api, reactivemongo.driver)
) )

View File

@ -171,6 +171,7 @@ module.exports = function(data, ctrl, tagTypes, practiceData) {
// var evalPutMinNodes = 5e6; // var evalPutMinNodes = 5e6;
var evalPutMinDepth = 16; var evalPutMinDepth = 16;
var evalPutMinNodes = 1e6; var evalPutMinNodes = 1e6;
var evalPutMaxMoves = 8;
var onCeval = throttle(1000, false, function(eval) { var onCeval = throttle(1000, false, function(eval) {
if (isStandard() && eval.depth >= evalPutMinDepth && eval.nodes > evalPutMinNodes) send("evalPut", { if (isStandard() && eval.depth >= evalPutMinDepth && eval.nodes > evalPutMinNodes) send("evalPut", {
fen: eval.fen, fen: eval.fen,
@ -181,7 +182,7 @@ module.exports = function(data, ctrl, tagTypes, practiceData) {
return { return {
cp: pv.cp, cp: pv.cp,
mate: pv.mate, mate: pv.mate,
moves: pv.pv moves: pv.pv.split(' ').slice(0, evalPutMaxMoves).join(' ')
}; };
}) })
}); });