better handle blocking IO and monitor it by name
parent
cfa31551fc
commit
e5880aba38
|
@ -51,7 +51,7 @@ object Environment
|
|||
|
||||
def isChatPanicEnabled = env.chat.panic.enabled
|
||||
|
||||
def blockingReportNbOpen: Int = env.report.api.nbOpen.awaitOrElse(10.millis, 0)
|
||||
def blockingReportNbOpen: Int = env.report.api.nbOpen.awaitOrElse(10.millis, "nbReports", 0)
|
||||
|
||||
def NotForKids(f: => Frag)(implicit ctx: Context) = if (ctx.kid) emptyFrag else f
|
||||
|
||||
|
|
|
@ -3,11 +3,12 @@ package lila.base
|
|||
import akka.actor.ActorSystem
|
||||
import scala.util.Try
|
||||
|
||||
import lila.common.Chronometer
|
||||
import LilaTypes._
|
||||
import ornicar.scalalib.Zero
|
||||
import scala.collection.BuildFrom
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ ExecutionContext => EC, Future }
|
||||
import scala.concurrent.{ ExecutionContext => EC, Future, Await, blocking }
|
||||
|
||||
final class PimpedFuture[A](private val fua: Fu[A]) extends AnyVal {
|
||||
|
||||
|
@ -104,30 +105,39 @@ final class PimpedFuture[A](private val fua: Fu[A]) extends AnyVal {
|
|||
fua
|
||||
}
|
||||
|
||||
def await(duration: FiniteDuration): A =
|
||||
scala.concurrent.Await.result(fua, duration)
|
||||
def await(duration: FiniteDuration, name: String): A =
|
||||
Chronometer.syncMon(_.blocking.time(name)) {
|
||||
if (duration.toMillis >= 100) blocking {
|
||||
Await.result(fua, duration)
|
||||
} else {
|
||||
Await.result(fua, duration)
|
||||
}
|
||||
}
|
||||
|
||||
def awaitOrElse(duration: FiniteDuration, default: => A): A =
|
||||
def awaitOrElse(duration: FiniteDuration, name: String, default: => A): A =
|
||||
try {
|
||||
scala.concurrent.Await.result(fua, duration)
|
||||
await(duration, name)
|
||||
} catch {
|
||||
case _: Exception => default
|
||||
}
|
||||
|
||||
def awaitSeconds(seconds: Int): A =
|
||||
await(seconds.seconds)
|
||||
|
||||
def withTimeout(duration: FiniteDuration)(implicit ec: EC, system: ActorSystem): Fu[A] =
|
||||
withTimeout(duration, LilaException(s"Future timed out after $duration"))
|
||||
|
||||
def withTimeout(duration: FiniteDuration, error: => Throwable)(implicit ec: EC, system: ActorSystem): Fu[A] = {
|
||||
def withTimeout(
|
||||
duration: FiniteDuration,
|
||||
error: => Throwable
|
||||
)(implicit ec: EC, system: ActorSystem): Fu[A] = {
|
||||
Future firstCompletedOf Seq(
|
||||
fua,
|
||||
akka.pattern.after(duration, system.scheduler)(Future failed error)
|
||||
)
|
||||
}
|
||||
|
||||
def withTimeoutDefault(duration: FiniteDuration, default: => A)(implicit ec: EC, system: ActorSystem): Fu[A] = {
|
||||
def withTimeoutDefault(
|
||||
duration: FiniteDuration,
|
||||
default: => A
|
||||
)(implicit ec: EC, system: ActorSystem): Fu[A] = {
|
||||
Future firstCompletedOf Seq(
|
||||
fua,
|
||||
akka.pattern.after(duration, system.scheduler)(Future(default))
|
||||
|
@ -137,8 +147,8 @@ final class PimpedFuture[A](private val fua: Fu[A]) extends AnyVal {
|
|||
def delay(duration: FiniteDuration)(implicit ec: EC, system: ActorSystem) =
|
||||
lila.common.Future.delay(duration)(fua)
|
||||
|
||||
def chronometer = lila.common.Chronometer(fua)
|
||||
def chronometerTry = lila.common.Chronometer.lapTry(fua)
|
||||
def chronometer = Chronometer(fua)
|
||||
def chronometerTry = Chronometer.lapTry(fua)
|
||||
|
||||
def mon(path: lila.mon.TimerPath) = chronometer.mon(path).result
|
||||
def monTry(path: Try[A] => lila.mon.TimerPath) = chronometerTry.mon(r => path(r)(lila.mon)).result
|
||||
|
@ -189,7 +199,7 @@ final class PimpedFutureOption[A](private val fua: Fu[Option[A]]) extends AnyVal
|
|||
def getOrElse(other: => Fu[A])(implicit ec: EC): Fu[A] = fua flatMap { _.fold(other)(fuccess) }
|
||||
|
||||
def map2[B](f: A => B)(implicit ec: EC): Fu[Option[B]] = fua.map(_ map f)
|
||||
def dmap2[B](f: A => B): Fu[Option[B]] = fua.map(_ map f)(EC.parasitic)
|
||||
def dmap2[B](f: A => B): Fu[Option[B]] = fua.map(_ map f)(EC.parasitic)
|
||||
}
|
||||
|
||||
// final class PimpedFutureValid[A](private val fua: Fu[Valid[A]]) extends AnyVal {
|
||||
|
|
|
@ -506,6 +506,9 @@ object mon {
|
|||
object bus {
|
||||
val classifiers = gauge("bus.classifiers").withoutTags
|
||||
}
|
||||
object blocking {
|
||||
def time(name: String) = timer("blocking.time").withTag("name", name)
|
||||
}
|
||||
|
||||
type TimerPath = lila.mon.type => Timer
|
||||
type CounterPath = lila.mon.type => Counter
|
||||
|
|
|
@ -4,7 +4,7 @@ import com.github.ghik.silencer.silent
|
|||
import reactivemongo.api._
|
||||
import reactivemongo.api.commands.Command
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ Await, Future }
|
||||
import scala.concurrent.Future
|
||||
|
||||
import dsl.Coll
|
||||
import lila.common.Chronometer
|
||||
|
@ -36,10 +36,10 @@ final class Db(
|
|||
private val dbName = uri.db | "lichess"
|
||||
|
||||
private lazy val db: DefaultDB = Chronometer.syncEffect(
|
||||
Await.result(
|
||||
driver.connect(uri, name.some).flatMap(_ database dbName),
|
||||
5.seconds
|
||||
)
|
||||
driver
|
||||
.connect(uri, name.some)
|
||||
.flatMap(_ database dbName)
|
||||
.await(5.seconds, s"db:$name")
|
||||
) { lap =>
|
||||
logger.info(s"MongoDB connected to $dbName in ${lap.showDuration}")
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package lila.hub
|
|||
|
||||
import akka.pattern.ask
|
||||
import play.api.data._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import actorApi.captcha._
|
||||
import lila.common.Captcha
|
||||
|
@ -28,7 +29,7 @@ trait CaptchedForm {
|
|||
|
||||
import scala.language.reflectiveCalls
|
||||
def validateCaptcha(data: CaptchedData) =
|
||||
getCaptcha(data.gameId) awaitSeconds 2 valid data.move.trim.toLowerCase
|
||||
getCaptcha(data.gameId).await(2 seconds, "getCaptcha") valid data.move.trim.toLowerCase
|
||||
|
||||
def captchaFailMessage = Captcha.failMessage
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ final class Syncache[K, V](
|
|||
private def waitForResult(k: K, fu: Fu[V], duration: FiniteDuration): V = {
|
||||
try {
|
||||
lila.common.Chronometer.syncMon(_ => recWait) {
|
||||
fu await duration
|
||||
fu.await(duration, "syncache")
|
||||
}
|
||||
} catch {
|
||||
case _: java.util.concurrent.TimeoutException =>
|
||||
|
|
|
@ -2,6 +2,7 @@ package lila.message
|
|||
|
||||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.security.Granter
|
||||
import lila.user.{ User, UserRepo }
|
||||
|
@ -17,11 +18,11 @@ final private[message] class DataForm(
|
|||
Form(
|
||||
mapping(
|
||||
"username" -> lila.user.DataForm.historicalUsernameField
|
||||
.verifying("Unknown username", { fetchUser(_).isDefined })
|
||||
.verifying("Unknown username", { blockingFetchUser(_).isDefined })
|
||||
.verifying(
|
||||
"Sorry, this player doesn't accept new messages", { name =>
|
||||
Granter(_.MessageAnyone)(me) || {
|
||||
security.canMessage(me.id, User normalize name) awaitSeconds 2 // damn you blocking API
|
||||
security.canMessage(me.id, User normalize name).await(2 seconds, "pmAccept") // damn you blocking API
|
||||
}
|
||||
}
|
||||
),
|
||||
|
@ -31,7 +32,7 @@ final private[message] class DataForm(
|
|||
)({
|
||||
case (username, subject, text, mod) =>
|
||||
ThreadData(
|
||||
user = fetchUser(username) err "Unknown username " + username,
|
||||
user = blockingFetchUser(username) err "Unknown username " + username,
|
||||
subject = subject,
|
||||
text = text,
|
||||
asMod = mod.isDefined
|
||||
|
@ -46,7 +47,7 @@ final private[message] class DataForm(
|
|||
)
|
||||
)
|
||||
|
||||
private def fetchUser(username: String) = userRepo named username awaitSeconds 2
|
||||
private def blockingFetchUser(username: String) = userRepo.named(username).await(1 second, "pmUser")
|
||||
}
|
||||
|
||||
object DataForm {
|
||||
|
|
|
@ -3,6 +3,7 @@ package lila.report
|
|||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
import play.api.data.validation._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.user.{ User, UserRepo }
|
||||
|
||||
|
@ -21,7 +22,7 @@ final private[report] class DataForm(
|
|||
val create = Form(
|
||||
mapping(
|
||||
"username" -> lila.user.DataForm.historicalUsernameField.verifying("Unknown username", {
|
||||
fetchUser(_).isDefined
|
||||
blockingFetchUser(_).isDefined
|
||||
}),
|
||||
"reason" -> text.verifying("error.required", Reason.keys contains _),
|
||||
"text" -> text(minLength = 5, maxLength = 2000),
|
||||
|
@ -30,7 +31,7 @@ final private[report] class DataForm(
|
|||
)({
|
||||
case (username, reason, text, gameId, move) =>
|
||||
ReportSetup(
|
||||
user = fetchUser(username) err "Unknown username " + username,
|
||||
user = blockingFetchUser(username) err "Unknown username " + username,
|
||||
reason = reason,
|
||||
text = text,
|
||||
gameId = gameId,
|
||||
|
@ -49,7 +50,8 @@ final private[report] class DataForm(
|
|||
)(ReportFlag.apply)(ReportFlag.unapply)
|
||||
)
|
||||
|
||||
private def fetchUser(username: String) = userRepo named username awaitSeconds 2
|
||||
private def blockingFetchUser(username: String) =
|
||||
userRepo.named(username).await(1.second, "reportUser")
|
||||
}
|
||||
|
||||
private[report] case class ReportFlag(
|
||||
|
|
|
@ -3,6 +3,7 @@ package lila.security
|
|||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
import play.api.data.validation.Constraints
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.common.{ EmailAddress, LameName }
|
||||
import lila.user.{ TotpSecret, User, UserRepo }
|
||||
|
@ -14,7 +15,8 @@ final class DataForm(
|
|||
authenticator: lila.user.Authenticator,
|
||||
emailValidator: EmailAddressValidator,
|
||||
lameNameCheck: LameNameCheck
|
||||
)(implicit ec: scala.concurrent.ExecutionContext) extends lila.hub.CaptchedForm {
|
||||
)(implicit ec: scala.concurrent.ExecutionContext)
|
||||
extends lila.hub.CaptchedForm {
|
||||
|
||||
import DataForm._
|
||||
|
||||
|
@ -67,7 +69,7 @@ final class DataForm(
|
|||
)
|
||||
)
|
||||
.verifying("usernameUnacceptable", u => !lameNameCheck.value || !LameName.username(u))
|
||||
.verifying("usernameAlreadyUsed", u => !userRepo.nameExists(u).awaitSeconds(4))
|
||||
.verifying("usernameAlreadyUsed", u => !userRepo.nameExists(u).await(3 seconds, "signupUsername"))
|
||||
|
||||
private val agreementBool = boolean.verifying(b => b)
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ final class EmailAddressValidator(
|
|||
|
||||
def uniqueConstraint(forUser: Option[User]) = Constraint[String]("constraint.email_unique") { e =>
|
||||
val email = EmailAddress(e)
|
||||
val (taken, reused) = (isTakenBySomeoneElse(email, forUser) zip wasUsedTwiceRecently(email)) awaitSeconds 2
|
||||
val (taken, reused) = (isTakenBySomeoneElse(email, forUser) zip wasUsedTwiceRecently(email)).await(2 seconds, "emailUnique")
|
||||
if (taken || reused) Invalid(ValidationError("error.email_unique"))
|
||||
else Valid
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ final class EmailAddressValidator(
|
|||
|
||||
// the DNS emails should have been preloaded
|
||||
private[security] val withAcceptableDns = Constraint[String]("constraint.email_acceptable") { e =>
|
||||
val ok = hasAcceptableDns(EmailAddress(e)).awaitOrElse(100.millis, {
|
||||
val ok = hasAcceptableDns(EmailAddress(e)).awaitOrElse(90.millis, "dns", {
|
||||
logger.warn(s"EmailAddressValidator.withAcceptableDns timeout! ${e} records should have been preloaded")
|
||||
true
|
||||
})
|
||||
|
|
|
@ -5,7 +5,6 @@ import akka.stream.scaladsl._
|
|||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
import play.api.libs.json._
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import chess.format.pgn.Tag
|
||||
|
@ -110,7 +109,7 @@ final class StudySearchApi(
|
|||
date
|
||||
case _ =>
|
||||
logger.info("Reset study index")
|
||||
Await.result(c.putMapping, 20 seconds)
|
||||
c.putMapping.await(10.seconds, "studyMapping")
|
||||
parseDate("2011-01-01").get
|
||||
}
|
||||
logger.info(s"Index to ${c.index.name} since $since")
|
||||
|
|
|
@ -2,6 +2,7 @@ package lila.team
|
|||
|
||||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.db.dsl._
|
||||
|
||||
|
@ -28,7 +29,7 @@ final private[team] class DataForm(
|
|||
Fields.gameId,
|
||||
Fields.move
|
||||
)(TeamSetup.apply)(TeamSetup.unapply)
|
||||
.verifying("This team already exists", d => !teamExists(d).awaitSeconds(2))
|
||||
.verifying("This team already exists", d => !teamExists(d).await(2 seconds, "teamExists"))
|
||||
.verifying(captchaFailMessage, validateCaptcha _)
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue