instanciate async DBs at most one per second

this looks a bit dangerous tho
pull/9807/head
Thibault Duplessis 2021-09-15 10:46:51 +02:00
parent 6c37e20278
commit 8a4d577e74
2 changed files with 30 additions and 4 deletions

View File

@ -14,17 +14,22 @@ final class AsyncDb(
driver: AsyncDriver
)(implicit ec: scala.concurrent.ExecutionContext) {
private lazy val connection =
private lazy val connection: Fu[(MongoConnection, Option[String])] =
MongoConnection.fromString(uri) flatMap { parsedUri =>
driver.connect(parsedUri, name.some).dmap(_ -> parsedUri.db)
}
private def db: Future[DB] =
private def makeDb: Future[DB] =
connection flatMap { case (conn, dbName) =>
conn database dbName.getOrElse("lichess")
}
def apply(name: CollName) = new AsyncColl(name, () => db.dmap(_(name.value)))
private val dbCache = new SingleFutureCache[DB](
compute = () => makeDb,
expireAfterMillis = 1000
)
def apply(name: CollName) = new AsyncColl(name, () => dbCache.get.dmap(_.collection(name.value)))
}
final class Db(
@ -48,5 +53,5 @@ final class Db(
logger.info(s"MongoDB connected to $uri in ${lap.showDuration}")
}
def apply(name: CollName): Coll = db(name.value)
def apply(name: CollName): Coll = db.collection(name.value)
}

View File

@ -0,0 +1,21 @@
package lila.db
/* Will try to compute the future only once every `expireAfterMillis`,
* and serve the current future to further `get` calls.
* Not thread safe: it can compute the future more than once every `expireAfterMillis`.
* It's a deliberate design choice to improve performance
* in the cases where accidental duplicate calls don't matter. */
final private class SingleFutureCache[A](compute: () => Fu[A], expireAfterMillis: Int) {
private var current: Fu[A] = fufail("SingleFutureCache.empty")
private var expiresAt: Long = 0
def get: Fu[A] = {
val now = nowMillis
if (now > expiresAt) {
expiresAt = now + expireAfterMillis
current = compute()
}
current
}
}