Application.scala 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package controllers
  2. import play.api.Play.current
  3. import play.api.db.DB
  4. import play.api.mvc._
  5. import play.api.libs.json.Json
  6. import java.util.concurrent._
  7. import scala.concurrent._
  8. import models.{World, Fortune}
  9. import utils._
  10. import scala.concurrent.Future
  11. import play.api.libs.concurrent.Execution.Implicits._
  12. import play.core.NamedThreadFactory
  13. object Application extends Controller {
  14. private val TestDatabaseRows = 10000
  15. private val partitionCount = current.configuration.getInt("db.default.partitionCount").getOrElse(2)
  16. private val maxConnections =
  17. partitionCount * current.configuration.getInt("db.default.maxConnectionsPerPartition").getOrElse(5)
  18. private val minConnections =
  19. partitionCount * current.configuration.getInt("db.default.minConnectionsPerPartition").getOrElse(5)
  20. private val tpe = new ThreadPoolExecutor(minConnections, maxConnections,
  21. 0L, TimeUnit.MILLISECONDS,
  22. new LinkedBlockingQueue[Runnable](),
  23. new NamedThreadFactory("dbEc"))
  24. private val dbEc = ExecutionContext.fromExecutorService(tpe)
  25. // If the thread-pool used by the database grows too large then our server
  26. // is probably struggling, and we should start dropping requests. Set
  27. // the max size of our queue something above the number of concurrent
  28. // connections that we need to handle.
  29. def isDbQueueTooBig: Boolean = tpe.getQueue.size() <= 1024
  30. def db = PredicatedAction(isDbQueueTooBig, ServiceUnavailable) {
  31. Action.async {
  32. getRandomWorlds(1).map { worlds =>
  33. Ok(Json.toJson(worlds.head))
  34. }
  35. }
  36. }
  37. def queries(countString: String) = PredicatedAction(isDbQueueTooBig, ServiceUnavailable) {
  38. Action.async {
  39. val n = parseCount(countString)
  40. getRandomWorlds(n).map { worlds =>
  41. Ok(Json.toJson(worlds))
  42. }
  43. }
  44. }
  45. private def parseCount(s: String): Int = {
  46. try {
  47. val parsed = java.lang.Integer.parseInt(s, 10)
  48. parsed match {
  49. case i if i < 1 => 1
  50. case i if i > 500 => 500
  51. case i => i
  52. }
  53. } catch {
  54. case _: NumberFormatException => 1
  55. }
  56. }
  57. private def getRandomWorlds(n: Int): Future[Seq[World]] = Future {
  58. val random = ThreadLocalRandom.current()
  59. DB.withConnection { implicit connection =>
  60. for (_ <- 1 to n) yield {
  61. val randomId: Long = random.nextInt(TestDatabaseRows) + 1
  62. World.findById(randomId)
  63. }
  64. }
  65. }(dbEc)
  66. def fortunes() = PredicatedAction(isDbQueueTooBig, ServiceUnavailable) {
  67. Action.async {
  68. Future {
  69. val fortunes = Fortune.getAll()
  70. val extendedFortunes = Fortune(0.toLong, "Additional fortune added at request time.") +: fortunes
  71. Ok(views.html.fortune(extendedFortunes))
  72. }
  73. }
  74. }
  75. def update(countString: String) = PredicatedAction(isDbQueueTooBig, ServiceUnavailable) {
  76. Action.async {
  77. val n = parseCount(countString)
  78. Future {
  79. val random = ThreadLocalRandom.current()
  80. val worlds = DB.withConnection { implicit connection =>
  81. for(_ <- 1 to n) yield {
  82. val randomId: Long = random.nextInt(TestDatabaseRows) + 1
  83. val world = World.findById(random.nextInt(TestDatabaseRows) + 1)
  84. val updatedWorld = world.copy(randomNumber = random.nextInt(10000) + 1)
  85. World.updateRandom(updatedWorld)
  86. updatedWorld
  87. }
  88. }
  89. Ok(Json.toJson(worlds)).withHeaders("Server" -> "Netty")
  90. }(dbEc)
  91. }
  92. }
  93. }