Merge branch 'master' into puzzle-ui
* master: also name single async caches and mixed cache name async caches and expire the result future remove user ranking on account closure {master} fix game tournament leaderboard style
This commit is contained in:
commit
3a42852b49
|
@ -45,6 +45,11 @@ final class Env(
|
|||
getPlayTime = Env.game.playTime.apply,
|
||||
completionRate = Env.playban.api.completionRate) _
|
||||
|
||||
lazy val teamInfo = new mashup.TeamInfoApi(
|
||||
api = Env.team.api,
|
||||
getForumNbPosts = Env.forum.categApi.teamNbPosts _,
|
||||
getForumPosts = Env.forum.recent.team _)
|
||||
|
||||
private def tryDailyPuzzle(): Fu[Option[lila.puzzle.DailyPuzzle]] =
|
||||
scala.concurrent.Future {
|
||||
Env.puzzle.daily()
|
||||
|
|
|
@ -66,6 +66,7 @@ object Lobby extends LilaController {
|
|||
headers: Headers)
|
||||
|
||||
private val cache = lila.memo.AsyncCache[RequestKey, Html](
|
||||
name = "lobby.homeCache",
|
||||
f = renderRequestKey,
|
||||
timeToLive = 1 second)
|
||||
|
||||
|
|
|
@ -130,7 +130,9 @@ object Mod extends LilaController {
|
|||
}
|
||||
|
||||
private[controllers] val ipIntelCache =
|
||||
lila.memo.AsyncCache[String, Int](ip => {
|
||||
lila.memo.AsyncCache[String, Int](
|
||||
name = "ipIntel",
|
||||
f = ip => {
|
||||
import play.api.libs.ws.WS
|
||||
import play.api.Play.current
|
||||
val email = Env.api.Net.Email
|
||||
|
|
|
@ -15,6 +15,7 @@ object Monitor extends LilaController {
|
|||
val coachPageView = "servers.lichess.statsite.counts.main.counter.coach.page_view.profile"
|
||||
}
|
||||
private val coachPageViewCache = lila.memo.AsyncCache[lila.user.User.ID, Result](
|
||||
name = "monitor.coachPageView",
|
||||
f = userId =>
|
||||
Env.coach.api byId lila.coach.Coach.Id(userId) flatMap {
|
||||
_ ?? { coach =>
|
||||
|
|
|
@ -18,6 +18,7 @@ object Prismic {
|
|||
}
|
||||
|
||||
private val fetchPrismicApi = AsyncCache.single[PrismicApi](
|
||||
name = "prismic.fetchPrismicApi",
|
||||
f = PrismicApi.get(Env.api.PrismicApiUrl, logger = prismicLogger),
|
||||
timeToLive = 1 minute)
|
||||
|
||||
|
|
|
@ -15,10 +15,7 @@ object Team extends LilaController {
|
|||
private def forms = Env.team.forms
|
||||
private def api = Env.team.api
|
||||
private def paginator = Env.team.paginator
|
||||
private lazy val teamInfo = mashup.TeamInfo(
|
||||
api = api,
|
||||
getForumNbPosts = Env.forum.categApi.teamNbPosts _,
|
||||
getForumPosts = Env.forum.recent.team _) _
|
||||
private lazy val teamInfo = Env.current.teamInfo
|
||||
|
||||
def all(page: Int) = Open { implicit ctx =>
|
||||
NotForKids {
|
||||
|
|
|
@ -22,22 +22,25 @@ case class TeamInfo(
|
|||
def hasRequests = requests.nonEmpty
|
||||
}
|
||||
|
||||
object TeamInfo {
|
||||
final class TeamInfoApi(
|
||||
api: TeamApi,
|
||||
getForumNbPosts: String => Fu[Int],
|
||||
getForumPosts: String => Fu[List[MiniForumPost]]) {
|
||||
|
||||
private case class Cachable(bestUserIds: List[User.ID], toints: Int)
|
||||
|
||||
private def fetchCachable(id: String): Fu[Cachable] = for {
|
||||
userIds ← (MemberRepo userIdsByTeam id).thenPp(s"team $id userIds")
|
||||
bestUserIds ← UserRepo.idsByIdsSortRating(userIds, 10).thenPp(s"team $id bestUserIds")
|
||||
toints ← UserRepo.idsSumToints(userIds).thenPp(s"team $id toints")
|
||||
} yield Cachable(bestUserIds, toints)
|
||||
|
||||
private val cache = lila.memo.AsyncCache[String, Cachable](
|
||||
teamId => for {
|
||||
userIds ← MemberRepo userIdsByTeam teamId
|
||||
bestUserIds ← UserRepo.idsByIdsSortRating(userIds, 10)
|
||||
toints ← UserRepo.idsSumToints(userIds)
|
||||
} yield Cachable(bestUserIds, toints),
|
||||
name = "teamInfo",
|
||||
f = fetchCachable,
|
||||
timeToLive = 10 minutes)
|
||||
|
||||
def apply(
|
||||
api: TeamApi,
|
||||
getForumNbPosts: String => Fu[Int],
|
||||
getForumPosts: String => Fu[List[MiniForumPost]])(team: Team, me: Option[User]): Fu[TeamInfo] = for {
|
||||
def apply(team: Team, me: Option[User]): Fu[TeamInfo] = for {
|
||||
requests ← (team.enabled && me.??(m => team.isCreator(m.id))) ?? api.requestsWithUsers(team)
|
||||
mine = me.??(m => api.belongsTo(team.id, m.id))
|
||||
requestedByMe ← !mine ?? me.??(m => RequestRepo.exists(team.id, m.id))
|
||||
|
|
|
@ -10,7 +10,7 @@ net {
|
|||
ip = "5.196.91.160"
|
||||
asset {
|
||||
domain = ${net.domain}
|
||||
version = 1245
|
||||
version = 1246
|
||||
}
|
||||
email = "contact@lichess.org"
|
||||
crawlable = false
|
||||
|
|
|
@ -56,6 +56,7 @@ final class Env(
|
|||
import lila.db.dsl._
|
||||
private val coll = db("flag")
|
||||
private val cache = lila.memo.MixedCache.single[Int](
|
||||
name = "asset.version",
|
||||
f = coll.primitiveOne[BSONNumberLike]($id("asset"), "version").map {
|
||||
_.fold(Net.AssetVersion)(_.toInt max Net.AssetVersion)
|
||||
},
|
||||
|
|
|
@ -39,6 +39,7 @@ final class BlogApi(prismicUrl: String, collection: String) {
|
|||
}
|
||||
|
||||
private val fetchPrismicApi = AsyncCache.single[Api](
|
||||
name = "blogApi.fetchPrismicApi",
|
||||
f = Api.get(prismicUrl, cache = cache, logger = prismicLogger),
|
||||
timeToLive = 10 seconds)
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ import scala.concurrent.duration._
|
|||
final class LastPostCache(api: BlogApi, ttl: Duration, collection: String) {
|
||||
|
||||
private val cache = lila.memo.MixedCache.single[List[MiniPost]](
|
||||
api.prismicApi flatMap { prismic =>
|
||||
name = "blog.lastPost",
|
||||
f = api.prismicApi flatMap { prismic =>
|
||||
api.recent(prismic, none, 3) map {
|
||||
_ ?? {
|
||||
_.results.toList flatMap MiniPost.fromDocument(collection)
|
||||
|
|
|
@ -37,7 +37,10 @@ final class ChallengeApi(
|
|||
|
||||
def byId = repo byId _
|
||||
|
||||
val countInFor = AsyncCache(repo.countCreatedByDestId, maxCapacity = 20000)
|
||||
val countInFor = AsyncCache(
|
||||
name = "challenge.countInFor",
|
||||
f = repo.countCreatedByDestId,
|
||||
maxCapacity = 20000)
|
||||
|
||||
def createdByChallengerId = repo createdByChallengerId _
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ final class CoachApi(
|
|||
import BsonHandlers._
|
||||
|
||||
private val cache = AsyncCache.single[List[Coach]](
|
||||
name = "coach.list",
|
||||
f = coachColl.find($empty).list[Coach](),
|
||||
timeToLive = 10 minutes)
|
||||
|
||||
|
|
|
@ -47,4 +47,9 @@ object Future {
|
|||
|
||||
def delay[A](duration: FiniteDuration)(run: => Fu[A])(implicit system: akka.actor.ActorSystem): Fu[A] =
|
||||
akka.pattern.after(duration, system.scheduler)(run)
|
||||
|
||||
def neverCompletes[T]: Fu[T] = {
|
||||
val p = scala.concurrent.Promise[T]()
|
||||
p.future
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package lila
|
||||
|
||||
import play.api.libs.concurrent.Execution.Implicits._
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.Future
|
||||
|
||||
object PimpedFuture {
|
||||
|
||||
|
@ -98,12 +98,14 @@ object PimpedFuture {
|
|||
}
|
||||
|
||||
def withTimeout(duration: FiniteDuration, error: => Throwable)(implicit system: akka.actor.ActorSystem): Fu[A] = {
|
||||
Future firstCompletedOf Seq(fua,
|
||||
Future firstCompletedOf Seq(
|
||||
fua,
|
||||
akka.pattern.after(duration, system.scheduler)(Future failed error))
|
||||
}
|
||||
|
||||
def withTimeoutDefault(duration: FiniteDuration, default: => A)(implicit system: akka.actor.ActorSystem): Fu[A] = {
|
||||
Future firstCompletedOf Seq(fua,
|
||||
Future firstCompletedOf Seq(
|
||||
fua,
|
||||
akka.pattern.after(duration, system.scheduler)(Future(default)))
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ final class EventApi(coll: Coll) {
|
|||
|
||||
import BsonHandlers._
|
||||
|
||||
val promotable = AsyncCache.single(fetchPromotable, timeToLive = 5 minutes)
|
||||
val promotable = AsyncCache.single(
|
||||
name = "event.promotable",
|
||||
fetchPromotable, timeToLive = 5 minutes)
|
||||
|
||||
def fetchPromotable: Fu[List[Event]] = coll.find($doc(
|
||||
"enabled" -> true,
|
||||
|
|
|
@ -15,6 +15,7 @@ private final class FishnetRepo(
|
|||
import BSONHandlers._
|
||||
|
||||
private val clientCache = AsyncCache[Client.Key, Option[Client]](
|
||||
name = "fishnet.client",
|
||||
f = key => clientColl.find(selectClient(key)).uno[Client],
|
||||
timeToLive = 10 seconds)
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ final class Cached(
|
|||
// timeToLive = 1 hour)
|
||||
|
||||
private val countShortTtl = AsyncCache[Bdoc, Int](
|
||||
name = "game.countShortTtl",
|
||||
f = (o: Bdoc) => coll countSel o,
|
||||
timeToLive = 5.seconds)
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ final class CrosstableApi(
|
|||
|
||||
// to avoid creating it twice during a new matchup
|
||||
private val creationCache = AsyncCache[(String, String), Option[Crosstable]](
|
||||
name = "crosstable",
|
||||
f = (create _).tupled,
|
||||
timeToLive = 20 seconds)
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@ private[i18n] final class Context(gitUrl: String, gitFile: String, keys: I18nKey
|
|||
|
||||
def get: Fu[Contexts] = cache(true)
|
||||
|
||||
private val cache = AsyncCache.single[Contexts](fetch, timeToLive = 1 hour)
|
||||
private val cache = AsyncCache.single[Contexts](
|
||||
name = "i18n.contexts",
|
||||
fetch, timeToLive = 1 hour)
|
||||
|
||||
private def parse(text: String): Contexts =
|
||||
text.lines.toList.map(_.trim).filter(_.nonEmpty).map(_.split('=')).foldLeft(Map[String, String]()) {
|
||||
|
|
|
@ -26,6 +26,7 @@ final class SeekApi(
|
|||
.cursor[Seek]()
|
||||
|
||||
private val cache = AsyncCache[CacheKey, List[Seek]](
|
||||
name = "lobby.seek.list",
|
||||
f = {
|
||||
case ForAnon => allCursor.gather[List](maxPerPage)
|
||||
case ForUser => allCursor.gather[List]()
|
||||
|
|
|
@ -17,18 +17,29 @@ final class AsyncCache[K, V] private (cache: Cache[V], f: K => Fu[V]) {
|
|||
|
||||
object AsyncCache {
|
||||
|
||||
implicit private val system = lila.common.PlayApp.system
|
||||
|
||||
def apply[K, V](
|
||||
name: String,
|
||||
f: K => Fu[V],
|
||||
maxCapacity: Int = 500,
|
||||
initialCapacity: Int = 16,
|
||||
timeToLive: Duration = Duration.Inf,
|
||||
timeToIdle: Duration = Duration.Inf) = new AsyncCache(
|
||||
timeToIdle: Duration = Duration.Inf,
|
||||
resultTimeout: FiniteDuration = 5 seconds) = new AsyncCache(
|
||||
cache = LruCache(maxCapacity, initialCapacity, timeToLive, timeToIdle),
|
||||
f = f)
|
||||
f = (key: K) => f(key).withTimeout(
|
||||
duration = resultTimeout,
|
||||
error = lila.common.LilaException(s"AsyncCache $name $key timed out after $resultTimeout"))
|
||||
)
|
||||
|
||||
def single[V](
|
||||
name: String,
|
||||
f: => Fu[V],
|
||||
timeToLive: Duration = Duration.Inf) = new AsyncCache[Boolean, V](
|
||||
timeToLive: Duration = Duration.Inf,
|
||||
resultTimeout: FiniteDuration = 5 seconds) = new AsyncCache[Boolean, V](
|
||||
cache = LruCache(timeToLive = timeToLive),
|
||||
f = _ => f)
|
||||
f = _ => f.withTimeout(
|
||||
duration = resultTimeout,
|
||||
error = lila.common.LilaException(s"AsyncCache $name single timed out after $resultTimeout")))
|
||||
}
|
||||
|
|
|
@ -30,37 +30,39 @@ object MixedCache {
|
|||
async.remove(k) >>- sync.invalidate(k)
|
||||
|
||||
def apply[K, V](
|
||||
name: String,
|
||||
f: K => Fu[V],
|
||||
timeToLive: Duration = Duration.Inf,
|
||||
awaitTime: FiniteDuration = 10.milliseconds,
|
||||
default: K => V,
|
||||
logger: lila.log.Logger): MixedCache[K, V] = {
|
||||
val async = AsyncCache(f, maxCapacity = 10000, timeToLive = 1 minute)
|
||||
val async = AsyncCache(name, f, maxCapacity = 10000, timeToLive = 1 minute)
|
||||
val sync = Builder.cache[K, V](
|
||||
timeToLive,
|
||||
(k: K) => async(k) await makeTimeout(awaitTime))
|
||||
new MixedCache(sync, default, invalidate(async, sync) _, logger branch "MixedCache")
|
||||
}
|
||||
|
||||
def fromAsync[K, V](
|
||||
async: AsyncCache[K,V],
|
||||
timeToLive: Duration = Duration.Inf,
|
||||
awaitTime: FiniteDuration = 10.milliseconds,
|
||||
default: K => V,
|
||||
logger: lila.log.Logger): MixedCache[K, V] = {
|
||||
val sync = Builder.cache[K, V](
|
||||
timeToLive,
|
||||
(k: K) => async(k) await makeTimeout(awaitTime))
|
||||
new MixedCache(sync, default, invalidate(async, sync) _, logger branch "MixedCache")
|
||||
}
|
||||
// def fromAsync[K, V](
|
||||
// async: AsyncCache[K,V],
|
||||
// timeToLive: Duration = Duration.Inf,
|
||||
// awaitTime: FiniteDuration = 10.milliseconds,
|
||||
// default: K => V,
|
||||
// logger: lila.log.Logger): MixedCache[K, V] = {
|
||||
// val sync = Builder.cache[K, V](
|
||||
// timeToLive,
|
||||
// (k: K) => async(k) await makeTimeout(awaitTime))
|
||||
// new MixedCache(sync, default, invalidate(async, sync) _, logger branch "MixedCache")
|
||||
// }
|
||||
|
||||
def single[V](
|
||||
name: String,
|
||||
f: => Fu[V],
|
||||
timeToLive: Duration = Duration.Inf,
|
||||
awaitTime: FiniteDuration = 5.milliseconds,
|
||||
default: V,
|
||||
logger: lila.log.Logger): MixedCache[Boolean, V] = {
|
||||
val async = AsyncCache.single(f, timeToLive = 1 minute)
|
||||
val async = AsyncCache.single(name, f, timeToLive = 1 minute)
|
||||
val sync = Builder.cache[Boolean, V](
|
||||
timeToLive,
|
||||
(_: Boolean) => async(true) await makeTimeout(awaitTime))
|
||||
|
|
|
@ -56,6 +56,7 @@ final class Gamify(
|
|||
def leaderboards = leaderboardsCache(true)
|
||||
|
||||
private val leaderboardsCache = AsyncCache.single[Leaderboards](
|
||||
name = "mod.leaderboards",
|
||||
f = mixedLeaderboard(DateTime.now minusDays 1, none) zip
|
||||
mixedLeaderboard(DateTime.now minusWeeks 1, none) zip
|
||||
mixedLeaderboard(DateTime.now minusMonths 1, none) map {
|
||||
|
|
|
@ -20,10 +20,10 @@ final class NotifyApi(
|
|||
|
||||
def getNotifications(userId: Notification.Notifies, page: Int): Fu[Paginator[Notification]] = Paginator(
|
||||
adapter = new Adapter(
|
||||
collection = repo.coll,
|
||||
selector = repo.userNotificationsQuery(userId),
|
||||
projection = $empty,
|
||||
sort = repo.recentSort),
|
||||
collection = repo.coll,
|
||||
selector = repo.userNotificationsQuery(userId),
|
||||
projection = $empty,
|
||||
sort = repo.recentSort),
|
||||
currentPage = page,
|
||||
maxPerPage = perPage)
|
||||
|
||||
|
@ -34,7 +34,10 @@ final class NotifyApi(
|
|||
repo.markAllRead(userId) >> unreadCountCache.remove(userId)
|
||||
|
||||
private val unreadCountCache =
|
||||
AsyncCache(repo.unreadNotificationsCount, maxCapacity = 20000)
|
||||
AsyncCache(
|
||||
name = "notify.unreadCountCache",
|
||||
f = repo.unreadNotificationsCount,
|
||||
maxCapacity = 20000)
|
||||
|
||||
def unreadCount(userId: Notification.Notifies): Fu[Notification.UnreadCount] =
|
||||
unreadCountCache(userId) map Notification.UnreadCount.apply
|
||||
|
|
|
@ -219,6 +219,7 @@ final class PlanApi(
|
|||
}
|
||||
|
||||
private val recentChargeUserIdsCache = AsyncCache[Int, List[User.ID]](
|
||||
name = "plan.recentChargeUserIds",
|
||||
f = nb => chargeColl.primitive[User.ID](
|
||||
$empty, sort = $doc("date" -> -1), nb = nb * 3 / 2, "userId"
|
||||
) flatMap filterUserIds map (_ take nb),
|
||||
|
@ -230,6 +231,7 @@ final class PlanApi(
|
|||
chargeColl.find($doc("userId" -> user.id)).sort($doc("date" -> -1)).list[Charge]()
|
||||
|
||||
private val topPatronUserIdsCache = AsyncCache[Int, List[User.ID]](
|
||||
name = "plan.topPatronUserIds",
|
||||
f = nb => chargeColl.aggregate(
|
||||
Match($doc("userId" $exists true)), List(
|
||||
GroupField("userId")("total" -> SumField("cents")),
|
||||
|
|
|
@ -15,7 +15,10 @@ final class PrefApi(
|
|||
cacheTtl: Duration) {
|
||||
|
||||
private def fetchPref(id: String): Fu[Option[Pref]] = coll.find($id(id)).uno[Pref]
|
||||
private val cache = AsyncCache(fetchPref, timeToLive = cacheTtl)
|
||||
private val cache = AsyncCache(
|
||||
name = "pref.fetchPref",
|
||||
f = fetchPref,
|
||||
timeToLive = cacheTtl)
|
||||
|
||||
private implicit val prefBSONHandler = new BSON[Pref] {
|
||||
|
||||
|
|
|
@ -14,7 +14,10 @@ private[puzzle] final class Daily(
|
|||
scheduler: Scheduler) {
|
||||
|
||||
private val cache =
|
||||
lila.memo.AsyncCache.single[Option[DailyPuzzle]](f = find, timeToLive = 10 minutes)
|
||||
lila.memo.AsyncCache.single[Option[DailyPuzzle]](
|
||||
name = "puzzle.daily",
|
||||
f = find,
|
||||
timeToLive = 10 minutes)
|
||||
|
||||
def apply(): Fu[Option[DailyPuzzle]] = cache apply true
|
||||
|
||||
|
|
|
@ -33,7 +33,9 @@ private[puzzle] final class PuzzleApi(
|
|||
|
||||
private def lastId: Fu[Int] = lila.db.Util findNextId puzzleColl map (_ - 1)
|
||||
|
||||
val cachedLastId = lila.memo.AsyncCache.single(lastId, timeToLive = 5 minutes)
|
||||
val cachedLastId = lila.memo.AsyncCache.single(
|
||||
name = "puzzle.lastId",
|
||||
lastId, timeToLive = 5 minutes)
|
||||
|
||||
def importOne(json: JsValue, token: String): Fu[PuzzleId] =
|
||||
if (token != apiToken) fufail("Invalid API token")
|
||||
|
|
|
@ -57,12 +57,14 @@ final class RelationApi(
|
|||
fetchFollows(u1, u2) flatMap { _ ?? fetchFollows(u2, u1) }
|
||||
|
||||
private val countFollowingCache = AsyncCache[ID, Int](
|
||||
name = "relation.count.following",
|
||||
f = userId => coll.countSel($doc("u1" -> userId, "r" -> Follow)),
|
||||
timeToLive = 10 minutes)
|
||||
|
||||
def countFollowing(userId: ID) = countFollowingCache(userId)
|
||||
|
||||
private val countFollowersCache = AsyncCache[ID, Int](
|
||||
name = "relation.count.followers",
|
||||
f = userId => coll.countSel($doc("u2" -> userId, "r" -> Follow)),
|
||||
timeToLive = 10 minutes)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import scala.concurrent.duration._
|
|||
private[simul] final class Cached(repo: SimulRepo) {
|
||||
|
||||
private val nameCache = lila.memo.MixedCache[Simul.ID, Option[String]](
|
||||
name = "simul.name",
|
||||
((id: Simul.ID) => repo find id map2 { (simul: Simul) => simul.fullName }),
|
||||
timeToLive = 6 hours,
|
||||
default = _ => none,
|
||||
|
|
|
@ -90,9 +90,13 @@ final class Env(
|
|||
|
||||
def isHosting(userId: String): Fu[Boolean] = api.currentHostIds map (_ contains userId)
|
||||
|
||||
val allCreated = lila.memo.AsyncCache.single(repo.allCreated, timeToLive = CreatedCacheTtl)
|
||||
val allCreated = lila.memo.AsyncCache.single(
|
||||
name = "simul.allCreated",
|
||||
repo.allCreated, timeToLive = CreatedCacheTtl)
|
||||
|
||||
val allCreatedFeaturable = lila.memo.AsyncCache.single(repo.allCreatedFeaturable, timeToLive = CreatedCacheTtl)
|
||||
val allCreatedFeaturable = lila.memo.AsyncCache.single(
|
||||
name = "simul.allCreatedFeaturable",
|
||||
repo.allCreatedFeaturable, timeToLive = CreatedCacheTtl)
|
||||
|
||||
def version(tourId: String): Fu[Int] =
|
||||
socketHub ? Ask(tourId, GetVersion) mapTo manifest[Int]
|
||||
|
|
|
@ -33,6 +33,7 @@ private[simul] final class SimulApi(
|
|||
def currentHostIds: Fu[Set[String]] = currentHostIdsCache apply true
|
||||
|
||||
private val currentHostIdsCache = AsyncCache.single[Set[String]](
|
||||
name = "simul.currentHostIds",
|
||||
f = repo.allStarted map (_ map (_.hostId) toSet),
|
||||
timeToLive = 10 minutes)
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ import scala.concurrent.duration._
|
|||
|
||||
private[team] final class Cached {
|
||||
|
||||
private val nameCache = MixedCache[String, Option[String]](TeamRepo.name,
|
||||
private val nameCache = MixedCache[String, Option[String]](
|
||||
name = "team.name",
|
||||
TeamRepo.name,
|
||||
timeToLive = 6 hours,
|
||||
default = _ => none,
|
||||
logger = logger)
|
||||
|
@ -13,6 +15,7 @@ private[team] final class Cached {
|
|||
def name(id: String) = nameCache get id
|
||||
|
||||
private[team] val teamIdsCache = MixedCache[String, Set[String]](
|
||||
name = "team.ids",
|
||||
MemberRepo.teamIdsByUser,
|
||||
timeToLive = 2 hours,
|
||||
default = _ => Set.empty,
|
||||
|
@ -20,7 +23,8 @@ private[team] final class Cached {
|
|||
|
||||
def teamIds(userId: String) = teamIdsCache get userId
|
||||
|
||||
val nbRequests = AsyncCache(
|
||||
(userId: String) => TeamRepo teamIdsByCreator userId flatMap RequestRepo.countByTeams,
|
||||
val nbRequests = AsyncCache[String, Int](
|
||||
name = "team.nbRequests",
|
||||
f = userId => TeamRepo teamIdsByCreator userId flatMap RequestRepo.countByTeams,
|
||||
maxCapacity = 20000)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ private[tournament] final class Cached(
|
|||
rankingTtl: FiniteDuration) {
|
||||
|
||||
private val nameCache = MixedCache[String, Option[String]](
|
||||
name = "tournament.name",
|
||||
((id: String) => TournamentRepo byId id map2 { (tour: Tournament) => tour.fullName }),
|
||||
timeToLive = 6 hours,
|
||||
default = _ => none,
|
||||
|
@ -17,6 +18,7 @@ private[tournament] final class Cached(
|
|||
def name(id: String): Option[String] = nameCache get id
|
||||
|
||||
val promotable = AsyncCache.single(
|
||||
name = "tournament.promotable",
|
||||
TournamentRepo.promotable,
|
||||
timeToLive = createdTtl)
|
||||
|
||||
|
@ -36,11 +38,13 @@ private[tournament] final class Cached(
|
|||
|
||||
// only applies to ongoing tournaments
|
||||
private val ongoingRanking = AsyncCache[String, Ranking](
|
||||
PlayerRepo.computeRanking,
|
||||
name = "tournament.ongoingRanking",
|
||||
f = PlayerRepo.computeRanking,
|
||||
timeToLive = 3.seconds)
|
||||
|
||||
// only applies to finished tournaments
|
||||
private val finishedRanking = AsyncCache[String, Ranking](
|
||||
PlayerRepo.computeRanking,
|
||||
name = "tournament.finishedRanking",
|
||||
f = PlayerRepo.computeRanking,
|
||||
timeToLive = rankingTtl)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package lila.tournament
|
||||
|
||||
import org.joda.time.format.ISODateTimeFormat
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.ISODateTimeFormat
|
||||
import play.api.libs.json._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
@ -174,21 +174,24 @@ final class JsonView(
|
|||
)
|
||||
|
||||
private val firstPageCache = lila.memo.AsyncCache[String, JsObject](
|
||||
(id: String) => TournamentRepo byId id flatten s"No such tournament: $id" flatMap { computeStanding(_, 1) },
|
||||
name = "tournament.firstPage",
|
||||
id => TournamentRepo byId id flatten s"No such tournament: $id" flatMap { computeStanding(_, 1) },
|
||||
timeToLive = 1 second)
|
||||
|
||||
private val cachableData = lila.memo.AsyncCache[String, CachableData](id =>
|
||||
for {
|
||||
pairings <- PairingRepo.recentByTour(id, 40)
|
||||
tour <- TournamentRepo byId id
|
||||
featured <- tour ?? fetchFeaturedGame
|
||||
podium <- podiumJson(id)
|
||||
next <- tour.filter(_.isFinished) ?? cached.findNext map2 nextJson
|
||||
} yield CachableData(
|
||||
pairings = JsArray(pairings map pairingJson),
|
||||
featured = featured map featuredJson,
|
||||
podium = podium,
|
||||
next = next),
|
||||
private val cachableData = lila.memo.AsyncCache[String, CachableData](
|
||||
name = "tournament.json.cachable",
|
||||
id =>
|
||||
for {
|
||||
pairings <- PairingRepo.recentByTour(id, 40)
|
||||
tour <- TournamentRepo byId id
|
||||
featured <- tour ?? fetchFeaturedGame
|
||||
podium <- podiumJson(id)
|
||||
next <- tour.filter(_.isFinished) ?? cached.findNext map2 nextJson
|
||||
} yield CachableData(
|
||||
pairings = JsArray(pairings map pairingJson),
|
||||
featured = featured map featuredJson,
|
||||
podium = podium,
|
||||
next = next),
|
||||
timeToLive = 1 second)
|
||||
|
||||
private def nextJson(tour: Tournament) = Json.obj(
|
||||
|
|
|
@ -310,7 +310,8 @@ final class TournamentApi(
|
|||
}
|
||||
|
||||
private val miniStandingCache = lila.memo.AsyncCache[String, List[RankedPlayer]](
|
||||
(id: String) => PlayerRepo.bestByTourWithRank(id, 30),
|
||||
name = "tournament.miniStanding",
|
||||
id => PlayerRepo.bestByTourWithRank(id, 30),
|
||||
timeToLive = 3 second)
|
||||
|
||||
def miniStanding(tourId: String, withStanding: Boolean): Fu[Option[MiniStanding]] =
|
||||
|
|
|
@ -51,6 +51,7 @@ final class Env(
|
|||
|
||||
object isStreamer {
|
||||
private val cache = lila.memo.MixedCache.single[Set[String]](
|
||||
name = "tv.streamers",
|
||||
f = streamerList.lichessIds,
|
||||
timeToLive = 10 seconds,
|
||||
default = Set.empty,
|
||||
|
@ -60,6 +61,7 @@ final class Env(
|
|||
|
||||
object streamsOnAir {
|
||||
private val cache = lila.memo.AsyncCache.single[List[StreamOnAir]](
|
||||
name = "tv.streamsOnAir",
|
||||
f = streaming.onAir,
|
||||
timeToLive = 2 seconds)
|
||||
def all = cache(true)
|
||||
|
|
|
@ -85,6 +85,7 @@ final class Cached(
|
|||
keyToString = _.toString)
|
||||
|
||||
val top50Online = lila.memo.AsyncCache.single[List[User]](
|
||||
name = "user.top50online",
|
||||
f = UserRepo.byIdsSortRating(onlineUserIdMemo.keys, 50),
|
||||
timeToLive = 10 seconds)
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ final class LightUserApi(coll: Coll) {
|
|||
}
|
||||
|
||||
private val cache = lila.memo.MixedCache[String, Option[LightUser]](
|
||||
name = "user.light",
|
||||
id => coll.find(
|
||||
$id(id),
|
||||
$doc(F.username -> true, F.title -> true, s"${F.plan}.active" -> true)
|
||||
|
|
|
@ -78,6 +78,7 @@ final class RankingApi(
|
|||
} map (_.flatten.toMap)
|
||||
|
||||
private val cache = AsyncCache[Perf.ID, Map[User.ID, Rank]](
|
||||
name = "rankingApi.weeklyStableRanking",
|
||||
f = compute,
|
||||
timeToLive = 15 minutes)
|
||||
|
||||
|
|
|
@ -49,14 +49,14 @@ private[video] final class VideoApi(
|
|||
val textScore = $doc("score" -> $doc("$meta" -> "textScore"))
|
||||
Paginator(
|
||||
adapter = new Adapter[Video](
|
||||
collection = videoColl,
|
||||
selector = $doc(
|
||||
"$text" -> $doc("$search" -> q)
|
||||
),
|
||||
projection = textScore,
|
||||
sort = textScore,
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
) mapFutureList videoViews(user),
|
||||
collection = videoColl,
|
||||
selector = $doc(
|
||||
"$text" -> $doc("$search" -> q)
|
||||
),
|
||||
projection = textScore,
|
||||
sort = textScore,
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
) mapFutureList videoViews(user),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage)
|
||||
}
|
||||
|
@ -82,12 +82,12 @@ private[video] final class VideoApi(
|
|||
|
||||
def popular(user: Option[User], page: Int): Fu[Paginator[VideoView]] = Paginator(
|
||||
adapter = new Adapter[Video](
|
||||
collection = videoColl,
|
||||
selector = $empty,
|
||||
projection = $empty,
|
||||
sort = $doc("metadata.likes" -> -1),
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
) mapFutureList videoViews(user),
|
||||
collection = videoColl,
|
||||
selector = $empty,
|
||||
projection = $empty,
|
||||
sort = $doc("metadata.likes" -> -1),
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
) mapFutureList videoViews(user),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage)
|
||||
|
||||
|
@ -95,24 +95,24 @@ private[video] final class VideoApi(
|
|||
if (tags.isEmpty) popular(user, page)
|
||||
else Paginator(
|
||||
adapter = new Adapter[Video](
|
||||
collection = videoColl,
|
||||
selector = $doc("tags" $all tags),
|
||||
projection = $empty,
|
||||
sort = $doc("metadata.likes" -> -1),
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
) mapFutureList videoViews(user),
|
||||
collection = videoColl,
|
||||
selector = $doc("tags" $all tags),
|
||||
projection = $empty,
|
||||
sort = $doc("metadata.likes" -> -1),
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
) mapFutureList videoViews(user),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage)
|
||||
|
||||
def byAuthor(user: Option[User], author: String, page: Int): Fu[Paginator[VideoView]] =
|
||||
Paginator(
|
||||
adapter = new Adapter[Video](
|
||||
collection = videoColl,
|
||||
selector = $doc("author" -> author),
|
||||
projection = $empty,
|
||||
sort = $doc("metadata.likes" -> -1),
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
) mapFutureList videoViews(user),
|
||||
collection = videoColl,
|
||||
selector = $doc("author" -> author),
|
||||
projection = $empty,
|
||||
sort = $doc("metadata.likes" -> -1),
|
||||
readPreference = ReadPreference.secondaryPreferred
|
||||
) mapFutureList videoViews(user),
|
||||
currentPage = page,
|
||||
maxPerPage = maxPerPage)
|
||||
|
||||
|
@ -129,6 +129,7 @@ private[video] final class VideoApi(
|
|||
object count {
|
||||
|
||||
private val cache = AsyncCache.single(
|
||||
name = "video.count",
|
||||
f = videoColl.count(none),
|
||||
timeToLive = 1.day)
|
||||
|
||||
|
@ -154,10 +155,11 @@ private[video] final class VideoApi(
|
|||
).some) map (0!=)
|
||||
|
||||
def seenVideoIds(user: User, videos: Seq[Video]): Fu[Set[Video.ID]] =
|
||||
viewColl.distinct[String, Set](View.BSONFields.videoId,
|
||||
viewColl.distinct[String, Set](
|
||||
View.BSONFields.videoId,
|
||||
$inIds(videos.map { v =>
|
||||
View.makeId(v.id, user.id)
|
||||
}).some)
|
||||
View.makeId(v.id, user.id)
|
||||
}).some)
|
||||
}
|
||||
|
||||
object tag {
|
||||
|
@ -173,37 +175,39 @@ private[video] final class VideoApi(
|
|||
import reactivemongo.api.collections.bson.BSONBatchCommands.AggregationFramework.{ Descending, GroupField, Match, Project, UnwindField, Sort, SumValue }
|
||||
|
||||
private val pathsCache = AsyncCache[List[Tag], List[TagNb]](
|
||||
name = "video.paths",
|
||||
f = filterTags => {
|
||||
val allPaths =
|
||||
if (filterTags.isEmpty) allPopular map { tags =>
|
||||
tags.filterNot(_.isNumeric)
|
||||
}
|
||||
else videoColl.aggregate(
|
||||
Match($doc("tags" $all filterTags)),
|
||||
List(Project($doc("tags" -> true)), UnwindField("tags"),
|
||||
GroupField("tags")("nb" -> SumValue(1)))).map(
|
||||
_.firstBatch.flatMap(_.asOpt[TagNb]))
|
||||
|
||||
allPopular zip allPaths map {
|
||||
case (all, paths) =>
|
||||
val tags = all map { t =>
|
||||
paths find (_._id == t._id) getOrElse TagNb(t._id, 0)
|
||||
} filterNot (_.empty) take max
|
||||
val missing = filterTags filterNot { t =>
|
||||
tags exists (_.tag == t)
|
||||
}
|
||||
val list = tags.take(max - missing.size) ::: missing.flatMap { t =>
|
||||
all find (_.tag == t)
|
||||
}
|
||||
list.sortBy { t =>
|
||||
if (filterTags contains t.tag) Int.MinValue
|
||||
else -t.nb
|
||||
}
|
||||
val allPaths =
|
||||
if (filterTags.isEmpty) allPopular map { tags =>
|
||||
tags.filterNot(_.isNumeric)
|
||||
}
|
||||
},
|
||||
else videoColl.aggregate(
|
||||
Match($doc("tags" $all filterTags)),
|
||||
List(Project($doc("tags" -> true)), UnwindField("tags"),
|
||||
GroupField("tags")("nb" -> SumValue(1)))).map(
|
||||
_.firstBatch.flatMap(_.asOpt[TagNb]))
|
||||
|
||||
allPopular zip allPaths map {
|
||||
case (all, paths) =>
|
||||
val tags = all map { t =>
|
||||
paths find (_._id == t._id) getOrElse TagNb(t._id, 0)
|
||||
} filterNot (_.empty) take max
|
||||
val missing = filterTags filterNot { t =>
|
||||
tags exists (_.tag == t)
|
||||
}
|
||||
val list = tags.take(max - missing.size) ::: missing.flatMap { t =>
|
||||
all find (_.tag == t)
|
||||
}
|
||||
list.sortBy { t =>
|
||||
if (filterTags contains t.tag) Int.MinValue
|
||||
else -t.nb
|
||||
}
|
||||
}
|
||||
},
|
||||
maxCapacity = 100)
|
||||
|
||||
private val popularCache = AsyncCache.single[List[TagNb]](
|
||||
name = "video.popular",
|
||||
f = videoColl.aggregate(
|
||||
Project($doc("tags" -> true)), List(
|
||||
UnwindField("tags"), GroupField("tags")("nb" -> SumValue(1)),
|
||||
|
|
Loading…
Reference in a new issue