finish abandoned games after 10 days

This commit is contained in:
Thibault Duplessis 2012-10-08 17:55:10 +02:00
parent 1003f7e14b
commit 49b0403352
9 changed files with 63 additions and 23 deletions

View file

@ -64,6 +64,9 @@ sudo ln -s /path/to/lila/bin/prod/archlinux/conf.d/lila ./
- Configure the daemon in /etc/conf.d/lila - Configure the daemon in /etc/conf.d/lila
- Add lila to DAEMONS in /etc/rc.conf - Add lila to DAEMONS in /etc/rc.conf
Optional
ulimit -n 99999
Restart on timeout Restart on timeout
------------------ ------------------

View file

@ -154,6 +154,7 @@ final class CoreEnv private (application: Application, val settings: Settings) {
lazy val titivate = new lila.core.Titivate( lazy val titivate = new lila.core.Titivate(
gameRepo = game.gameRepo, gameRepo = game.gameRepo,
finisher = round.finisher, finisher = round.finisher,
meddler = round.meddler,
bookmarkApi = bookmark.api) bookmarkApi = bookmark.api)
lazy val mod = new lila.mod.ModEnv( lazy val mod = new lila.mod.ModEnv(

View file

@ -40,11 +40,11 @@ object Cron {
} }
} }
effect(2 seconds, "fisherman cleanup") { effect(2 seconds, "fisherman: cleanup") {
env.lobby.fisherman.cleanup env.lobby.fisherman.cleanup
} }
effect(10 seconds, "lobby cleanup") { effect(10 seconds, "lobby: cleanup") {
env.lobby.hookRepo.cleanupOld env.lobby.hookRepo.cleanupOld
} }
@ -56,18 +56,23 @@ object Cron {
if (current.mode != Mode.Dev) { if (current.mode != Mode.Dev) {
effect(4.1 hours, "game cleanup") { env.ai.clientDiagnose
effect(4.17 hours, "game: cleanup") {
env.titivate.cleanupUnplayed flatMap { _ env.titivate.cleanupUnplayed flatMap { _
env.titivate.cleanupNext env.titivate.cleanupNext
} }
} }
effect(1 hour, "game finish") { effect(1.13 hour, "game: finish by clock") {
env.titivate.finishByClock env.titivate.finishByClock
} }
env.ai.clientDiagnose effect(1 minutes, "game: finish abandoned") {
env.titivate.finishAbandoned
} }
}
unsafe(10 seconds, "ai: diagnose") { unsafe(10 seconds, "ai: diagnose") {
env.ai.clientDiagnose env.ai.clientDiagnose
} }

View file

@ -2,7 +2,7 @@ package lila
package core package core
import game.GameRepo import game.GameRepo
import round.Finisher import round.{ Finisher, Meddler }
import bookmark.BookmarkApi import bookmark.BookmarkApi
import com.mongodb.casbah.query.Imports._ import com.mongodb.casbah.query.Imports._
@ -13,16 +13,24 @@ import scalaz.effects._
final class Titivate( final class Titivate(
gameRepo: GameRepo, gameRepo: GameRepo,
finisher: Finisher, finisher: Finisher,
meddler: Meddler,
bookmarkApi: BookmarkApi) { bookmarkApi: BookmarkApi) {
val finishByClock: IO[Unit] = val finishByClock: IO[Unit] = for {
for {
games gameRepo.candidatesToAutofinish games gameRepo.candidatesToAutofinish
_ putStrLn("[titivate] Finish %d games by clock" format games.size)
_ (finisher outoftimes games).sequence _ (finisher outoftimes games).sequence
} yield () } yield ()
val cleanupUnplayed = for { val finishAbandoned: IO[Unit] = for {
games gameRepo abandoned 200
_ putStrLn("[titivate] Finish %d abandoned games" format games.size)
_ (games map meddler.finishAbandoned).sequence
} yield ()
val cleanupUnplayed: IO[Unit] = for {
ids gameRepo.unplayedIds ids gameRepo.unplayedIds
_ putStrLn("[titivate] Remove %d unplayed games" format ids.size)
_ gameRepo removeIds ids _ gameRepo removeIds ids
_ bookmarkApi removeByGameIds ids _ bookmarkApi removeByGameIds ids
} yield () } yield ()

View file

@ -356,6 +356,11 @@ case class DbGame(
def isBeingPlayed = def isBeingPlayed =
!finishedOrAborted && updatedAt.fold(_ > DateTime.now - 20.seconds, false) !finishedOrAborted && updatedAt.fold(_ > DateTime.now - 20.seconds, false)
def abandoned = updatedAt.fold(
u (status <= Status.Started) && (u <= DbGame.abandonedDate),
false
)
def hasBookmarks = bookmarks > 0 def hasBookmarks = bookmarks > 0
def showBookmarks = if (hasBookmarks) bookmarks else "" def showBookmarks = if (hasBookmarks) bookmarks else ""
@ -408,6 +413,8 @@ object DbGame {
val playerIdSize = 4 val playerIdSize = 4
val fullIdSize = 12 val fullIdSize = 12
def abandonedDate = DateTime.now - 10.days
def takeGameId(fullId: String) = fullId take gameIdSize def takeGameId(fullId: String) = fullId take gameIdSize
def apply( def apply(

View file

@ -148,7 +148,7 @@ final class GameRepo(collection: MongoCollection)
remove("_id" $in ids) remove("_id" $in ids)
} }
def candidatesToAutofinish: IO[List[DbGame]] = io { val candidatesToAutofinish: IO[List[DbGame]] = io {
find(Query.playable ++ find(Query.playable ++
Query.clock(true) ++ Query.clock(true) ++
("createdAt" $gt (DateTime.now - 1.day)) ++ // index ("createdAt" $gt (DateTime.now - 1.day)) ++ // index
@ -156,7 +156,13 @@ final class GameRepo(collection: MongoCollection)
).toList.map(_.decode).flatten ).toList.map(_.decode).flatten
} }
def featuredCandidates: IO[List[DbGame]] = io { def abandoned(max: Int): IO[List[DbGame]] = io {
find(
Query.notFinished ++ ("updatedAt" $lt DbGame.abandonedDate)
).limit(max).toList.map(_.decode).flatten
}
val featuredCandidates: IO[List[DbGame]] = io {
find(Query.playable ++ find(Query.playable ++
Query.clock(true) ++ Query.clock(true) ++
("turns" $gt 1) ++ ("turns" $gt 1) ++

View file

@ -27,7 +27,7 @@ object Query {
val finished: DBObject = "status" $in List(Status.Mate.id, Status.Resign.id, Status.Outoftime.id, Status.Timeout.id) val finished: DBObject = "status" $in List(Status.Mate.id, Status.Resign.id, Status.Outoftime.id, Status.Timeout.id)
val notFinished: DBObject = DBObject("status" -> Status.Started.id) val notFinished: DBObject = "status" $lte Status.Started.id
val frozen: DBObject = "status" $gte Status.Mate.id val frozen: DBObject = "status" $gte Status.Mate.id

View file

@ -36,4 +36,14 @@ final class Meddler(
povOption gameRepo pov povRef povOption gameRepo pov povRef
_ povOption.fold(resign, putStrLn("Cannot resign missing game " + povRef)) _ povOption.fold(resign, putStrLn("Cannot resign missing game " + povRef))
} yield () } yield ()
def finishAbandoned(game: DbGame): IO[Unit] = game.abandoned.fold(
finisher.resign(Pov(game, game.player))
.prefixFailuresWith("Finish abandoned game " + game.id)
.fold(
err putStrLn(err.shows),
_ map (_ ()) // discard the events
),
putStrLn("Game is not abandoned")
)
} }

View file

@ -31,7 +31,7 @@ final class Indexer(
def count(request: CountRequest): Int = request.in(indexName, typeName)(es) def count(request: CountRequest): Int = request.in(indexName, typeName)(es)
val indexQueue: IO[Unit] = for { val indexQueue: IO[Unit] = for {
ids queue next 1000 ids queue next 2000
_ ids.toNel.fold( _ ids.toNel.fold(
neIds for { neIds for {
_ putStrLn("Search indexing %d games" format neIds.list.size) _ putStrLn("Search indexing %d games" format neIds.list.size)