limit study chapter nodes to 3000

I've seen some with 35,000 nodes on prod.
It works but it's slow. And pointless.
pull/4019/head
Thibault Duplessis 2018-02-04 22:20:54 -05:00
parent 2f89731d4e
commit a457bc3e7c
4 changed files with 22 additions and 5 deletions

View File

@ -79,10 +79,16 @@ case class Chapter(
def withoutChildren = copy(root = root.withoutChildren)
def relayAndTags = relay map { Chapter.RelayAndTags(id, _, tags) }
def isOverweight = root.children.countRecursive >= Chapter.maxNodes
}
object Chapter {
// I've seen chapters with 35,000 nodes on prod.
// It works but could be used for DoS.
val maxNodes = 3000
case class Id(value: String) extends AnyVal with StringValue
implicit val idIso = lila.common.Iso.string[Id](Id.apply, _.value)

View File

@ -21,7 +21,10 @@ private final class ExplorerGame(
}
def insert(userId: User.ID, study: Study, position: Position, gameId: Game.ID): Fu[Option[(Chapter, Path)]] =
importer(gameId) map {
if (position.chapter.isOverweight) {
logger.info(s"Overweight chapter ${study.id}/${position.chapter.id}")
fuccess(none)
} else importer(gameId) map {
_ ?? { game =>
position.node ?? { fromNode =>
GameToRoot(game, none, false).|> { root =>

View File

@ -198,6 +198,10 @@ object Node {
case (node, index) => node.children.pathToIndexes(tail).map(rest => index :: rest)
}
}
def countRecursive: Int = nodes.foldLeft(nodes.size) {
case (count, n) => count + n.children.countRecursive
}
}
val emptyChildren = Children(Vector.empty)

View File

@ -192,12 +192,16 @@ final class StudyApi(
}
}
private[study] def doAddNode(userId: User.ID, study: Study, position: Position, rawNode: Node, uid: Uid, opts: MoveOpts, relay: Option[Chapter.Relay]): Funit = {
private def doAddNode(userId: User.ID, study: Study, position: Position, rawNode: Node, uid: Uid, opts: MoveOpts, relay: Option[Chapter.Relay]): Funit = {
val node = rawNode.withoutChildren
position.chapter.addNode(node, position.path, relay) match {
def failReload = reloadUidBecauseOf(study, uid, position.chapter.id)
if (position.chapter.isOverweight) {
logger.info(s"Overweight chapter ${study.id}/${position.chapter.id}")
fuccess(failReload)
} else position.chapter.addNode(node, position.path, relay) match {
case None =>
fufail(s"Invalid addNode ${study.id} ${position.ref} $node") >>-
reloadUidBecauseOf(study, uid, position.chapter.id) inject none
failReload
fufail(s"Invalid addNode ${study.id} ${position.ref} $node")
case Some(chapter) =>
chapter.root.nodeAt(position.path) ?? { parent =>
val newPosition = position.ref + node