Browse Source

Play2: Change throughput and switch to thread-pool-executor (for db benchmarks) (#4556)

* Play 2: parallelism-factor = 1.0

* Play2: Use thread-pool-executor for all database benchmarks

* Play 2: throughput = 64
Matthias Kurz 6 years ago
parent
commit
dc837a9e0a

+ 26 - 43
frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/controllers/Application.java

@@ -4,56 +4,41 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Random;
 import java.util.Random;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
-import javax.inject.Inject;
-
 import models.Fortune;
 import models.Fortune;
 import models.World;
 import models.World;
 import play.libs.Json;
 import play.libs.Json;
 import play.mvc.Controller;
 import play.mvc.Controller;
 import play.mvc.Result;
 import play.mvc.Result;
-import utils.DatabaseExecutionContext;
 
 
 public class Application extends Controller {
 public class Application extends Controller {
 
 
-    private final DatabaseExecutionContext dbEc;
-
-    @Inject
-    public Application(final DatabaseExecutionContext dbEc) {
-        this.dbEc = dbEc;
-    }
-
-    public CompletionStage<Result> db() {
-        return getRandomWorlds(1).thenApply(worlds -> ok(Json.toJson(worlds.get(0))));
+    public Result db() {
+        return ok(Json.toJson(getRandomWorlds(1).get(0)));
     }
     }
 
 
-    public CompletionStage<Result> queries(final String queries) {
-        return getRandomWorlds(queryCount(queries)).thenApply(worlds -> ok(Json.toJson(worlds)));
+    public Result queries(final String queries) {
+        return ok(Json.toJson(getRandomWorlds(queryCount(queries))));
     }
     }
 
 
-    public CompletionStage<Result> fortunes() {
-        return CompletableFuture.supplyAsync(() -> {
-            final List<Fortune> fortunes = Fortune.findAll();
-            fortunes.add(new Fortune("Additional fortune added at request time."));
-            Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
+    public Result fortunes() {
+        final List<Fortune> fortunes = Fortune.findAll();
+        fortunes.add(new Fortune("Additional fortune added at request time."));
+        Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
 
 
-            return ok(views.html.fortunes.render(fortunes));
-        }, dbEc);
+        return ok(views.html.fortunes.render(fortunes));
     }
     }
 
 
-    public CompletionStage<Result> update(final String queries) {
-        return getRandomWorlds(queryCount(queries)).thenApplyAsync(worlds -> {
-            final Random random = ThreadLocalRandom.current();
-            for (final World world : worlds) {
-                world.randomNumber = (long) (random.nextInt(10000) + 1);
-            }
+    public Result update(final String queries) {
+        final List<World> worlds = getRandomWorlds(queryCount(queries));
+        final Random random = ThreadLocalRandom.current();
+        for (final World world : worlds) {
+            world.randomNumber = (long) (random.nextInt(10000) + 1);
+        }
 
 
-            final List<World> updatedWorlds = World.save(worlds);
-            return ok(Json.toJson(updatedWorlds));
-        }, dbEc);
+        final List<World> updatedWorlds = World.save(worlds);
+        return ok(Json.toJson(updatedWorlds));
     }
     }
 
 
     private int queryCount(final String queryCountString) {
     private int queryCount(final String queryCountString) {
@@ -72,17 +57,15 @@ public class Application extends Controller {
         return queryCount;
         return queryCount;
     }
     }
 
 
-    private CompletionStage<List<World>> getRandomWorlds(final int n) {
-        return CompletableFuture.supplyAsync(() -> {
-            final Random random = ThreadLocalRandom.current();
-            final List<World> worlds = new ArrayList<>(n);
-            for (int i = 0; i < n; ++i) {
-                long randomId = random.nextInt(10000) + 1;
-                final World world = World.find(randomId);
-                worlds.add(world);
-            }
-            return worlds;
-        }, dbEc);
+    private List<World> getRandomWorlds(final int n) {
+        final Random random = ThreadLocalRandom.current();
+        final List<World> worlds = new ArrayList<>(n);
+        for (int i = 0; i < n; ++i) {
+            long randomId = random.nextInt(10000) + 1;
+            final World world = World.find(randomId);
+            worlds.add(world);
+        }
+        return worlds;
     }
     }
 
 
 }
 }

+ 11 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/application.conf

@@ -50,7 +50,13 @@ play.server {
 akka {
 akka {
   actor {
   actor {
     default-dispatcher {
     default-dispatcher {
+      executor = "thread-pool-executor"
+
       fork-join-executor {
       fork-join-executor {
+        # one thread per core is enough
+        # https://github.com/playframework/playframework/issues/7242#issuecomment-295215448
+        parallelism-factor = 1.0
+
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         parallelism-max = 14
         parallelism-max = 14
@@ -59,6 +65,11 @@ akka {
         task-peeking-mode="LIFO" # based on https://www.playframework.com/documentation/2.7.x/Migration24#Thread-pool-configuration
         task-peeking-mode="LIFO" # based on https://www.playframework.com/documentation/2.7.x/Migration24#Thread-pool-configuration
       }
       }
 
 
+      # https://www.playframework.com/documentation/2.7.x/ThreadPools#Highly-synchronous
+      thread-pool-executor {
+        fixed-pool-size = 44 # db conn pool (29) + number of cores (14) + housekeeping (1)
+      }
+
       # https://doc.akka.io/docs/akka/2.5.11/dispatchers.html#looking-up-a-dispatcher
       # https://doc.akka.io/docs/akka/2.5.11/dispatchers.html#looking-up-a-dispatcher
       # Throughput defines the maximum number of messages to be
       # Throughput defines the maximum number of messages to be
       # processed per actor before the thread jumps to the next actor.
       # processed per actor before the thread jumps to the next actor.

+ 44 - 53
frameworks/Java/play2-java/play2-java-jooq-hikaricp/app/controllers/Application.java

@@ -7,8 +7,6 @@ import static play.mvc.Http.MimeTypes.JSON;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Random;
 import java.util.Random;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
 import javax.inject.Inject;
 import javax.inject.Inject;
@@ -25,7 +23,6 @@ import models.tables.records.WorldRecord;
 import play.db.Database;
 import play.db.Database;
 import play.mvc.Controller;
 import play.mvc.Controller;
 import play.mvc.Result;
 import play.mvc.Result;
-import utils.DatabaseExecutionContext;
 
 
 public class Application extends Controller {
 public class Application extends Controller {
 
 
@@ -33,53 +30,49 @@ public class Application extends Controller {
     private static final JSONFormat JSON_FORMAT = JSONFormat.DEFAULT_FOR_RECORDS.recordFormat(RecordFormat.OBJECT);
     private static final JSONFormat JSON_FORMAT = JSONFormat.DEFAULT_FOR_RECORDS.recordFormat(RecordFormat.OBJECT);
 
 
     private final Database db;
     private final Database db;
-    private final DatabaseExecutionContext dbEc;
 
 
     @Inject
     @Inject
-    public Application(final Database db, final DatabaseExecutionContext dbEc) {
+    public Application(final Database db) {
         this.db = db;
         this.db = db;
-        this.dbEc = dbEc;
     }
     }
 
 
-    public CompletionStage<Result> db() {
-        return getRandomWorlds(1).thenApply(worlds -> ok(worlds.get(0).formatJSON(JSON_FORMAT)).as(JSON));
+    public Result db() {
+        return ok(getRandomWorlds(1).get(0).formatJSON(JSON_FORMAT)).as(JSON);
     }
     }
 
 
-    public CompletionStage<Result> queries(final String queries) {
-        return getRandomWorlds(queryCount(queries)).thenApply(worlds -> ok(worlds.formatJSON(JSON_FORMAT)).as(JSON));
+    public Result queries(final String queries) {
+        return ok(getRandomWorlds(queryCount(queries)).formatJSON(JSON_FORMAT)).as(JSON);
     }
     }
 
 
-    public CompletionStage<Result> fortunes() {
-        return CompletableFuture.supplyAsync(() -> {
-            final List<FortuneRecord> fortunes = this.db.withConnection(connection -> {
-                return DSL.using(connection, DIALECT).select(FORTUNE.ID, FORTUNE.MESSAGE).from(FORTUNE).fetchInto(FortuneRecord.class);
-            });
-            fortunes.add(new FortuneRecord(UInteger.valueOf(0), "Additional fortune added at request time."));
-            Collections.sort(fortunes, (f1, f2) -> f1.getMessage().compareTo(f2.getMessage()));
+    public Result fortunes() {
+        final List<FortuneRecord> fortunes = this.db.withConnection(connection -> {
+            return DSL.using(connection, DIALECT).select(FORTUNE.ID, FORTUNE.MESSAGE).from(FORTUNE).fetchInto(FortuneRecord.class);
+        });
+        fortunes.add(new FortuneRecord(UInteger.valueOf(0), "Additional fortune added at request time."));
+        Collections.sort(fortunes, (f1, f2) -> f1.getMessage().compareTo(f2.getMessage()));
 
 
-            return ok(views.html.fortunes.render(fortunes));
-        }, dbEc);
+        return ok(views.html.fortunes.render(fortunes));
     }
     }
 
 
-    public CompletionStage<Result> update(final String queries) {
-        return getRandomWorlds(queryCount(queries)).thenApplyAsync(worlds -> {
-            final Random random = ThreadLocalRandom.current();
-            for (final WorldRecord world : worlds) {
-                world.setRandomnumber((random.nextInt(10000) + 1));
-            }
+    public Result update(final String queries) {
+        final org.jooq.Result<WorldRecord> worlds =  getRandomWorlds(queryCount(queries));
 
 
-            final int batchSize = 25;
-            final int batches = ((worlds.size() / batchSize) + 1);
-            this.db.withConnection(connection -> {
-                final DSLContext sql = DSL.using(connection, DIALECT);
-                for ( int i = 0 ; i < batches ; ++i ) {
-                    sql.batchUpdate(worlds.subList(i * batchSize, Math.min((i + 1) * batchSize, worlds.size()))).execute();
-                }
-                return null;
-            });
+        final Random random = ThreadLocalRandom.current();
+        for (final WorldRecord world : worlds) {
+            world.setRandomnumber((random.nextInt(10000) + 1));
+        }
 
 
-            return ok(worlds.formatJSON(JSON_FORMAT)).as(JSON);
-        }, dbEc);
+        final int batchSize = 25;
+        final int batches = ((worlds.size() / batchSize) + 1);
+        this.db.withConnection(connection -> {
+            final DSLContext sql = DSL.using(connection, DIALECT);
+            for ( int i = 0 ; i < batches ; ++i ) {
+                sql.batchUpdate(worlds.subList(i * batchSize, Math.min((i + 1) * batchSize, worlds.size()))).execute();
+            }
+            return null;
+        });
+
+        return ok(worlds.formatJSON(JSON_FORMAT)).as(JSON);
     }
     }
 
 
     private int queryCount(final String queryCountString) {
     private int queryCount(final String queryCountString) {
@@ -98,24 +91,22 @@ public class Application extends Controller {
         return queryCount;
         return queryCount;
     }
     }
 
 
-    private CompletionStage<org.jooq.Result<WorldRecord>> getRandomWorlds(final int n) {
-        return CompletableFuture.supplyAsync(() -> {
-            final Random random = ThreadLocalRandom.current();
-            org.jooq.Result<WorldRecord> worlds = null;
-            for (int i = 0; i < n; ++i) {
-                long randomId = random.nextInt(10000) + 1;
-                final org.jooq.Result<WorldRecord> world = this.db.withConnection(connection -> {
-                    return DSL.using(connection, DIALECT).selectFrom(WORLD).where(WORLD.ID.eq(UInteger.valueOf(randomId))).fetch();
-                });
-
-                if(worlds == null) {
-                    worlds = world;
-                } else {
-                    worlds.add(world.get(0));
-                }
+    private org.jooq.Result<WorldRecord> getRandomWorlds(final int n) {
+        final Random random = ThreadLocalRandom.current();
+        org.jooq.Result<WorldRecord> worlds = null;
+        for (int i = 0; i < n; ++i) {
+            long randomId = random.nextInt(10000) + 1;
+            final org.jooq.Result<WorldRecord> world = this.db.withConnection(connection -> {
+                return DSL.using(connection, DIALECT).selectFrom(WORLD).where(WORLD.ID.eq(UInteger.valueOf(randomId))).fetch();
+            });
+
+            if(worlds == null) {
+                worlds = world;
+            } else {
+                worlds.add(world.get(0));
             }
             }
-            return worlds;
-        }, dbEc);
+        }
+        return worlds;
     }
     }
 
 
 }
 }

+ 11 - 0
frameworks/Java/play2-java/play2-java-jooq-hikaricp/conf/application.conf

@@ -50,7 +50,13 @@ play.server {
 akka {
 akka {
   actor {
   actor {
     default-dispatcher {
     default-dispatcher {
+      executor = "thread-pool-executor"
+
       fork-join-executor {
       fork-join-executor {
+        # one thread per core is enough
+        # https://github.com/playframework/playframework/issues/7242#issuecomment-295215448
+        parallelism-factor = 1.0
+
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         parallelism-max = 14
         parallelism-max = 14
@@ -59,6 +65,11 @@ akka {
         task-peeking-mode="LIFO" # based on https://www.playframework.com/documentation/2.7.x/Migration24#Thread-pool-configuration
         task-peeking-mode="LIFO" # based on https://www.playframework.com/documentation/2.7.x/Migration24#Thread-pool-configuration
       }
       }
 
 
+      # https://www.playframework.com/documentation/2.7.x/ThreadPools#Highly-synchronous
+      thread-pool-executor {
+        fixed-pool-size = 44 # db conn pool (29) + number of cores (14) + housekeeping (1)
+      }
+
       # https://doc.akka.io/docs/akka/2.5.11/dispatchers.html#looking-up-a-dispatcher
       # https://doc.akka.io/docs/akka/2.5.11/dispatchers.html#looking-up-a-dispatcher
       # Throughput defines the maximum number of messages to be
       # Throughput defines the maximum number of messages to be
       # processed per actor before the thread jumps to the next actor.
       # processed per actor before the thread jumps to the next actor.

+ 27 - 37
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/controllers/Application.java

@@ -4,8 +4,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Random;
 import java.util.Random;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadLocalRandom;
 
 
 import javax.inject.Inject;
 import javax.inject.Inject;
@@ -16,47 +14,41 @@ import play.db.jpa.JPAApi;
 import play.libs.Json;
 import play.libs.Json;
 import play.mvc.Controller;
 import play.mvc.Controller;
 import play.mvc.Result;
 import play.mvc.Result;
-import utils.DatabaseExecutionContext;
 
 
 public class Application extends Controller {
 public class Application extends Controller {
 
 
     private final JPAApi jpa;
     private final JPAApi jpa;
-    private final DatabaseExecutionContext dbEc;
 
 
     @Inject
     @Inject
-    public Application(final JPAApi jpa, final DatabaseExecutionContext dbEc) {
+    public Application(final JPAApi jpa) {
         this.jpa = jpa;
         this.jpa = jpa;
-        this.dbEc = dbEc;
     }
     }
 
 
-    public CompletionStage<Result> db() {
-        return getRandomWorlds(1).thenApply(worlds -> ok(Json.toJson(worlds.get(0))));
+    public Result db() {
+        return ok(Json.toJson(getRandomWorlds(1).get(0)));
     }
     }
 
 
-    public CompletionStage<Result> queries(final String queries) {
-        return getRandomWorlds(queryCount(queries)).thenApply(worlds -> ok(Json.toJson(worlds)));
+    public Result queries(final String queries) {
+        return ok(Json.toJson(getRandomWorlds(queryCount(queries))));
     }
     }
 
 
-    public CompletionStage<Result> fortunes() {
-        return CompletableFuture.supplyAsync(() -> {
-            final List<Fortune> fortunes = Fortune.findAll(this.jpa);
-            fortunes.add(new Fortune("Additional fortune added at request time."));
-            Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
+    public Result fortunes() {
+        final List<Fortune> fortunes = Fortune.findAll(this.jpa);
+        fortunes.add(new Fortune("Additional fortune added at request time."));
+        Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
 
 
-            return ok(views.html.fortunes.render(fortunes));
-        }, dbEc);
+        return ok(views.html.fortunes.render(fortunes));
     }
     }
 
 
-    public CompletionStage<Result> update(final String queries) {
-        return getRandomWorlds(queryCount(queries)).thenApplyAsync(worlds -> {
-            final Random random = ThreadLocalRandom.current();
-            for (final World world : worlds) {
-                world.randomNumber = (long) (random.nextInt(10000) + 1);
-            }
+    public Result update(final String queries) {
+        final List<World> worlds = getRandomWorlds(queryCount(queries));
+        final Random random = ThreadLocalRandom.current();
+        for (final World world : worlds) {
+            world.randomNumber = (long) (random.nextInt(10000) + 1);
+        }
 
 
-            final List<World> updatedWorlds = World.save(worlds, this.jpa);
-            return ok(Json.toJson(updatedWorlds));
-        }, dbEc);
+        final List<World> updatedWorlds = World.save(worlds, this.jpa);
+        return ok(Json.toJson(updatedWorlds));
     }
     }
 
 
     private int queryCount(final String queryCountString) {
     private int queryCount(final String queryCountString) {
@@ -75,17 +67,15 @@ public class Application extends Controller {
         return queryCount;
         return queryCount;
     }
     }
 
 
-    private CompletionStage<List<World>> getRandomWorlds(final int n) {
-        return CompletableFuture.supplyAsync(() -> {
-            final Random random = ThreadLocalRandom.current();
-            final List<World> worlds = new ArrayList<>(n);
-            for (int i = 0; i < n; ++i) {
-                long randomId = random.nextInt(10000) + 1;
-                final World world = World.findById(randomId, this.jpa);
-                worlds.add(world);
-            }
-            return worlds;
-        }, dbEc);
+    private List<World> getRandomWorlds(final int n) {
+        final Random random = ThreadLocalRandom.current();
+        final List<World> worlds = new ArrayList<>(n);
+        for (int i = 0; i < n; ++i) {
+            long randomId = random.nextInt(10000) + 1;
+            final World world = World.findById(randomId, this.jpa);
+            worlds.add(world);
+        }
+        return worlds;
     }
     }
 
 
 }
 }

+ 11 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/application.conf

@@ -50,7 +50,13 @@ play.server {
 akka {
 akka {
   actor {
   actor {
     default-dispatcher {
     default-dispatcher {
+      executor = "thread-pool-executor"
+
       fork-join-executor {
       fork-join-executor {
+        # one thread per core is enough
+        # https://github.com/playframework/playframework/issues/7242#issuecomment-295215448
+        parallelism-factor = 1.0
+
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         parallelism-max = 14
         parallelism-max = 14
@@ -59,6 +65,11 @@ akka {
         task-peeking-mode="LIFO" # based on https://www.playframework.com/documentation/2.7.x/Migration24#Thread-pool-configuration
         task-peeking-mode="LIFO" # based on https://www.playframework.com/documentation/2.7.x/Migration24#Thread-pool-configuration
       }
       }
 
 
+      # https://www.playframework.com/documentation/2.7.x/ThreadPools#Highly-synchronous
+      thread-pool-executor {
+        fixed-pool-size = 44 # db conn pool (29) + number of cores (14) + housekeeping (1)
+      }
+
       # https://doc.akka.io/docs/akka/2.5.11/dispatchers.html#looking-up-a-dispatcher
       # https://doc.akka.io/docs/akka/2.5.11/dispatchers.html#looking-up-a-dispatcher
       # Throughput defines the maximum number of messages to be
       # Throughput defines the maximum number of messages to be
       # processed per actor before the thread jumps to the next actor.
       # processed per actor before the thread jumps to the next actor.

+ 5 - 1
frameworks/Java/play2-java/play2-java/conf/application.conf

@@ -51,6 +51,10 @@ akka {
   actor {
   actor {
     default-dispatcher {
     default-dispatcher {
       fork-join-executor {
       fork-join-executor {
+        # one thread per core is enough
+        # https://github.com/playframework/playframework/issues/7242#issuecomment-295215448
+        parallelism-factor = 1.0
+
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         parallelism-max = 14
         parallelism-max = 14
@@ -63,7 +67,7 @@ akka {
       # Throughput defines the maximum number of messages to be
       # Throughput defines the maximum number of messages to be
       # processed per actor before the thread jumps to the next actor.
       # processed per actor before the thread jumps to the next actor.
       # Set to 1 for as fair as possible.
       # Set to 1 for as fair as possible.
-      throughput = 1
+      throughput = 64
     }
     }
   }
   }
 }
 }

+ 13 - 23
frameworks/Scala/play2-scala/play2-scala-anorm/app/controllers/Application.scala

@@ -3,28 +3,26 @@ package controllers
 import javax.inject.{Inject, Singleton}
 import javax.inject.{Inject, Singleton}
 
 
 import play.api.mvc._
 import play.api.mvc._
-import play.mvc.Http
 import play.api.libs.json.Json
 import play.api.libs.json.Json
 import java.util.concurrent._
 import java.util.concurrent._
 import models.{WorldDAO, FortunesDAO, World, Fortune}
 import models.{WorldDAO, FortunesDAO, World, Fortune}
 import utils.DbOperation
 import utils.DbOperation
-import scala.concurrent.{Future, ExecutionContext}
 
 
 @Singleton()
 @Singleton()
-class Application @Inject() (fortunesDAO: FortunesDAO, worldDAO: WorldDAO, dbOperation: DbOperation, val controllerComponents: ControllerComponents)(implicit ec: ExecutionContext)
+class Application @Inject() (fortunesDAO: FortunesDAO, worldDAO: WorldDAO, dbOperation: DbOperation, val controllerComponents: ControllerComponents)
   extends BaseController {
   extends BaseController {
 
 
-  def getRandomWorlds(n: Int): Future[Seq[World]] = dbOperation.asyncDbOp { implicit connection =>
+  def getRandomWorlds(n: Int): Seq[World] = dbOperation.syncDbOp { implicit connection =>
     for (_ <- 1 to n) yield {
     for (_ <- 1 to n) yield {
       worldDAO.findById(getNextRandom)
       worldDAO.findById(getNextRandom)
     }
     }
   }
   }
 
 
-  def getFortunes: Future[Seq[Fortune]] = dbOperation.asyncDbOp { implicit connection =>
+  def getFortunes: Seq[Fortune] = dbOperation.syncDbOp { implicit connection =>
     fortunesDAO.getAll
     fortunesDAO.getAll
   }
   }
 
 
-  def updateWorlds(n: Int): Future[Seq[World]] = dbOperation.asyncDbOp { implicit connection =>
+  def updateWorlds(n: Int): Seq[World] = dbOperation.syncDbOp { implicit connection =>
     for(_ <- 1 to n) yield {
     for(_ <- 1 to n) yield {
       val world = worldDAO.findById(getNextRandom)
       val world = worldDAO.findById(getNextRandom)
       val updatedWorld = world.copy(randomNumber = getNextRandom)
       val updatedWorld = world.copy(randomNumber = getNextRandom)
@@ -44,31 +42,23 @@ class Application @Inject() (fortunesDAO: FortunesDAO, worldDAO: WorldDAO, dbOpe
 
 
   import models.WorldJsonHelpers.toJson
   import models.WorldJsonHelpers.toJson
 
 
-  def db = Action.async {
-    getRandomWorlds(1).map { worlds =>
-      Ok(Json.toJson(worlds.head))
-    }
+  def db = Action {
+    Ok(Json.toJson(getRandomWorlds(1).head))
   }
   }
 
 
-  def queries(countString: String) = Action.async {
+  def queries(countString: String) = Action {
     val n = parseCount(countString)
     val n = parseCount(countString)
-    getRandomWorlds(n).map { worlds =>
-      Ok(Json.toJson(worlds))
-    }
+    Ok(Json.toJson(getRandomWorlds(n)))
   }
   }
 
 
-  def fortunes() = Action.async {
-    getFortunes.map { dbFortunes =>
-      val appendedFortunes =  Fortune(0, "Additional fortune added at request time.") :: dbFortunes.to[List]
-      Ok(views.html.fortune(appendedFortunes))
-    }
+  def fortunes() = Action {
+    val appendedFortunes =  Fortune(0, "Additional fortune added at request time.") :: getFortunes.to[List]
+    Ok(views.html.fortune(appendedFortunes))
   }
   }
 
 
-  def update(queries: String) = Action.async {
+  def update(queries: String) = Action {
     val n = parseCount(queries)
     val n = parseCount(queries)
-    updateWorlds(n).map { worlds =>
-      Ok(Json.toJson(worlds))
-    }
+    Ok(Json.toJson(updateWorlds(n)))
   }
   }
 
 
   private def parseCount(s: String): Int = {
   private def parseCount(s: String): Int = {

+ 3 - 5
frameworks/Scala/play2-scala/play2-scala-anorm/app/utils/DbOperation.scala

@@ -12,20 +12,18 @@ import scala.concurrent.Future
 
 
 @Singleton
 @Singleton
 class DbOperation @Inject() (protected val db: Database,
 class DbOperation @Inject() (protected val db: Database,
-  configuration: Configuration, dbEc: DatabaseExecutionContext) {
+  configuration: Configuration) {
 
 
   /**
   /**
    * Run a DB operation in the DB context.
    * Run a DB operation in the DB context.
    */
    */
-  def asyncDbOp[T](op: Connection => T): Future[T] = {
+  def syncDbOp[T](op: Connection => T): T = {
     // If the thread-pool queue used by the database grows too large then our server
     // If the thread-pool queue used by the database grows too large then our server
     // is probably struggling, and we should start dropping requests. If we don't
     // is probably struggling, and we should start dropping requests. If we don't
     // then we'll just slow everything down and it will fail anyway. Better to fail
     // then we'll just slow everything down and it will fail anyway. Better to fail
     // quickly rather than slowly. Set the max size of our queue something above the
     // quickly rather than slowly. Set the max size of our queue something above the
     // number of concurrent connections that we expect to be handling.
     // number of concurrent connections that we expect to be handling.
-    Future {
-      db.withConnection { connection => op(connection) }
-    }(dbEc)
+    db.withConnection { connection => op(connection) }
   }
   }
 
 
 }
 }

+ 11 - 0
frameworks/Scala/play2-scala/play2-scala-anorm/conf/application.conf

@@ -45,7 +45,13 @@ play.server {
 akka {
 akka {
   actor {
   actor {
     default-dispatcher {
     default-dispatcher {
+      executor = "thread-pool-executor"
+
       fork-join-executor {
       fork-join-executor {
+        # one thread per core is enough
+        # https://github.com/playframework/playframework/issues/7242#issuecomment-295215448
+        parallelism-factor = 1.0
+
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         parallelism-max = 14
         parallelism-max = 14
@@ -54,6 +60,11 @@ akka {
         task-peeking-mode="LIFO" # based on https://www.playframework.com/documentation/2.7.x/Migration24#Thread-pool-configuration
         task-peeking-mode="LIFO" # based on https://www.playframework.com/documentation/2.7.x/Migration24#Thread-pool-configuration
       }
       }
 
 
+      # https://www.playframework.com/documentation/2.7.x/ThreadPools#Highly-synchronous
+      thread-pool-executor {
+        fixed-pool-size = 44 # db conn pool (29) + number of cores (14) + housekeeping (1)
+      }
+
       # https://doc.akka.io/docs/akka/2.5.11/dispatchers.html#looking-up-a-dispatcher
       # https://doc.akka.io/docs/akka/2.5.11/dispatchers.html#looking-up-a-dispatcher
       # Throughput defines the maximum number of messages to be
       # Throughput defines the maximum number of messages to be
       # processed per actor before the thread jumps to the next actor.
       # processed per actor before the thread jumps to the next actor.

+ 5 - 1
frameworks/Scala/play2-scala/play2-scala-reactivemongo/conf/application.conf

@@ -42,6 +42,10 @@ akka {
   actor {
   actor {
     default-dispatcher {
     default-dispatcher {
       fork-join-executor {
       fork-join-executor {
+        # one thread per core is enough
+        # https://github.com/playframework/playframework/issues/7242#issuecomment-295215448
+        parallelism-factor = 1.0
+
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         parallelism-max = 14
         parallelism-max = 14
@@ -54,7 +58,7 @@ akka {
       # Throughput defines the maximum number of messages to be
       # Throughput defines the maximum number of messages to be
       # processed per actor before the thread jumps to the next actor.
       # processed per actor before the thread jumps to the next actor.
       # Set to 1 for as fair as possible.
       # Set to 1 for as fair as possible.
-      throughput = 1
+      throughput = 64
     }
     }
   }
   }
 }
 }

+ 5 - 1
frameworks/Scala/play2-scala/play2-scala-slick/conf/application.conf

@@ -46,6 +46,10 @@ akka {
   actor {
   actor {
     default-dispatcher {
     default-dispatcher {
       fork-join-executor {
       fork-join-executor {
+        # one thread per core is enough
+        # https://github.com/playframework/playframework/issues/7242#issuecomment-295215448
+        parallelism-factor = 1.0
+
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         parallelism-max = 14
         parallelism-max = 14
@@ -58,7 +62,7 @@ akka {
       # Throughput defines the maximum number of messages to be
       # Throughput defines the maximum number of messages to be
       # processed per actor before the thread jumps to the next actor.
       # processed per actor before the thread jumps to the next actor.
       # Set to 1 for as fair as possible.
       # Set to 1 for as fair as possible.
-      throughput = 1
+      throughput = 64
     }
     }
   }
   }
 }
 }

+ 5 - 1
frameworks/Scala/play2-scala/play2-scala/conf/application.conf

@@ -46,6 +46,10 @@ akka {
   actor {
   actor {
     default-dispatcher {
     default-dispatcher {
       fork-join-executor {
       fork-join-executor {
+        # one thread per core is enough
+        # https://github.com/playframework/playframework/issues/7242#issuecomment-295215448
+        parallelism-factor = 1.0
+
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # Information about the TechEmpower benchmark environment: https://www.techempower.com/benchmarks/#section=environment
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         # The environment variable physical_cpu_count does NOT include the hyperthreaded cores!
         parallelism-max = 14
         parallelism-max = 14
@@ -58,7 +62,7 @@ akka {
       # Throughput defines the maximum number of messages to be
       # Throughput defines the maximum number of messages to be
       # processed per actor before the thread jumps to the next actor.
       # processed per actor before the thread jumps to the next actor.
       # Set to 1 for as fair as possible.
       # Set to 1 for as fair as possible.
-      throughput = 1
+      throughput = 64
     }
     }
   }
   }
 }
 }