Application.java 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package controllers;
  2. import akka.dispatch.ExecutionContexts;
  3. import models.World;
  4. import play.Play;
  5. import play.core.NamedThreadFactory;
  6. import play.libs.F;
  7. import play.libs.Json;
  8. import play.mvc.Controller;
  9. import play.mvc.Result;
  10. import scala.concurrent.ExecutionContext;
  11. import utils.Predicate;
  12. import utils.Predicated;
  13. import java.util.ArrayList;
  14. import java.util.List;
  15. import java.util.Random;
  16. import java.util.concurrent.*;
  17. public class Application extends Controller {
  18. private static final int MAX_QUERIES_PER_REQUEST = 20;
  19. private static final int TEST_DATABASE_ROWS = 10000;
  20. private static final int partitionCount = Play.application().configuration().getInt("db.default.partitionCount");
  21. private static final int maxConnections =
  22. partitionCount * Play.application().configuration().getInt("db.default.maxConnectionsPerPartition");
  23. private static final int minConnections =
  24. partitionCount * Play.application().configuration().getInt("db.default.minConnectionsPerPartition");
  25. private static final ThreadPoolExecutor tpe = new ThreadPoolExecutor(minConnections, maxConnections,
  26. 0L, TimeUnit.MILLISECONDS,
  27. new LinkedBlockingQueue<Runnable>(),
  28. new NamedThreadFactory("dbEc"));
  29. private static final ExecutionContext dbEc = ExecutionContexts.fromExecutorService(tpe);
  30. // If the thread-pool used by the database grows too large then our server
  31. // is probably struggling, and we should start dropping requests. Set
  32. // the max size of our queue something above the number of concurrent
  33. // connections that we need to handle.
  34. public static class IsDbAvailable implements Predicate {
  35. @Override
  36. public boolean condition() {
  37. return tpe.getQueue().size() <= 1024;
  38. }
  39. }
  40. @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
  41. public static F.Promise<Result> db() {
  42. return getRandomWorlds(1).map(new F.Function<List<World>, Result>() {
  43. @Override
  44. public Result apply(List<World> worlds) {
  45. return ok(Json.toJson(worlds.get(0)));
  46. }
  47. });
  48. }
  49. @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
  50. public static F.Promise<Result> queries(final String queryCountString) {
  51. int queryCount;
  52. try {
  53. queryCount = Integer.parseInt(queryCountString, 10);
  54. } catch (NumberFormatException e) {
  55. queryCount = 1;
  56. }
  57. if (queryCount < 1) {
  58. queryCount = 1;
  59. } else if (queryCount > 500) {
  60. queryCount = 500;
  61. }
  62. return getRandomWorlds(queryCount).map(new F.Function<List<World>, Result>() {
  63. @Override
  64. public Result apply(List<World> worlds) {
  65. return ok(Json.toJson(worlds));
  66. }
  67. });
  68. }
  69. private static F.Promise<List<World>> getRandomWorlds(final int n) {
  70. return F.Promise.promise(new F.Function0<List<World>>() {
  71. @Override
  72. public List<World> apply() {
  73. Random random = ThreadLocalRandom.current();
  74. List<World> worlds = new ArrayList<World>(n);
  75. for (int i = 0; i < n; ++i) {
  76. long randomId = random.nextInt(TEST_DATABASE_ROWS) + 1;
  77. World world = World.find.byId(randomId);
  78. worlds.add(world);
  79. }
  80. return worlds;
  81. }
  82. }, dbEc);
  83. }
  84. }