ソースを参照

Merge branch 'master' into cleanup-metadata

Conflicts:
	frameworks/Java/rapidoid/benchmark_config.json
	frameworks/Java/sabina/benchmark_config.json
	frameworks/Python/web2py/benchmark_config.json
msmith-techempower 8 年 前
コミット
67864f71f6
100 ファイル変更1664 行追加590 行削除
  1. 18 0
      .github/PULL_REQUEST_TEMPLATE.md
  2. 3 0
      .gitignore
  3. 0 3
      .travis.yml
  4. 19 0
      config/upstart.example/tfb.conf
  5. 67 6
      frameworks/C++/cutelyst/benchmark_config.json
  6. 2 2
      frameworks/C++/cutelyst/nginx.conf
  7. 4 2
      frameworks/C++/cutelyst/setup.sh
  8. 24 0
      frameworks/C++/cutelyst/setup_thread.sh
  9. 1 1
      frameworks/C++/cutelyst/setup_uwsgi_nginx.sh
  10. 12 4
      frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp
  11. 6 6
      frameworks/C++/cutelyst/src/databaseupdatestest.cpp
  12. 2 2
      frameworks/C++/cutelyst/src/fortunetest.cpp
  13. 4 4
      frameworks/C++/cutelyst/src/multipledatabasequeriestest.cpp
  14. 4 4
      frameworks/C++/cutelyst/src/singledatabasequerytest.cpp
  15. 1 1
      frameworks/C++/silicon/CMakeLists.txt
  16. 1 1
      frameworks/C++/silicon/setup_lwan_mysql.sh
  17. 1 1
      frameworks/C++/silicon/setup_mhd_epoll_mysql.sh
  18. 1 1
      frameworks/C++/silicon/setup_mhd_tpc_mysql.sh
  19. 0 1
      frameworks/C/h2o/CMakeLists.txt
  20. 1 1
      frameworks/C/h2o/setup.sh
  21. 37 61
      frameworks/C/h2o/src/fortune.c
  22. 1 1
      frameworks/C/h2o/src/fortune.h
  23. 35 51
      frameworks/C/h2o/src/request_handler.c
  24. 3 1
      frameworks/C/h2o/src/request_handler.h
  25. 8 2
      frameworks/C/h2o/src/template.c
  26. 5 28
      frameworks/C/h2o/src/world.c
  27. 1 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/AppSettings.cs
  28. 12 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/DatabaseServer.cs
  29. 30 12
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/Scenarios.cs
  30. 11 51
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/FortunesController.cs
  31. 42 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleQueriesController.cs
  32. 42 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleUpdatesController.cs
  33. 41 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/SingleQueryController.cs
  34. 49 2
      frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbContext.cs
  35. 24 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/BatchUpdateString.cs
  36. 50 12
      frameworks/CSharp/aspnetcore/Benchmarks/Data/DapperDb.cs
  37. 27 3
      frameworks/CSharp/aspnetcore/Benchmarks/Data/EfDb.cs
  38. 4 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/Fortune.cs
  39. 19 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/IDb.cs
  40. 43 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/NoTransactionSqlServerConnection.cs
  41. 6 3
      frameworks/CSharp/aspnetcore/Benchmarks/Data/Random.cs
  42. 83 39
      frameworks/CSharp/aspnetcore/Benchmarks/Data/RawDb.cs
  43. 5 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/World.cs
  44. 0 47
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/ErrorHandlerMiddleware.cs
  45. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesEfMiddleware.cs
  46. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs
  47. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesDapperMiddleware.cs
  48. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesEfMiddleware.cs
  49. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesRawMiddleware.cs
  50. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/SingleQueryEfMiddleware.cs
  51. 1 2
      frameworks/CSharp/aspnetcore/Benchmarks/NuGet.Config
  52. 41 8
      frameworks/CSharp/aspnetcore/Benchmarks/Startup.cs
  53. 0 3
      frameworks/CSharp/aspnetcore/Benchmarks/appsettings.json
  54. 4 0
      frameworks/CSharp/aspnetcore/Benchmarks/appsettings.postgresql.json
  55. 21 10
      frameworks/CSharp/aspnetcore/Benchmarks/project.json
  56. 184 10
      frameworks/CSharp/aspnetcore/benchmark_config.json
  57. 10 2
      frameworks/CSharp/aspnetcore/run-linux.sh
  58. 3 0
      frameworks/CSharp/aspnetcore/setup-dapper.sh
  59. 3 0
      frameworks/CSharp/aspnetcore/setup-ef.sh
  60. 3 0
      frameworks/CSharp/aspnetcore/setup-json.sh
  61. 0 3
      frameworks/CSharp/aspnetcore/setup-mvc.sh
  62. 3 0
      frameworks/CSharp/aspnetcore/setup-plaintext.sh
  63. 3 0
      frameworks/CSharp/aspnetcore/setup-raw.sh
  64. 0 3
      frameworks/CSharp/aspnetcore/setup.sh
  65. 17 4
      frameworks/CSharp/revenj/Revenj.Bench/RestService.cs
  66. 3 3
      frameworks/Clojure/pedestal/project.clj
  67. 62 76
      frameworks/Clojure/pedestal/src/pedestal/service.clj
  68. 4 27
      frameworks/Crystal/kemal/server-postgres.cr
  69. 2 25
      frameworks/Crystal/kemal/server-redis.cr
  70. 2 2
      frameworks/Crystal/kemal/setup-postgres.sh
  71. 2 2
      frameworks/Crystal/kemal/setup-redis.sh
  72. 2 2
      frameworks/Crystal/kemal/shard.lock
  73. 3 5
      frameworks/Crystal/kemal/shard.yml
  74. 20 0
      frameworks/Crystal/kemal/views/fortunes.ecr
  75. 1 0
      frameworks/Go/echo/setup.sh
  76. 1 0
      frameworks/Go/echo/setup_prefork.sh
  77. 1 0
      frameworks/Go/echo/setup_std.sh
  78. 7 9
      frameworks/Go/falcore/src/framework_benchmarks/falcore.go
  79. 1 1
      frameworks/Go/fasthttp/setup-mysql-prefork.sh
  80. 1 1
      frameworks/Go/fasthttp/setup-mysql.sh
  81. 1 1
      frameworks/Go/fasthttp/setup-postgresql-prefork.sh
  82. 1 1
      frameworks/Go/fasthttp/setup-postgresql.sh
  83. 1 1
      frameworks/Go/fasthttp/source_code
  84. 0 0
      frameworks/Go/fasthttp/src/templates/auxiliary.go
  85. 3 1
      frameworks/Haskell/yesod/.gitignore
  86. 27 4
      frameworks/Haskell/yesod/benchmark_config.json
  87. 9 0
      frameworks/Haskell/yesod/run_yesod_mysql_mongo.sh
  88. 9 0
      frameworks/Haskell/yesod/run_yesod_postgres.sh
  89. 0 9
      frameworks/Haskell/yesod/setup.sh
  90. 0 0
      frameworks/Haskell/yesod/yesod-mysql-mongo/.gitignore
  91. 0 0
      frameworks/Haskell/yesod/yesod-mysql-mongo/src/yesod.hs
  92. 0 0
      frameworks/Haskell/yesod/yesod-mysql-mongo/stack.yaml
  93. 2 2
      frameworks/Haskell/yesod/yesod-mysql-mongo/yesod-mysql-mongo.cabal
  94. 10 0
      frameworks/Haskell/yesod/yesod-postgres/.gitignore
  95. 194 0
      frameworks/Haskell/yesod/yesod-postgres/src/Main.hs
  96. 5 0
      frameworks/Haskell/yesod/yesod-postgres/stack.yaml
  97. 43 0
      frameworks/Haskell/yesod/yesod-postgres/yesod-postgres.cabal
  98. 4 4
      frameworks/Java/curacao/pom.xml
  99. 1 1
      frameworks/Java/curacao/src/main/resources/application.conf
  100. 2 0
      frameworks/Java/jooby/setup.sh

+ 18 - 0
.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,18 @@
+<!--
+....................................
+
+MAKE SURE YOU ARE OPENING A PULL 
+REQUEST AGAINST THE CORRECT BRANCH
+
+....................................
+
+master = bug fixes directly 
+addressing the current preview round
+
+round-X = new features, frameworks, 
+tests, and any other larger changes 
+that you would like to see in the 
+next round
+
+....................................
+-->

+ 3 - 0
.gitignore

@@ -83,3 +83,6 @@ nimcache
 # crystal
 .crystal
 *.out
+
+#don't ignore merge request templates
+!.github/

+ 0 - 3
.travis.yml

@@ -97,7 +97,6 @@ env:
     - "TESTDIR=Java/servlet"
     - "TESTDIR=Java/servlet3-cass"
     - "TESTDIR=Java/spark"
-    - "TESTDIR=Java/sabina"
     - "TESTDIR=Java/spring"
     - "TESTDIR=Java/tapestry"
     - "TESTDIR=Java/undertow"
@@ -186,13 +185,11 @@ env:
     - "TESTDIR=Scala/finatra"
     - "TESTDIR=Scala/fintrospect"
     - "TESTDIR=Scala/lift-stateless"
-    - "TESTDIR=Scala/plain"
     - "TESTDIR=Scala/play2-scala"
     - "TESTDIR=Scala/scalatra"
     - "TESTDIR=Scala/scruffy"
     - "TESTDIR=Scala/spray"
     - "TESTDIR=Scala/s-server"
-    - "TESTDIR=Scala/spray-es"
     - "TESTDIR=Scala/unfiltered"
     - "TESTDIR=Scala/http4s"
     - "TESTDIR=Scala/finch"

+ 19 - 0
config/upstart.example/tfb.conf

@@ -0,0 +1,19 @@
+# /etc/init/tfb.conf
+env TFB_REPOPARENT="/private"
+env TFB_REPONAME="FrameworkBenchmarks"
+env TFB_REPOURI="https://github.com/ashawnbandy-te-tfb/FrameworkBenchmarks.git"
+env TFB_MAILINGLIST="[email protected]"
+env TFB_MAILING_FROM="[email protected]"
+env TFB_LOGSFOLDER="/private/logs"
+env TFB_REPOBRANCH="45216-continuousbenchmarking-20160609-asb-2"
+setuid techempower
+setgid techempower
+umask 0002
+respawn
+respawn limit 5 2
+script
+  if [ ! -d "$TFB_REPOPARENT/$TFB_REPONAME" ]; then
+    git clone -b $TFB_REPOBRANCH $TFB_REPOURI $TFB_REPOPARENT/$TFB_REPONAME
+  fi
+  exec /$TFB_REPOPARENT/$TFB_REPONAME/toolset/run-continuously.sh
+end script

+ 67 - 6
frameworks/C++/cutelyst/benchmark_config.json

@@ -16,7 +16,7 @@
                 "webserver": "None",
                 "os": "Linux",
                 "database_os": "Linux",
-                "display_name": "cutelyst-uwsgi",
+                "display_name": "cutelyst-pf",
                 "notes": "",
                 "versus": ""
             },
@@ -37,7 +37,7 @@
                 "webserver": "None",
                 "os": "Linux",
                 "database_os": "Linux",
-                "display_name": "cutelyst-uwsgi-pg-raw",
+                "display_name": "cutelyst-pf-pg-raw",
                 "notes": "",
                 "versus": ""
             },
@@ -58,7 +58,68 @@
                 "webserver": "None",
                 "os": "Linux",
                 "database_os": "Linux",
-                "display_name": "cutelyst-uwsgi-mysql-raw",
+                "display_name": "cutelyst-pf-mysql-raw",
+                "notes": "",
+                "versus": ""
+            },
+            "thread": {
+                "setup_file": "setup_thread",
+                "json_url": "/json",
+                "plaintext_url": "/plaintext",
+                "port": 8080,
+                "approach": "Realistic",
+                "classification": "Fullstack",
+                "database": "None",
+                "framework": "cutelyst",
+                "language": "C++",
+                "orm": "Raw",
+                "platform": "Qt",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "cutelyst-thread",
+                "notes": "",
+                "versus": ""
+            },
+            "thread-postgres-raw": {
+                "setup_file": "setup_thread",
+                "db_url": "/db_postgres",
+                "query_url": "/query_postgres?queries=",
+                "update_url": "/updates_postgres?queries=",
+                "fortune_url": "/fortunes_raw_postgres",
+                "port": 8080,
+                "approach": "Realistic",
+                "classification": "Platform",
+                "database": "Postgres",
+                "framework": "cutelyst",
+                "language": "C++",
+                "orm": "Raw",
+                "platform": "Qt",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "cutelyst-thread-pg-raw",
+                "notes": "",
+                "versus": ""
+            },
+            "thread-mysql-raw": {
+                "setup_file": "setup_thread",
+                "db_url": "/db_mysql",
+                "query_url": "/query_mysql?queries=",
+                "update_url": "/updates_mysql?queries=",
+                "fortune_url": "/fortunes_raw_mysql",
+                "port": 8080,
+                "approach": "Realistic",
+                "classification": "Platform",
+                "database": "MySQL",
+                "framework": "cutelyst",
+                "language": "C++",
+                "orm": "Raw",
+                "platform": "Qt",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "cutelyst-thread-mysql-raw",
                 "notes": "",
                 "versus": ""
             },
@@ -74,7 +135,7 @@
                 "language": "C++",
                 "orm": "Raw",
                 "platform": "Qt",
-                "webserver": "None",
+                "webserver": "nginx",
                 "os": "Linux",
                 "database_os": "Linux",
                 "display_name": "cutelyst-uwsgi-nginx",
@@ -95,7 +156,7 @@
                 "language": "C++",
                 "orm": "Raw",
                 "platform": "Qt",
-                "webserver": "None",
+                "webserver": "nginx",
                 "os": "Linux",
                 "database_os": "Linux",
                 "display_name": "cutelyst-uwsgi-nginx-pg-raw",
@@ -116,7 +177,7 @@
                 "language": "C++",
                 "orm": "Raw",
                 "platform": "Qt",
-                "webserver": "None",
+                "webserver": "nginx",
                 "os": "Linux",
                 "database_os": "Linux",
                 "display_name": "cutelyst-uwsgi-nginx-mysql-raw",

+ 2 - 2
frameworks/C++/cutelyst/nginx.conf

@@ -1,6 +1,6 @@
 # This file is based on /usr/local/nginx/conf/nginx.conf.default.
 
-# One worker process per core
+worker_processes auto;
 error_log stderr error;
 
 events {
@@ -27,7 +27,7 @@ http {
     # some worker processes to be handling too connections relative to the
     # other workers based on an initial imbalance, so this is disabled for
     # now.
-#    keepalive_requests 1000;
+    keepalive_requests 1000;
 
     #keepalive_timeout  0;
     keepalive_timeout  65;

+ 4 - 2
frameworks/C++/cutelyst/setup.sh

@@ -3,7 +3,7 @@
 fw_depends cutelyst
 
 sed -i 's|DatabaseHostName=.*|DatabaseHostName='"$DBHOST"'|g' config/config.ini
-sed -i 's|SendDate=.*|SendDate=true|g' config/config.ini
+sed -i 's|SendDate=.*|SendDate=false|g' config/config.ini
 
 cd $IROOT
 mkdir cutelyst-benchmarks || true
@@ -19,4 +19,6 @@ make clean
 make -j $MAX_THREADS
 
 export LD_LIBRARY_PATH=/opt/qt${QT_VERSION_MM}/lib:${IROOT}/lib/x86_64-linux-gnu/
-uwsgi --ini ${TROOT}/config/config.ini --cutelyst-app ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $(( $MAX_THREADS * 2 )) &
+export CUTELYST_CONFIG=${TROOT}/config/config.ini
+
+${IROOT}/bin/cutelyst-wsgi --http-socket :8080 -a ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $MAX_THREADS &

+ 24 - 0
frameworks/C++/cutelyst/setup_thread.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+
+fw_depends cutelyst
+
+sed -i 's|DatabaseHostName=.*|DatabaseHostName='"$DBHOST"'|g' config/config.ini
+sed -i 's|SendDate=.*|SendDate=false|g' config/config.ini
+
+cd $IROOT
+mkdir cutelyst-benchmarks || true
+cd cutelyst-benchmarks
+rm -rf *
+
+QT_VERSION_MM=56
+export CMAKE_PREFIX_PATH=/opt/qt${QT_VERSION_MM}:${IROOT}
+
+cmake $TROOT -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$IROOT
+
+make clean
+make -j $MAX_THREADS
+
+export LD_LIBRARY_PATH=/opt/qt${QT_VERSION_MM}/lib:${IROOT}/lib/x86_64-linux-gnu/
+export CUTELYST_CONFIG=${TROOT}/config/config.ini
+
+${IROOT}/bin/cutelyst-wsgi --http-socket :8080 -a ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -t $MAX_THREADS &

+ 1 - 1
frameworks/C++/cutelyst/setup_uwsgi_nginx.sh

@@ -22,4 +22,4 @@ make -j $MAX_THREADS
 nginx -c $TROOT/nginx.conf
 
 export LD_LIBRARY_PATH=/opt/qt${QT_VERSION_MM}/lib:${IROOT}/lib/x86_64-linux-gnu/
-uwsgi --ini ${TROOT}/config/config_socket.ini --cutelyst-app ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $(( $MAX_THREADS * 2 )) &
+uwsgi --ini ${TROOT}/config/config_socket.ini --cutelyst-app ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $MAX_THREADS &

+ 12 - 4
frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp

@@ -4,7 +4,10 @@
 
 #include <QtSql/QSqlDatabase>
 #include <QtSql/QSqlError>
+#include <QCoreApplication>
+#include <QThread>
 #include <QDebug>
+#include <QMutexLocker>
 
 #include "root.h"
 #include "jsontest.h"
@@ -16,6 +19,8 @@
 
 using namespace Cutelyst;
 
+static QMutex mutex;
+
 cutelyst_benchmarks::cutelyst_benchmarks(QObject *parent) : Application(parent)
 {
 }
@@ -46,27 +51,30 @@ bool cutelyst_benchmarks::init()
 
 bool cutelyst_benchmarks::postFork()
 {
+    QMutexLocker locker(&mutex);
+
     QSqlDatabase db;
-    db = QSqlDatabase::addDatabase(QLatin1String("QPSQL"), QLatin1String("postgres"));
+    db = QSqlDatabase::addDatabase(QLatin1String("QPSQL"), QLatin1String("postgres-") + QThread::currentThread()->objectName());
     db.setDatabaseName(QLatin1String("hello_world"));
     db.setUserName(QLatin1String("benchmarkdbuser"));
     db.setPassword(QLatin1String("benchmarkdbpass"));
     db.setHostName(config(QLatin1String("DatabaseHostName")).toString());
     if (!db.open()) {
-        qDebug() << "Error opening db:" << db << db.lastError().databaseText();
+        qDebug() << "Error opening PostgreSQL db:" << db << db.connectionName() << db.lastError().databaseText();
         return false;
     }
 
-    db = QSqlDatabase::addDatabase(QLatin1String("QMYSQL"), QLatin1String("mysql"));
+    db = QSqlDatabase::addDatabase(QLatin1String("QMYSQL"), QLatin1String("mysql-") + QThread::currentThread()->objectName());
     db.setDatabaseName(QLatin1String("hello_world"));
     db.setUserName(QLatin1String("benchmarkdbuser"));
     db.setPassword(QLatin1String("benchmarkdbpass"));
     db.setHostName(config(QLatin1String("DatabaseHostName")).toString());
     if (!db.open()) {
-        qDebug() << "Error opening db:" << db << db.lastError().databaseText();
+        qDebug() << "Error opening MySQL db:" << db << db.connectionName() << db.lastError().databaseText();
         return false;
     }
 
+    qDebug() << "Connections" << QCoreApplication::applicationPid() << QThread::currentThread() << QSqlDatabase::connectionNames();
 //    db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("sqlite"));
 //    if (!db.open()) {
 //        qDebug() << "Error opening db:" << db << db.lastError().databaseText();

+ 6 - 6
frameworks/C++/cutelyst/src/databaseupdatestest.cpp

@@ -18,10 +18,10 @@ void DatabaseUpdatesTest::updates_postgres(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     QSqlQuery updateQuery = CPreparedSqlQueryForDatabase(
                 QLatin1String("UPDATE world SET randomNumber = :randomNumber WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     processQuery(c, query, updateQuery);
 }
 
@@ -29,10 +29,10 @@ void DatabaseUpdatesTest::updates_mysql(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
     QSqlQuery updateQuery = CPreparedSqlQueryForDatabase(
                 QLatin1String("UPDATE world SET randomNumber = :randomNumber WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
     processQuery(c, query, updateQuery);
 }
 
@@ -48,7 +48,7 @@ void DatabaseUpdatesTest::processQuery(Context *c, QSqlQuery &query, QSqlQuery &
     }
 
     for (int i = 0; i < queries; ++i) {
-        int id = (qrand() % 9999) + 1;
+        int id = (qrand() % 10000) + 1;
 
         query.bindValue(QStringLiteral(":id"), id);
         if (!query.exec() || !query.next()) {
@@ -56,7 +56,7 @@ void DatabaseUpdatesTest::processQuery(Context *c, QSqlQuery &query, QSqlQuery &
             return;
         }
 
-        int randomNumber = (qrand() % 9999) + 1;
+        int randomNumber = (qrand() % 10000) + 1;
         updateQuery.bindValue(QStringLiteral(":id"), id);
         updateQuery.bindValue(QStringLiteral(":randomNumber"), randomNumber);
         if (!updateQuery.exec()) {

+ 2 - 2
frameworks/C++/cutelyst/src/fortunetest.cpp

@@ -34,14 +34,14 @@ QSqlQuery FortuneTest::postgresQuery()
 {
     return CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, message FROM fortune"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
 }
 
 QSqlQuery FortuneTest::mysqlQuery()
 {
     return CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, message FROM fortune"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
 }
 
 static bool caseSensitiveLessThan(const Fortune &a1, const Fortune &a2)

+ 4 - 4
frameworks/C++/cutelyst/src/multipledatabasequeriestest.cpp

@@ -18,7 +18,7 @@ void MultipleDatabaseQueriesTest::query_postgres(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     processQuery(c, query);
 }
 
@@ -26,7 +26,7 @@ void MultipleDatabaseQueriesTest::query_mysql(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
     processQuery(c, query);
 }
 
@@ -42,9 +42,9 @@ void MultipleDatabaseQueriesTest::processQuery(Context *c, QSqlQuery &query)
     }
 
     for (int i = 0; i < queries; ++i) {
-        int id = qrand() % 9999;
+        int id = (qrand() % 10000) + 1;
 
-        query.bindValue(QStringLiteral(":id"), id + 1);
+        query.bindValue(QStringLiteral(":id"), id);
         if (!query.exec() || !query.next()) {
             c->res()->setStatus(Response::InternalServerError);
             return;

+ 4 - 4
frameworks/C++/cutelyst/src/singledatabasequerytest.cpp

@@ -17,7 +17,7 @@ void SingleDatabaseQueryTest::db_postgres(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("postgres")));
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     processQuery(c, query);
 }
 
@@ -25,15 +25,15 @@ void SingleDatabaseQueryTest::db_mysql(Context *c)
 {
     QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QSqlDatabase::database(QLatin1String("mysql")));
+                QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
     processQuery(c, query);
 }
 
 void SingleDatabaseQueryTest::processQuery(Context *c, QSqlQuery &query)
 {
-    int id = qrand() % 9999;
+    int id = (qrand() % 10000) + 1;
 
-    query.bindValue(QStringLiteral(":id"), id + 1);
+    query.bindValue(QStringLiteral(":id"), id);
     if (!query.exec() || !query.next()) {
         c->res()->setStatus(Response::InternalServerError);
         return;

+ 1 - 1
frameworks/C++/silicon/CMakeLists.txt

@@ -16,4 +16,4 @@ set_target_properties(silicon_epoll_mysql PROPERTIES COMPILE_FLAGS "-DTFB_USE_EP
 target_link_libraries(silicon_epoll_mysql microhttpd mysqlclient)
 
 add_executable(silicon_lwan_mysql techempower_lwan.cc)
-target_link_libraries(silicon_lwan_mysql mysqlclient lwan-common curl z pthread dl)
+target_link_libraries(silicon_lwan_mysql mysqlclient lwan curl z pthread dl luajit-5.1)

+ 1 - 1
frameworks/C++/silicon/setup_lwan_mysql.sh

@@ -5,7 +5,7 @@ fw_depends silicon lwan
 rm -rf build
 mkdir build
 cd build
-cmake .. -DCMAKE_CXX_COMPILER=clang++-3.5
+cmake .. -DCMAKE_CXX_COMPILER=clang++-3.8
 make silicon_lwan_mysql
 
 $TROOT/build/silicon_lwan_mysql ${DBHOST} 8080 &

+ 1 - 1
frameworks/C++/silicon/setup_mhd_epoll_mysql.sh

@@ -5,7 +5,7 @@ fw_depends silicon microhttpd
 rm -rf build
 mkdir build
 cd build
-cmake .. -DCMAKE_CXX_COMPILER=clang++-3.5
+cmake .. -DCMAKE_CXX_COMPILER=clang++-3.8
 make silicon_epoll_mysql
 
 $TROOT/build/silicon_epoll_mysql ${DBHOST} 8080 ${MAX_THREADS} &

+ 1 - 1
frameworks/C++/silicon/setup_mhd_tpc_mysql.sh

@@ -5,7 +5,7 @@ fw_depends silicon microhttpd
 rm -rf build
 mkdir build
 cd build
-cmake .. -DCMAKE_CXX_COMPILER=clang++-3.5
+cmake .. -DCMAKE_CXX_COMPILER=clang++-3.8
 make silicon_tpc_mysql
 
 $TROOT/build/silicon_tpc_mysql ${DBHOST} 8080 ${MAX_THREADS} &

+ 0 - 1
frameworks/C/h2o/CMakeLists.txt

@@ -8,7 +8,6 @@ find_path(MUSTACHE_C_INCLUDE mustache.h)
 find_path(YAJL_INCLUDE yajl/yajl_gen.h)
 set(COMMON_OPTIONS -flto -pthread)
 add_compile_options(-std=gnu11 -pedantic -Wall -Wextra ${COMMON_OPTIONS})
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
 set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fstack-protector-all -D_FORTIFY_SOURCE=2")
 set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast")
 set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -Ofast")

+ 1 - 1
frameworks/C/h2o/setup.sh

@@ -25,7 +25,7 @@ run_curl()
 
 run_h2o_app()
 {
-	"$1/h2o_app" -a2 -f "$2/template/fortunes.mustache" -m2 "$3" "$4" \
+	"$1/h2o_app" -a2 -f "$2/template/fortunes.mustache" -m8 "$3" "$4" \
 		-d "host=$DBHOST dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass" &
 }
 

+ 37 - 61
frameworks/C/h2o/src/fortune.c

@@ -49,6 +49,7 @@ typedef struct {
 	iovec_list_t *iovec_list_iter;
 	h2o_req_t *req;
 	list_t *result;
+	size_t content_length;
 	size_t num_result;
 	db_query_param_t param;
 	h2o_generator_t generator;
@@ -56,9 +57,8 @@ typedef struct {
 
 static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
-                           char *buffer,
+                           const char *buffer,
                            uintmax_t buffer_size);
-static void cleanup_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
 static int compare_fortunes(const list_t *x, const list_t *y);
 static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
 static list_t *get_sorted_sublist(list_t *head);
@@ -76,7 +76,7 @@ static list_t *sort_fortunes(list_t *head);
 
 static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
-                           char *buffer,
+                           const char *buffer,
                            uintmax_t buffer_size)
 {
 	IGNORE_FUNCTION_PARAMETER(api);
@@ -101,33 +101,14 @@ static uintmax_t add_iovec(mustache_api_t *api,
 	}
 
 	if (ret) {
-		iovec_list->iov[iovec_list->iovcnt].base = buffer;
+		iovec_list->iov[iovec_list->iovcnt].base = (char *) buffer;
 		iovec_list->iov[iovec_list->iovcnt++].len = buffer_size;
+		fortune_ctx->content_length += buffer_size;
 	}
 
 	return ret;
 }
 
-static void cleanup_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	IGNORE_FUNCTION_PARAMETER(req);
-
-	fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t,
-	                                                           generator,
-	                                                           self);
-	const list_t *iter = fortune_ctx->result;
-
-	if (iter)
-		do {
-			const fortune_t * const fortune = H2O_STRUCT_FROM_MEMBER(fortune_t, l, iter);
-
-			if (fortune->data)
-				PQclear(fortune->data);
-
-			iter = iter->next;
-		} while (iter);
-}
-
 static int compare_fortunes(const list_t *x, const list_t *y)
 {
 	const fortune_t * const f1 = H2O_STRUCT_FROM_MEMBER(fortune_t, l, x);
@@ -146,19 +127,12 @@ static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req)
 	fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t,
 	                                                           generator,
 	                                                           self);
+	iovec_list_t * const iovec_list = H2O_STRUCT_FROM_MEMBER(iovec_list_t,
+	                                                         l,
+	                                                         fortune_ctx->iovec_list);
 
-	if (fortune_ctx->iovec_list) {
-		iovec_list_t * const iovec_list = H2O_STRUCT_FROM_MEMBER(iovec_list_t,
-		                                                         l,
-		                                                         fortune_ctx->iovec_list);
-
-		fortune_ctx->iovec_list = iovec_list->l.next;
-		h2o_send(fortune_ctx->req, iovec_list->iov, iovec_list->iovcnt, false);
-	}
-	else {
-		h2o_send(req, NULL, 0, true);
-		cleanup_fortunes(self, req);
-	}
+	fortune_ctx->iovec_list = iovec_list->l.next;
+	h2o_send(req, iovec_list->iov, iovec_list->iovcnt, !fortune_ctx->iovec_list);
 }
 
 static list_t *get_sorted_sublist(list_t *head)
@@ -218,7 +192,6 @@ static void on_fortune_error(db_query_param_t *param, const char *error_string)
 	                                                           param,
 	                                                           param);
 
-	cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 	send_error(BAD_GATEWAY, error_string, fortune_ctx->req);
 }
 
@@ -236,37 +209,45 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 		ret = SUCCESS;
 
 		for (size_t i = 0; i < num_rows; i++) {
+			const char * const message_data = PQgetvalue(result, i, 1);
+			h2o_iovec_t message = h2o_htmlescape(&fortune_ctx->req->pool,
+			                                     message_data,
+			                                     PQgetlength(result, i, 1));
+			const size_t id_len = PQgetlength(result, i, 0);
+			const size_t fortune_size = offsetof(fortune_t, data) + id_len +
+			                            (message_data == message.base ? message.len : 0);
 			fortune_t * const fortune = h2o_mem_alloc_pool(&fortune_ctx->req->pool,
-			                                               sizeof(*fortune));
+			                                               fortune_size);
 
 			if (fortune) {
-				memset(fortune, 0, sizeof(*fortune));
-				fortune->id.base = PQgetvalue(result, i, 0);
-				fortune->id.len = PQgetlength(result, i, 0);
-				fortune->message = h2o_htmlescape(&fortune_ctx->req->pool,
-				                                  PQgetvalue(result, i, 1),
-				                                  PQgetlength(result, i, 1));
+				memset(fortune, 0, offsetof(fortune_t, data));
+				memcpy(fortune->data, PQgetvalue(result, i, 0), id_len);
+				fortune->id.base = fortune->data;
+				fortune->id.len = id_len;
+
+				if (message_data == message.base) {
+					message.base = fortune->data + id_len;
+					memcpy(message.base, message_data, message.len);
+				}
+
+				fortune->message = message;
 				fortune->l.next = fortune_ctx->result;
 				fortune_ctx->result = &fortune->l;
 				fortune_ctx->num_result++;
-
-				if (!i)
-					fortune->data = result;
 			}
 			else {
-				cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 				send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
 				ret = DONE;
-
-				if (!i)
-					PQclear(result);
-
 				break;
 			}
 		}
+
+		PQclear(result);
 	}
-	else if (result)
+	else if (result) {
 		PQclear(result);
+		send_error(BAD_GATEWAY, PQresultErrorMessage(result), fortune_ctx->req);
+	}
 	else {
 		mustache_api_t api = {.sectget = on_fortune_section,
 		                      .varget = on_fortune_variable,
@@ -286,18 +267,15 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 
 		if (mustache_render(&api, fortune_ctx, ctx->global_data->fortunes_template)) {
 			fortune_ctx->iovec_list = iovec_list->l.next;
-			set_default_response_param(HTML, fortune_ctx->req);
+			set_default_response_param(HTML, fortune_ctx->content_length, fortune_ctx->req);
 			h2o_start_response(fortune_ctx->req, &fortune_ctx->generator);
 			h2o_send(fortune_ctx->req,
 			         iovec_list->iov,
 			         iovec_list->iovcnt,
-			         false);
+			         !fortune_ctx->iovec_list);
 		}
-		else {
-			cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
+		else
 			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
-			ret = DONE;
-		}
 	}
 
 	return ret;
@@ -335,7 +313,6 @@ static void on_fortune_timeout(db_query_param_t *param)
 	                                                           param,
 	                                                           param);
 
-	cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, fortune_ctx->req);
 }
 
@@ -401,7 +378,6 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 			fortune->message.len = sizeof(NEW_FORTUNE_MESSAGE) - 1;
 			memset(fortune_ctx, 0, sizeof(*fortune_ctx));
 			fortune_ctx->generator.proceed = complete_fortunes;
-			fortune_ctx->generator.stop = cleanup_fortunes;
 			fortune_ctx->num_result = 1;
 			fortune_ctx->param.command = FORTUNE_TABLE_NAME;
 			fortune_ctx->param.on_error = on_fortune_error;

+ 1 - 1
frameworks/C/h2o/src/fortune.h

@@ -28,9 +28,9 @@
 
 typedef struct {
 	list_t l;
-	PGresult *data;
 	h2o_iovec_t id;
 	h2o_iovec_t message;
+	char data[];
 } fortune_t;
 
 int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req);

+ 35 - 51
frameworks/C/h2o/src/request_handler.c

@@ -20,6 +20,7 @@
 #include <assert.h>
 #include <h2o.h>
 #include <stdalign.h>
+#include <stdint.h>
 #include <string.h>
 #include <yajl/yajl_gen.h>
 
@@ -29,56 +30,29 @@
 #include "utility.h"
 #include "world.h"
 
-typedef struct {
-	yajl_gen gen;
-	h2o_generator_t h2o_generator;
-} json_ctx_t;
+#define HELLO_RESPONSE "Hello, World!"
 
-static void cleanup_json_response(struct st_h2o_generator_t *self, h2o_req_t *req);
-static void complete_json_response(struct st_h2o_generator_t *self, h2o_req_t *req);
 static int json_serializer(struct st_h2o_handler_t *self, h2o_req_t *req);
 static int plaintext(struct st_h2o_handler_t *self, h2o_req_t *req);
 static const char *status_code_to_string(http_status_code_t status_code);
 
-static void cleanup_json_response(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	IGNORE_FUNCTION_PARAMETER(req);
-
-	json_ctx_t * const json_ctx = H2O_STRUCT_FROM_MEMBER(json_ctx_t, h2o_generator, self);
-
-	yajl_gen_free(json_ctx->gen);
-}
-
-static void complete_json_response(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	h2o_send(req, NULL, 0, true);
-	cleanup_json_response(self, req);
-}
-
 static int json_serializer(struct st_h2o_handler_t *self, h2o_req_t *req)
 {
 	IGNORE_FUNCTION_PARAMETER(self);
 
-	json_ctx_t * const json_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*json_ctx));
-
-	if (json_ctx) {
-		memset(json_ctx, 0, sizeof(*json_ctx));
-		json_ctx->gen = get_json_generator(&req->pool);
-		json_ctx->h2o_generator.proceed = complete_json_response;
-		json_ctx->h2o_generator.stop = cleanup_json_response;
+	const yajl_gen gen = get_json_generator(&req->pool);
 
-		if (json_ctx->gen) {
-			CHECK_YAJL_STATUS(yajl_gen_map_open, json_ctx->gen);
-			CHECK_YAJL_STATUS(yajl_gen_string, json_ctx->gen, YAJL_STRLIT("message"));
-			CHECK_YAJL_STATUS(yajl_gen_string, json_ctx->gen, YAJL_STRLIT("Hello, World!"));
-			CHECK_YAJL_STATUS(yajl_gen_map_close, json_ctx->gen);
+	if (gen) {
+		CHECK_YAJL_STATUS(yajl_gen_map_open, gen);
+		CHECK_YAJL_STATUS(yajl_gen_string, gen, YAJL_STRLIT("message"));
+		CHECK_YAJL_STATUS(yajl_gen_string, gen, YAJL_STRLIT(HELLO_RESPONSE));
+		CHECK_YAJL_STATUS(yajl_gen_map_close, gen);
 
-			if (!send_json_response(json_ctx->gen, &json_ctx->h2o_generator, req))
-				return 0;
+		if (!send_json_response(gen, NULL, req))
+			return 0;
 
 error_yajl:
-			yajl_gen_free(json_ctx->gen);
-		}
+		yajl_gen_free(gen);
 	}
 
 	send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
@@ -88,8 +62,8 @@ error_yajl:
 static int plaintext(struct st_h2o_handler_t *self, h2o_req_t *req)
 {
 	IGNORE_FUNCTION_PARAMETER(self);
-	set_default_response_param(PLAIN, req);
-	h2o_send_inline(req, H2O_STRLIT("Hello, World!"));
+	set_default_response_param(PLAIN, sizeof(HELLO_RESPONSE) - 1, req);
+	h2o_send_inline(req, H2O_STRLIT(HELLO_RESPONSE));
 	return 0;
 }
 
@@ -197,20 +171,29 @@ void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req
 
 int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req)
 {
+	const unsigned char *buf;
 	h2o_iovec_t h2o_iovec = {.len = 0};
 	int ret = EXIT_FAILURE;
 
-	static_assert(sizeof(h2o_iovec.base) == sizeof(const unsigned char *) &&
-	              alignof(h2o_iovec.base) == alignof(const unsigned char *),
-	              "Types must be compatible.");
-
-	if (yajl_gen_get_buf(gen,
-	                     (const unsigned char **) &h2o_iovec.base,
-	                     &h2o_iovec.len) == yajl_gen_status_ok) {
-		set_default_response_param(JSON, req);
-		h2o_start_response(req, h2o_generator);
-		h2o_send(req, &h2o_iovec, 1, false);
-		ret = EXIT_SUCCESS;
+	if (yajl_gen_get_buf(gen, &buf, &h2o_iovec.len) == yajl_gen_status_ok) {
+		if (h2o_generator) {
+			h2o_iovec.base = (char *) buf;
+			set_default_response_param(JSON, SIZE_MAX, req);
+			h2o_start_response(req, h2o_generator);
+			h2o_send(req, &h2o_iovec, 1, false);
+			ret = EXIT_SUCCESS;
+		}
+		else {
+			h2o_iovec.base = h2o_mem_alloc_pool(&req->pool, h2o_iovec.len);
+
+			if (h2o_iovec.base) {
+				memcpy(h2o_iovec.base, buf, h2o_iovec.len);
+				yajl_gen_free(gen);
+				set_default_response_param(JSON, h2o_iovec.len, req);
+				h2o_send_inline(req, h2o_iovec.base, h2o_iovec.len);
+				ret = EXIT_SUCCESS;
+			}
+		}
 	}
 
 	return ret;
@@ -228,8 +211,9 @@ void send_service_unavailable_error(const char *body, h2o_req_t *req)
 	                   H2O_SEND_ERROR_KEEP_HEADERS);
 }
 
-void set_default_response_param(content_type_t content_type, h2o_req_t *req)
+void set_default_response_param(content_type_t content_type, size_t content_length, h2o_req_t *req)
 {
+	req->res.content_length = content_length;
 	req->res.status = OK;
 	req->res.reason = status_code_to_string(req->res.status);
 

+ 3 - 1
frameworks/C/h2o/src/request_handler.h

@@ -46,6 +46,8 @@ void register_request_handlers(h2o_hostconf_t *hostconf, h2o_access_log_filehand
 void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req);
 int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req);
 void send_service_unavailable_error(const char *body, h2o_req_t *req);
-void set_default_response_param(content_type_t content_type, h2o_req_t *req);
+void set_default_response_param(content_type_t content_type,
+                                size_t content_length,
+                                h2o_req_t *req);
 
 #endif // REQUEST_H_

+ 8 - 2
frameworks/C/h2o/src/template.c

@@ -46,7 +46,10 @@ static uintmax_t read_template(mustache_api_t *api,
                                void *userdata,
                                char *buffer,
                                uintmax_t buffer_size);
-static void template_error(mustache_api_t *api, void *userdata, uintmax_t lineno, char *error);
+static void template_error(mustache_api_t *api,
+                           void *userdata,
+                           uintmax_t lineno,
+                           const char *error);
 
 static uintmax_t prerender_section(mustache_api_t *api,
                                    void *userdata,
@@ -101,7 +104,10 @@ static uintmax_t read_template(mustache_api_t *api,
 	return fread(buffer, sizeof(*buffer), buffer_size, template_input->input);
 }
 
-static void template_error(mustache_api_t *api, void *userdata, uintmax_t lineno, char *error)
+static void template_error(mustache_api_t *api,
+                           void *userdata,
+                           uintmax_t lineno,
+                           const char *error)
 {
 	IGNORE_FUNCTION_PARAMETER(api);
 

+ 5 - 28
frameworks/C/h2o/src/world.c

@@ -54,7 +54,6 @@ typedef struct {
 typedef struct {
 	db_query_param_t param;
 	yajl_gen gen;
-	h2o_generator_t h2o_generator;
 	const char *id_pointer;
 	h2o_req_t *req;
 	uint32_t id;
@@ -70,8 +69,6 @@ typedef struct {
 	query_result_t res[];
 } multiple_query_ctx_t;
 
-static void complete_request(struct st_h2o_generator_t *self, h2o_req_t *req);
-static void cleanup_request(struct st_h2o_generator_t *self, h2o_req_t *req);
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req);
 static int initialize_single_query_context(h2o_req_t *req,
                                            on_result_t on_result,
@@ -85,27 +82,9 @@ static int on_update_write_ready(db_query_param_t *param, PGconn *db_conn);
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen);
 static void serialize_items(const query_result_t *res,
                             size_t num_result,
-                            h2o_generator_t *h2o_generator,
                             yajl_gen gen,
                             h2o_req_t *req);
 
-static void cleanup_request(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	IGNORE_FUNCTION_PARAMETER(req);
-
-	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
-	                                                              h2o_generator,
-	                                                              self);
-
-	yajl_gen_free(query_ctx->gen);
-}
-
-static void complete_request(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	h2o_send(req, NULL, 0, true);
-	cleanup_request(self, req);
-}
-
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 {
 	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
@@ -178,8 +157,6 @@ static int initialize_single_query_context(h2o_req_t *req,
 	query_ctx->gen = get_json_generator(&req->pool);
 
 	if (query_ctx->gen) {
-		query_ctx->h2o_generator.proceed = complete_request;
-		query_ctx->h2o_generator.stop = cleanup_request;
 		query_ctx->id_format = 1;
 		query_ctx->id_len = sizeof(query_ctx->id);
 		query_ctx->id_pointer = (const char *) &query_ctx->id;
@@ -251,7 +228,6 @@ static result_return_t on_multiple_query_result(db_query_param_t *param, PGresul
 		else if (query_ctx->update_state == NO_UPDATE) {
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
-			                &query_ctx->single.h2o_generator,
 			                query_ctx->single.gen,
 			                query_ctx->single.req);
 			return DONE;
@@ -300,7 +276,7 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		PQclear(result);
 
 		if (!serialize_item(ntohl(query_ctx->id), random_number, query_ctx->gen) &&
-		    !send_json_response(query_ctx->gen, &query_ctx->h2o_generator, query_ctx->req))
+		    !send_json_response(query_ctx->gen, NULL, query_ctx->req))
 			return DONE;
 
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, query_ctx->req);
@@ -342,7 +318,6 @@ static result_return_t on_update_result(db_query_param_t *param, PGresult *resul
 
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
-			                &query_ctx->single.h2o_generator,
 			                query_ctx->single.gen,
 			                query_ctx->single.req);
 			ret = DONE;
@@ -354,6 +329,7 @@ static result_return_t on_update_result(db_query_param_t *param, PGresult *resul
 	PQclear(result);
 	return ret;
 error:
+	yajl_gen_free(query_ctx->single.gen);
 	send_error(BAD_GATEWAY, PQresultErrorMessage(result), query_ctx->single.req);
 	PQclear(result);
 	return DONE;
@@ -440,7 +416,6 @@ error_yajl:
 
 static void serialize_items(const query_result_t *res,
                             size_t num_result,
-                            h2o_generator_t *h2o_generator,
                             yajl_gen gen,
                             h2o_req_t *req)
 {
@@ -452,9 +427,11 @@ static void serialize_items(const query_result_t *res,
 
 	CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
 
-	if (send_json_response(gen, h2o_generator, req))
+	if (send_json_response(gen, NULL, req)) {
 error_yajl:
+		yajl_gen_free(gen);
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+	}
 }
 
 int multiple_queries(struct st_h2o_handler_t *self, h2o_req_t *req)

+ 1 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Configuration/AppSettings.cs

@@ -7,5 +7,6 @@ namespace Benchmarks.Configuration
     public class AppSettings
     {
         public string ConnectionString { get; set; }
+        public DatabaseServer Database { get; set; } = DatabaseServer.SqlServer;
     }
 }

+ 12 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Configuration/DatabaseServer.cs

@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+
+namespace Benchmarks.Configuration
+{
+    public enum DatabaseServer
+    {
+        SqlServer,
+        PostgreSql
+    }
+}

+ 30 - 12
frameworks/CSharp/aspnetcore/Benchmarks/Configuration/Scenarios.cs

@@ -44,6 +44,15 @@ namespace Benchmarks.Configuration
         [ScenarioPath("/queries/dapper")]
         public bool DbMultiQueryDapper { get; set; }
 
+        [ScenarioPath("/updates/raw")]
+        public bool DbMultiUpdateRaw { get; set; }
+
+        [ScenarioPath("/updates/ef")]
+        public bool DbMultiUpdateEf { get; set; }
+
+        [ScenarioPath("/updates/dapper")]
+        public bool DbMultiUpdateDapper { get; set; }
+
         [ScenarioPath("/fortunes/raw")]
         public bool DbFortunesRaw { get; set; }
 
@@ -62,23 +71,32 @@ namespace Benchmarks.Configuration
         [ScenarioPath("/mvc/view")]
         public bool MvcViews { get; set; }
 
-        //[ScenarioPath("/mvc/db/raw")]
-        //public bool MvcDbSingleQueryRaw { get; set; }
+        [ScenarioPath("/mvc/db/raw")]
+        public bool MvcDbSingleQueryRaw { get; set; }
+
+        [ScenarioPath("/mvc/db/dapper")]
+        public bool MvcDbSingleQueryDapper { get; set; }
+
+        [ScenarioPath("/mvc/db/ef")]
+        public bool MvcDbSingleQueryEf { get; set; }
+
+        [ScenarioPath("/mvc/queries/raw")]
+        public bool MvcDbMultiQueryRaw { get; set; }
 
-        //[ScenarioPath("/mvc/db/dapper")]
-        //public bool MvcDbSingleQueryDapper { get; set; }
+        [ScenarioPath("/mvc/queries/dapper")]
+        public bool MvcDbMultiQueryDapper { get; set; }
 
-        //[ScenarioPath("/mvc/db/ef")]
-        //public bool MvcDbSingleQueryEf { get; set; }
+        [ScenarioPath("/mvc/queries/ef")]
+        public bool MvcDbMultiQueryEf { get; set; }
 
-        //[ScenarioPath("/mvc/queries/raw")]
-        //public bool MvcDbMultiQueryRaw { get; set; }
+        [ScenarioPath("/mvc/updates/raw")]
+        public bool MvcDbMultiUpdateRaw { get; set; }
 
-        //[ScenarioPath("/mvc/queries/dapper")]
-        //public bool MvcDbMultiQueryDapper { get; set; }
+        [ScenarioPath("/mvc/updates/dapper")]
+        public bool MvcDbMultiUpdateDapper { get; set; }
 
-        //[ScenarioPath("/mvc/queries/ef")]
-        //public bool MvcDbMultiQueryEf { get; set; }
+        [ScenarioPath("/mvc/updates/ef")]
+        public bool MvcDbMultiUpdateEf { get; set; }
 
         [ScenarioPath("/mvc/fortunes/raw")]
         public bool MvcDbFortunesRaw { get; set; }

+ 11 - 51
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/FortunesController.cs

@@ -8,68 +8,28 @@ using Microsoft.Extensions.DependencyInjection;
 
 namespace Benchmarks.Controllers
 {
-    [Route("mvc")]
+    [Route("mvc/fortunes")]
     public class FortunesController : Controller
     {
-        private RawDb _rawDb;
-        private DapperDb _dapperDb;
-        private EfDb _efDb;
-
-        private RawDb RawDb
-        {
-            get
-            {
-                if (_rawDb == null)
-                {
-                    _rawDb = HttpContext.RequestServices.GetRequiredService<RawDb>();
-                }
-
-                return _rawDb;
-            }
-        }
-
-        private DapperDb DapperDb
-        {
-            get
-            {
-                if (_dapperDb == null)
-                {
-                    _dapperDb = HttpContext.RequestServices.GetRequiredService<DapperDb>();
-                }
-
-                return _dapperDb;
-            }
-        }
-
-        private EfDb EfDb
-        {
-            get
-            {
-                if (_efDb == null)
-                {
-                    _efDb = HttpContext.RequestServices.GetRequiredService<EfDb>();
-                }
-
-                return _efDb;
-            }
-        }
-
-        [HttpGet("fortunes/raw")]
+        [HttpGet("raw")]
         public async Task<IActionResult> Raw()
-        {
-            return View("Fortunes", await RawDb.LoadFortunesRows());
+        {   
+            var db = HttpContext.RequestServices.GetRequiredService<RawDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
         }
 
-        [HttpGet("fortunes/dapper")]
+        [HttpGet("dapper")]
         public async Task<IActionResult> Dapper()
         {
-            return View("Fortunes", await DapperDb.LoadFortunesRows());
+            var db = HttpContext.RequestServices.GetRequiredService<DapperDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
         }
 
-        [HttpGet("fortunes/ef")]
+        [HttpGet("ef")]
         public async Task<IActionResult> Ef()
         {
-            return View("Fortunes", await EfDb.LoadFortunesRows());
+            var db = HttpContext.RequestServices.GetRequiredService<EfDb>();
+            return View("Fortunes", await db.LoadFortunesRows());
         }
     }
 }

+ 42 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleQueriesController.cs

@@ -0,0 +1,42 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/queries")]
+    public class MultipleQueriesController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World[]> Raw(int queries = 1)
+        {
+            return ExecuteQuery<RawDb>(queries);
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World[]> Dapper(int queries = 1)
+        {
+            return ExecuteQuery<DapperDb>(queries);
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        public Task<World[]> Ef(int queries = 1)
+        {
+            return ExecuteQuery<EfDb>(queries);
+        }
+
+        private Task<World[]> ExecuteQuery<T>(int queries) where T : IDb
+        {
+            queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadMultipleQueriesRows(queries);
+        }
+    }
+}

+ 42 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleUpdatesController.cs

@@ -0,0 +1,42 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/updates")]
+    public class MultipleUpdatesController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World[]> Raw(int queries = 1)
+        {
+            return ExecuteQuery<RawDb>(queries);
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World[]> Dapper(int queries = 1)
+        {
+            return ExecuteQuery<DapperDb>(queries);
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        public Task<World[]> Ef(int queries = 1)
+        {
+            return ExecuteQuery<EfDb>(queries);
+        }
+
+        private Task<World[]> ExecuteQuery<T>(int queries) where T : IDb
+        {
+            queries = queries < 1 ? 1 : queries > 500 ? 500 : queries;
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadMultipleUpdatesRows(queries);
+        }
+    }
+}

+ 41 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Controllers/SingleQueryController.cs

@@ -0,0 +1,41 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Threading.Tasks;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Benchmarks.Controllers
+{
+    [Route("mvc/db")]
+    public class SingleQueryController : Controller
+    {
+        [HttpGet("raw")]
+        [Produces("application/json")]
+        public Task<World> Raw()
+        {
+            return ExecuteQuery<RawDb>();
+        }
+
+        [HttpGet("dapper")]
+        [Produces("application/json")]
+        public Task<World> Dapper()
+        {
+            return ExecuteQuery<DapperDb>();
+        }
+
+        [HttpGet("ef")]
+        [Produces("application/json")]
+        public Task<World> Ef()
+        {
+            return ExecuteQuery<EfDb>();
+        }
+
+        private Task<World> ExecuteQuery<T>() where T : IDb
+        {
+            var db = HttpContext.RequestServices.GetRequiredService<T>();
+            return db.LoadSingleQueryRow();
+        }
+    }
+}

+ 49 - 2
frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbContext.cs

@@ -1,9 +1,14 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
+using System.Linq;
 using Benchmarks.Configuration;
 using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
+using Microsoft.EntityFrameworkCore.Infrastructure.Internal;
 using Microsoft.Extensions.Options;
+using Microsoft.Extensions.DependencyInjection;
 
 namespace Benchmarks.Data
 {
@@ -20,9 +25,51 @@ namespace Benchmarks.Data
 
         public DbSet<Fortune> Fortune { get; set; }
 
+        public bool UseBatchUpdate 
+        { 
+            get
+            {
+                return _appSettings.Database != DatabaseServer.PostgreSql;
+            }
+        } 
+
         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
         {
-            optionsBuilder.UseSqlServer(_appSettings.ConnectionString);
+            if (_appSettings.Database == DatabaseServer.PostgreSql)
+            {
+                optionsBuilder.UseNpgsql(_appSettings.ConnectionString);
+            }
+            else
+            {
+                var extension = GetOrCreateExtension(optionsBuilder);
+                extension.ConnectionString = _appSettings.ConnectionString;
+                ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
+            }
+        }
+
+        private static SqlServerOptionsExtension GetOrCreateExtension(DbContextOptionsBuilder optionsBuilder)
+        {
+            var existing = optionsBuilder.Options.FindExtension<NoTxSqlServerOptionsExtension>();
+            return existing != null
+                ? new NoTxSqlServerOptionsExtension(existing)
+                : new NoTxSqlServerOptionsExtension();
+        }
+
+        private class NoTxSqlServerOptionsExtension : SqlServerOptionsExtension
+        {
+            public NoTxSqlServerOptionsExtension()
+            {
+            }
+
+            public NoTxSqlServerOptionsExtension(NoTxSqlServerOptionsExtension copyFrom) : base(copyFrom)
+            {
+            }
+            public override void ApplyServices(IServiceCollection services)
+            {
+                base.ApplyServices(services);
+                services.Remove(services.First((sd) => sd.ServiceType == typeof(ISqlServerConnection)));
+                services.AddScoped<ISqlServerConnection, NoTransactionSqlServerConnection>();
+            }
         }
     }
-}
+}

+ 24 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/BatchUpdateString.cs

@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Benchmarks.Data
+{
+    internal class BatchUpdateString
+    {
+        public static IList<BatchUpdateString> Strings { get;} = 
+            Enumerable.Range(0, 500)
+                      .Select(i => new BatchUpdateString
+                      {
+                          Id = $"Id_{i}",
+                          Random = $"Random_{i}",
+                          UpdateQuery = $"UPDATE world SET randomnumber = @Random_{i} WHERE id = @Id_{i};"
+                      }).ToArray();
+                        
+        public string Id { get; set; }
+        public string Random { get; set; }
+        public string UpdateQuery { get; set; }
+    }
+}

+ 50 - 12
frameworks/CSharp/aspnetcore/Benchmarks/Data/DapperDb.cs

@@ -1,8 +1,11 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
+using System;
 using System.Collections.Generic;
 using System.Data.Common;
+using System.Dynamic;
+using System.Text;
 using System.Threading.Tasks;
 using Benchmarks.Configuration;
 using Dapper;
@@ -10,7 +13,7 @@ using Microsoft.Extensions.Options;
 
 namespace Benchmarks.Data
 {
-    public class DapperDb
+    public class DapperDb : IDb
     {
         private readonly IRandom _random;
         private readonly DbProviderFactory _dbProviderFactory;
@@ -30,15 +33,39 @@ namespace Benchmarks.Data
                 db.ConnectionString = _connectionString;
 
                 // Note: Don't need to open connection if only doing one thing; let dapper do it
-                return await db.QueryFirstOrDefaultAsync<World>(
-                    "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id",
-                    new { Id = _random.Next(1, 10001) });
+                return await ReadSingleRow(db);
             }
         }
 
+        async Task<World> ReadSingleRow(DbConnection db)
+        {
+            return await db.QueryFirstOrDefaultAsync<World>(
+                    "SELECT id, randomnumber FROM world WHERE id = @Id",
+                    new { Id = _random.Next(1, 10001) });
+        }
+
         public async Task<World[]> LoadMultipleQueriesRows(int count)
         {
-            var result = new World[count];
+            var results = new World[count];
+            using (var db = _dbProviderFactory.CreateConnection())
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
+
+                for (int i = 0; i < count; i++)
+                {
+                    results[i] = await ReadSingleRow(db);
+                }
+            }
+
+            return results;
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+            IDictionary<string, object> parameters = new ExpandoObject();
+            var updateCommand = new StringBuilder(count);
 
             using (var db = _dbProviderFactory.CreateConnection())
             {
@@ -47,15 +74,26 @@ namespace Benchmarks.Data
 
                 for (int i = 0; i < count; i++)
                 {
-                    result[i] = await db.QueryFirstOrDefaultAsync<World>(
-                        "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id",
-                        new { Id = _random.Next(1, 10001) });
+                    results[i] = await ReadSingleRow(db);
+                }
+
+                // postgres has problems with deadlocks when these aren't sorted
+                Array.Sort<World>(results, (a, b) => a.Id.CompareTo(b.Id));
+
+                for (int i = 0; i < count; i++)
+                {
+                    var randomNumber = _random.Next(1, 10001);
+                    parameters[BatchUpdateString.Strings[i].Random] = randomNumber;
+                    parameters[BatchUpdateString.Strings[i].Id] = results[i].Id;
+
+                    results[i].RandomNumber = randomNumber;
+                    updateCommand.Append(BatchUpdateString.Strings[i].UpdateQuery);
                 }
 
-                db.Close();
+                await db.ExecuteAsync(updateCommand.ToString(), parameters);
             }
 
-            return result;
+            return results;
         }
 
         public async Task<IEnumerable<Fortune>> LoadFortunesRows()
@@ -67,7 +105,7 @@ namespace Benchmarks.Data
                 db.ConnectionString = _connectionString;
 
                 // Note: don't need to open connection if only doing one thing; let dapper do it
-                result = (await db.QueryAsync<Fortune>("SELECT [Id], [Message] FROM [Fortune]")).AsList();
+                result = (await db.QueryAsync<Fortune>("SELECT id, message FROM fortune")).AsList();
             }
 
             result.Add(new Fortune { Message = "Additional fortune added at request time." });
@@ -76,4 +114,4 @@ namespace Benchmarks.Data
             return result;
         }
     }
-}
+}

+ 27 - 3
frameworks/CSharp/aspnetcore/Benchmarks/Data/EfDb.cs

@@ -7,7 +7,7 @@ using Microsoft.EntityFrameworkCore;
 
 namespace Benchmarks.Data
 {
-    public class EfDb
+    public class EfDb : IDb
     {
         private readonly IRandom _random;
         private readonly ApplicationDbContext _dbContext;
@@ -22,7 +22,6 @@ namespace Benchmarks.Data
         public Task<World> LoadSingleQueryRow()
         {
             var id = _random.Next(1, 10001);
-            
             return _dbContext.World.FirstAsync(w => w.Id == id);
         }
 
@@ -39,6 +38,31 @@ namespace Benchmarks.Data
             return result;
         }
 
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+
+            for (int i = 0; i < count; i++)
+            {
+                var id = _random.Next(1, 10001);
+                var result = await _dbContext.World.AsTracking().FirstAsync(w => w.Id == id);
+
+                result.RandomNumber = _random.Next(1, 10001);
+                results[i] = result;
+                if(!_dbContext.UseBatchUpdate)
+                {
+                    await _dbContext.SaveChangesAsync();
+                }
+            }
+            
+            if(_dbContext.UseBatchUpdate)
+            {
+                await _dbContext.SaveChangesAsync();
+            }
+
+            return results;
+        }
+
         public async Task<IEnumerable<Fortune>> LoadFortunesRows()
         {
             var result = await _dbContext.Fortune.ToListAsync();
@@ -49,4 +73,4 @@ namespace Benchmarks.Data
             return result;
         }
     }
-}
+}

+ 4 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/Fortune.cs

@@ -3,13 +3,17 @@
 
 using System;
 using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
 
 namespace Benchmarks.Data
 {
+    [Table("fortune")]
     public class Fortune : IComparable<Fortune>, IComparable
     {
+        [Column("id")]
         public int Id { get; set; }
 
+        [Column("message")]
         [StringLength(2048)]
         public string Message { get; set; }
         

+ 19 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/IDb.cs

@@ -0,0 +1,19 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Benchmarks.Data
+{
+    public interface IDb
+    {
+        Task<World> LoadSingleQueryRow();
+
+        Task<World[]> LoadMultipleQueriesRows(int count);
+
+        Task<World[]> LoadMultipleUpdatesRows(int count);
+
+        Task<IEnumerable<Fortune>> LoadFortunesRows();
+    }
+}

+ 43 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/NoTransactionSqlServerConnection.cs

@@ -0,0 +1,43 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System.Data;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.Storage.Internal;
+using Microsoft.Extensions.Logging;
+
+namespace Benchmarks.Data
+{
+    class NoTransactionSqlServerConnection : SqlServerConnection
+    {
+        public NoTransactionSqlServerConnection(IDbContextOptions options, ILogger<SqlServerConnection> logger)
+            : base(options, logger)
+        {
+        }
+
+        public override Task<IDbContextTransaction> BeginTransactionAsync(
+            IsolationLevel isolationLevel, CancellationToken cancellationToken = new CancellationToken())
+            => Task.FromResult<IDbContextTransaction>(new FakeTransaction());
+
+        public override IDbContextTransaction BeginTransaction(IsolationLevel isolationLevel) 
+            => new FakeTransaction();
+
+        private class FakeTransaction : IDbContextTransaction
+        {
+            public void Dispose()
+            {
+            }
+
+            public void Commit()
+            {
+            }
+
+            public void Rollback()
+            {
+            }
+        }
+    }
+}

+ 6 - 3
frameworks/CSharp/aspnetcore/Benchmarks/Data/Random.cs

@@ -2,16 +2,19 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 using System;
+using System.Threading;
 
 namespace Benchmarks.Data
 {
     public class DefaultRandom : IRandom
     {
-        private readonly Random _random = new Random();
+        private static int nextSeed = 0;
+        // Random isn't thread safe
+        private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref nextSeed)));
 
         public int Next(int minValue, int maxValue)
         {
-            return _random.Next(minValue, maxValue);
+            return _random.Value.Next(minValue, maxValue);
         }
     }
-}
+}

+ 83 - 39
frameworks/CSharp/aspnetcore/Benchmarks/Data/RawDb.cs

@@ -1,21 +1,23 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
+using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Data.Common;
+using System.Text;
 using System.Threading.Tasks;
 using Benchmarks.Configuration;
 using Microsoft.Extensions.Options;
 
 namespace Benchmarks.Data
 {
-    public class RawDb
+    public class RawDb : IDb
     {
         private readonly IRandom _random;
         private readonly DbProviderFactory _dbProviderFactory;
         private readonly string _connectionString;
-
+        
         public RawDb(IRandom random, DbProviderFactory dbProviderFactory, IOptions<AppSettings> appSettings)
         {
             _random = random;
@@ -26,66 +28,108 @@ namespace Benchmarks.Data
         public async Task<World> LoadSingleQueryRow()
         {
             using (var db = _dbProviderFactory.CreateConnection())
-            using (var cmd = db.CreateCommand())
+            using (var cmd = CreateReadCommand(db))
             {
-                cmd.CommandText = "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id";
-                var id = cmd.CreateParameter();
-                id.ParameterName = "@Id";
-                id.DbType = DbType.Int32;
-                id.Value = _random.Next(1, 10001);
-                cmd.Parameters.Add(id);
-
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
 
-                using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
-                {
-                    await rdr.ReadAsync();
+                return await ReadSingleRow(db, cmd);
+            }
+        }
+        
+        async Task<World> ReadSingleRow(DbConnection connection, DbCommand cmd)
+        {
+            using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
+            {
+                await rdr.ReadAsync();
 
-                    return new World
-                    {
-                        Id = rdr.GetInt32(0),
-                        RandomNumber = rdr.GetInt32(1)
-                    };
-                }
+                return new World
+                {
+                    Id = rdr.GetInt32(0),
+                    RandomNumber = rdr.GetInt32(1)
+                };
             }
         }
 
+        DbCommand CreateReadCommand(DbConnection connection)
+        {
+            var cmd = connection.CreateCommand();
+            cmd.CommandText = "SELECT id, randomnumber FROM world WHERE id = @Id";
+            var id = cmd.CreateParameter();
+            id.ParameterName = "@Id";
+            id.DbType = DbType.Int32;
+            id.Value = _random.Next(1, 10001);
+            cmd.Parameters.Add(id);
+
+            return cmd;
+        }
+
         public async Task<World[]> LoadMultipleQueriesRows(int count)
         {
             var result = new World[count];
 
             using (var db = _dbProviderFactory.CreateConnection())
-            using (var cmd = db.CreateCommand())
+            using (var cmd = CreateReadCommand(db))
             {
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
+                for (int i = 0; i < count; i++)
+                {
+                    result[i] = await ReadSingleRow(db, cmd);
+                    cmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                }
+            }
 
-                cmd.CommandText = "SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id";
-                var id = cmd.CreateParameter();
-                id.ParameterName = "@Id";
-                id.DbType = DbType.Int32;
-                cmd.Parameters.Add(id);
+            return result;
+        }
+
+        public async Task<World[]> LoadMultipleUpdatesRows(int count)
+        {
+            var results = new World[count];
+           
+            var updateCommand = new StringBuilder(count);
+
+            using (var db = _dbProviderFactory.CreateConnection())
+            using (var updateCmd = db.CreateCommand())
+            using (var queryCmd = CreateReadCommand(db))
+            {
+                db.ConnectionString = _connectionString;
+                await db.OpenAsync();
 
                 for (int i = 0; i < count; i++)
                 {
-                    id.Value = _random.Next(1, 10001);
-                    using (var rdr = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow))
-                    {
-                        await rdr.ReadAsync();
+                    results[i] = await ReadSingleRow(db, queryCmd);
+                    queryCmd.Parameters["@Id"].Value = _random.Next(1, 10001);
+                }
 
-                        result[i] = new World
-                        {
-                            Id = rdr.GetInt32(0),
-                            RandomNumber = rdr.GetInt32(1)
-                        };
-                    }
+                // postgres has problems with deadlocks when these aren't sorted
+                Array.Sort<World>(results, (a, b) => a.Id.CompareTo(b.Id));
+
+                for(int i = 0; i < count; i++)
+                {
+                    var id = updateCmd.CreateParameter();
+                    id.ParameterName = BatchUpdateString.Strings[i].Id;
+                    id.DbType = DbType.Int32;
+                    updateCmd.Parameters.Add(id);
+
+                    var random = updateCmd.CreateParameter();
+                    random.ParameterName = BatchUpdateString.Strings[i].Random;
+                    id.DbType = DbType.Int32;
+                    updateCmd.Parameters.Add(random);
+
+                    var randomNumber = _random.Next(1, 10001);
+                    id.Value = results[i].Id;
+                    random.Value = randomNumber;
+                    results[i].RandomNumber = randomNumber;
+
+                    updateCommand.Append(BatchUpdateString.Strings[i].UpdateQuery);
                 }
 
-                db.Close();
+                updateCmd.CommandText = updateCommand.ToString();
+                await updateCmd.ExecuteNonQueryAsync();
             }
 
-            return result;
+            return results;
         }
 
         public async Task<IEnumerable<Fortune>> LoadFortunesRows()
@@ -95,7 +139,7 @@ namespace Benchmarks.Data
             using (var db = _dbProviderFactory.CreateConnection())
             using (var cmd = db.CreateCommand())
             {
-                cmd.CommandText = "SELECT [Id], [Message] FROM [Fortune]";
+                cmd.CommandText = "SELECT id, message FROM fortune";
 
                 db.ConnectionString = _connectionString;
                 await db.OpenAsync();
@@ -119,4 +163,4 @@ namespace Benchmarks.Data
             return result;
         }
     }
-}
+}

+ 5 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Data/World.cs

@@ -1,12 +1,17 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
+using System.ComponentModel.DataAnnotations.Schema;
+
 namespace Benchmarks.Data
 {
+    [Table("world")]
     public class World
     {
+        [Column("id")] 
         public int Id { get; set; }
 
+        [Column("randomnumber")] 
         public int RandomNumber { get; set; }
     }
 }

+ 0 - 47
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/ErrorHandlerMiddleware.cs

@@ -1,47 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
-
-using System;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-
-namespace Benchmarks.Middleware
-{
-    public class ErrorHandlerMiddleware
-    {
-        private readonly RequestDelegate _next;
-
-        public ErrorHandlerMiddleware(RequestDelegate next)
-        {
-            _next = next;
-        }
-
-        public async Task Invoke(HttpContext httpContext)
-        {
-            try
-            {
-                await _next(httpContext);
-            }
-            catch (Exception ex)
-            {
-                if (!httpContext.Response.HasStarted)
-                {
-                    httpContext.Response.Clear();
-                    httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
-                    httpContext.Response.ContentType = "text/html";
-                }
-                
-                await httpContext.Response.WriteAsync($"<pre style='color:red'>{ex.ToString()}</pre>");
-            }
-        }
-    }
-
-    public static class ErrorHandlerMiddlewareExtensions
-    {
-        public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder)
-        {
-            return builder.UseMiddleware<ErrorHandlerMiddleware>();
-        }
-    }
-}

+ 5 - 5
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesEfMiddleware.cs

@@ -8,6 +8,7 @@ using Benchmarks.Configuration;
 using Benchmarks.Data;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 
 namespace Benchmarks.Middleware
 {
@@ -16,13 +17,11 @@ namespace Benchmarks.Middleware
         private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbFortunesEf));
 
         private readonly RequestDelegate _next;
-        private readonly EfDb _db;
         private readonly HtmlEncoder _htmlEncoder;
 
-        public FortunesEfMiddleware(RequestDelegate next, EfDb db, HtmlEncoder htmlEncoder)
+        public FortunesEfMiddleware(RequestDelegate next, HtmlEncoder htmlEncoder)
         {
             _next = next;
-            _db = db;
             _htmlEncoder = htmlEncoder;
         }
 
@@ -30,7 +29,8 @@ namespace Benchmarks.Middleware
         {
             if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
             {
-                var rows = await _db.LoadFortunesRows();
+                var db = httpContext.RequestServices.GetService<EfDb>();
+                var rows = await db.LoadFortunesRows();
 
                 await MiddlewareHelpers.RenderFortunesHtml(rows, httpContext, _htmlEncoder);
 
@@ -48,4 +48,4 @@ namespace Benchmarks.Middleware
             return builder.UseMiddleware<FortunesEfMiddleware>();
         }
     }
-}
+}

+ 5 - 5
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs

@@ -7,6 +7,7 @@ using Benchmarks.Configuration;
 using Benchmarks.Data;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
 
@@ -21,20 +22,19 @@ namespace Benchmarks.Middleware
         };
 
         private readonly RequestDelegate _next;
-        private readonly EfDb _db;
 
-        public MultipleQueriesEfMiddleware(RequestDelegate next, EfDb db)
+        public MultipleQueriesEfMiddleware(RequestDelegate next)
         {
             _next = next;
-            _db = db;
         }
 
         public async Task Invoke(HttpContext httpContext)
         {
             if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
             {
+                var db = httpContext.RequestServices.GetService<EfDb>();
                 var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
-                var rows = await _db.LoadMultipleQueriesRows(count);
+                var rows = await db.LoadMultipleQueriesRows(count);
 
                 var result = JsonConvert.SerializeObject(rows, _jsonSettings);
 
@@ -58,4 +58,4 @@ namespace Benchmarks.Middleware
             return builder.UseMiddleware<MultipleQueriesEfMiddleware>();
         }
     }
-}
+}

+ 61 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesDapperMiddleware.cs

@@ -0,0 +1,61 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class MultipleUpdatesDapperMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateDapper));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+        private readonly DapperDb _db;
+
+        public MultipleUpdatesDapperMiddleware(RequestDelegate next, DapperDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+                var rows = await _db.LoadMultipleUpdatesRows(count);
+
+                var result = JsonConvert.SerializeObject(rows, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class MultipleUpdatesDapperMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesDapper(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesDapperMiddleware>();
+        }
+    }
+}

+ 61 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesEfMiddleware.cs

@@ -0,0 +1,61 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class MultipleUpdatesEfMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateEf));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+
+        public MultipleUpdatesEfMiddleware(RequestDelegate next)
+        {
+            _next = next;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var db = httpContext.RequestServices.GetService<EfDb>();
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+                var rows = await db.LoadMultipleUpdatesRows(count);
+
+                var result = JsonConvert.SerializeObject(rows, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class MultipleUpdatesEfMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesEf(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesEfMiddleware>();
+        }
+    }
+}

+ 61 - 0
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesRawMiddleware.cs

@@ -0,0 +1,61 @@
+// Copyright (c) .NET Foundation. All rights reserved. 
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
+
+using System;
+using System.Threading.Tasks;
+using Benchmarks.Configuration;
+using Benchmarks.Data;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace Benchmarks.Middleware
+{
+    public class MultipleUpdatesRawMiddleware
+    {
+        private static readonly PathString _path = new PathString(Scenarios.GetPath(s => s.DbMultiUpdateRaw));
+        private static readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
+        {
+            ContractResolver = new CamelCasePropertyNamesContractResolver()
+        };
+
+        private readonly RequestDelegate _next;
+        private readonly RawDb _db;
+
+        public MultipleUpdatesRawMiddleware(RequestDelegate next, RawDb db)
+        {
+            _next = next;
+            _db = db;
+        }
+
+        public async Task Invoke(HttpContext httpContext)
+        {
+            if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
+            {
+                var count = MiddlewareHelpers.GetMultipleQueriesQueryCount(httpContext);
+                var rows = await _db.LoadMultipleUpdatesRows(count);
+
+                var result = JsonConvert.SerializeObject(rows, _jsonSettings);
+
+                httpContext.Response.StatusCode = StatusCodes.Status200OK;
+                httpContext.Response.ContentType = "application/json";
+                httpContext.Response.ContentLength = result.Length;
+
+                await httpContext.Response.WriteAsync(result);
+
+                return;
+            }
+
+            await _next(httpContext);
+        }
+    }
+
+    public static class MultipleUpdatesRawMiddlewareExtensions
+    {
+        public static IApplicationBuilder UseMultipleUpdatesRaw(this IApplicationBuilder builder)
+        {
+            return builder.UseMiddleware<MultipleUpdatesRawMiddleware>();
+        }
+    }
+}

+ 5 - 5
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/SingleQueryEfMiddleware.cs

@@ -7,6 +7,7 @@ using Benchmarks.Configuration;
 using Benchmarks.Data;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
 
@@ -21,19 +22,18 @@ namespace Benchmarks.Middleware
         };
 
         private readonly RequestDelegate _next;
-        private readonly EfDb _db;
 
-        public SingleQueryEfMiddleware(RequestDelegate next, EfDb db)
+        public SingleQueryEfMiddleware(RequestDelegate next)
         {
             _next = next;
-            _db = db;
         }
 
         public async Task Invoke(HttpContext httpContext)
         {
             if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
             {
-                var row = await _db.LoadSingleQueryRow();
+                var db = httpContext.RequestServices.GetService<EfDb>();
+                var row = await db.LoadSingleQueryRow();
                 var result = JsonConvert.SerializeObject(row, _jsonSettings);
 
                 httpContext.Response.StatusCode = StatusCodes.Status200OK;
@@ -56,4 +56,4 @@ namespace Benchmarks.Middleware
             return builder.UseMiddleware<SingleQueryEfMiddleware>();
         }
     }
-}
+}

+ 1 - 2
frameworks/CSharp/aspnetcore/Benchmarks/NuGet.Config

@@ -1,8 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <configuration>
   <packageSources>
     <clear />
-    <add key="AspNetVNext" value="https://dotnet.myget.org/F/aspnet1/api/v3/index.json" />
     <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
   </packageSources>
 </configuration>

+ 41 - 8
frameworks/CSharp/aspnetcore/Benchmarks/Startup.cs

@@ -4,6 +4,8 @@
 using System;
 using System.Data.Common;
 using System.Data.SqlClient;
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
 using Benchmarks.Configuration;
 using Benchmarks.Data;
 using Benchmarks.Middleware;
@@ -11,7 +13,9 @@ using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
 using Microsoft.Extensions.PlatformAbstractions;
+using Npgsql;
 
 namespace Benchmarks
 {
@@ -49,11 +53,21 @@ namespace Benchmarks
             services.AddSingleton<ApplicationDbSeeder>();
             services.AddEntityFrameworkSqlServer()
                 .AddDbContext<ApplicationDbContext>();
-
+            
             if (Scenarios.Any("Raw") || Scenarios.Any("Dapper"))
             {
-                // TODO: Add support for plugging in different DbProviderFactory implementations via configuration
-                services.AddSingleton<DbProviderFactory>(SqlClientFactory.Instance);
+                services.AddSingleton<DbProviderFactory>((provider) => {
+                    var settings = provider.GetRequiredService<IOptions<AppSettings>>().Value;
+
+                    if (settings.Database == DatabaseServer.PostgreSql)
+                    {
+                        return NpgsqlFactory.Instance;
+                    }
+                    else
+                    {
+                        return SqlClientFactory.Instance;
+                    }
+                });
             }
 
             if (Scenarios.Any("Ef"))
@@ -73,7 +87,12 @@ namespace Benchmarks
 
             if (Scenarios.Any("Fortunes"))
             {
-                services.AddWebEncoders();
+                var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
+                settings.AllowCharacter('\u2014');  // allow EM DASH through
+                services.AddWebEncoders((options) =>
+                {
+                    options.TextEncoderSettings = settings;
+                });
             }
 
             if (Scenarios.Any("Mvc"))
@@ -83,7 +102,7 @@ namespace Benchmarks
                     //.AddApplicationPart(typeof(Startup).GetTypeInfo().Assembly)
                     .AddControllersAsServices();
 
-                if (Scenarios.MvcJson)
+                if (Scenarios.MvcJson || Scenarios.Any("MvcDbSingle") || Scenarios.Any("MvcDbMulti"))
                 {
                     mvcBuilder.AddJsonFormatters();
                 }
@@ -99,8 +118,6 @@ namespace Benchmarks
 
         public void Configure(IApplicationBuilder app, ApplicationDbSeeder dbSeeder, ApplicationDbContext dbContext)
         {
-            app.UseErrorHandler();
-
             if (Scenarios.Plaintext)
             {
                 app.UsePlainText();
@@ -143,6 +160,22 @@ namespace Benchmarks
                 app.UseMultipleQueriesEf();
             }
 
+            // Multiple update endpoints
+            if (Scenarios.DbMultiUpdateRaw)
+            {
+                app.UseMultipleUpdatesRaw();
+            }
+
+            if (Scenarios.DbMultiUpdateDapper)
+            {
+                app.UseMultipleUpdatesDapper();
+            }
+
+            if (Scenarios.DbMultiUpdateEf)
+            {
+                app.UseMultipleUpdatesEf();
+            }
+
             // Fortunes endpoints
             if (Scenarios.DbFortunesRaw)
             {
@@ -182,4 +215,4 @@ namespace Benchmarks
             app.RunDebugInfoPage();
         }
     }
-}
+}

+ 0 - 3
frameworks/CSharp/aspnetcore/Benchmarks/appsettings.json

@@ -1,3 +0,0 @@
-{
-  "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-Benchmarks;Trusted_Connection=True;MultipleActiveResultSets=true"
-}

+ 4 - 0
frameworks/CSharp/aspnetcore/Benchmarks/appsettings.postgresql.json

@@ -0,0 +1,4 @@
+{
+  "ConnectionString": "Server={db_server_placeholder};Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024",
+  "Database": "postgresql"
+}

+ 21 - 10
frameworks/CSharp/aspnetcore/Benchmarks/project.json

@@ -1,8 +1,15 @@
 {
   "version": "1.0.0-*",
-  "compilationOptions": {
+  "buildOptions": {
     "emitEntryPoint": true,
-    "preserveCompilationContext": true
+    "preserveCompilationContext": true,
+    "copyToOutput": {
+      "include": [
+        "appsettings.json",
+        "wwwroot",
+        "Views"
+      ]
+    }
   },
   "dependencies": {
     "Dapper": "1.50.0-*",
@@ -18,7 +25,9 @@
     "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0-*",
     "Microsoft.Extensions.Configuration.Json": "1.0.0-*",
     "Microsoft.Extensions.Configuration.CommandLine": "1.0.0-*",
-    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-*"
+    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-*",
+    "Npgsql": "3.1.8-*",
+    "Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.2-*" 
   },
   "frameworks": {
     "netcoreapp1.0": {
@@ -28,7 +37,7 @@
           "version": "1.0.0-*",
           "type": "platform"
         },
-        "System.Runtime.Serialization.Primitives": "4.1.0-*"
+        "System.Runtime.Serialization.Primitives": "4.1.1-*"
       }
     },
     "net451": { }
@@ -42,14 +51,16 @@
       ]
     }
   },
-  "content": [
-    "appsettings.json",
-    "wwwroot",
-    "Views"
-  ],
+  "publishOptions": {
+    "include": [
+      "appsettings.json",
+      "wwwroot",
+      "Views"
+    ]
+  },
   "runtimeOptions": {
     "configProperties": {
       "System.GC.Server": true
     }
   }
-}
+}

+ 184 - 10
frameworks/CSharp/aspnetcore/benchmark_config.json

@@ -2,8 +2,7 @@
   "framework": "aspnetcore",
   "tests": [{
     "default": {
-      "setup_file": "setup",
-      "json_url": "/json",
+      "setup_file": "setup-plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
@@ -14,7 +13,93 @@
       "flavor": "Mono",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-json": {
+      "setup_file": "setup-json",
+      "json_url": "/json",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-raw": {
+      "setup_file": "setup-raw",
+      "db_url": "/db/raw",
+      "query_url": "/queries/raw?queries=",
+      "update_url": "/updates/raw?queries=",
+      "fortune_url": "/fortunes/raw",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-ef": {
+      "setup_file": "setup-ef",
+      "db_url": "/db/ef",
+      "query_url": "/queries/ef?queries=",
+      "update_url": "/updates/ef?queries=",
+      "fortune_url": "/fortunes/ef",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Full",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-dapper": {
+      "setup_file": "setup-dapper",
+      "db_url": "/db/dapper",
+      "query_url": "/queries/dapper?queries=",
+      "update_url": "/updates/dapper?queries=",
+      "fortune_url": "/fortunes/dapper",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Micro",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-linux",
@@ -22,8 +107,7 @@
       "versus": ""
     },
     "mvc-linux": {
-      "setup_file": "setup-mvc",
-      "json_url": "/mvc/json",
+      "setup_file": "setup-plaintext",
       "plaintext_url": "/mvc/plaintext",
       "port": 8080,
       "approach": "Realistic",
@@ -33,13 +117,99 @@
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-linux",
       "notes": "",
       "versus": ""
     },
+    "mvc-linux-json": {
+      "setup_file": "setup-json",
+      "json_url": "/mvc/json",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-raw": {
+      "setup_file": "setup-raw",
+      "db_url": "/mvc/db/raw",
+      "query_url": "/mvc/queries/raw?queries=",
+      "update_url": "/mvc/updates/raw?queries=",
+      "fortune_url": "/mvc/fortunes/raw",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-ef": {
+      "setup_file": "setup-ef",
+      "db_url": "/mvc/db/ef",
+      "query_url": "/mvc/queries/ef?queries=",
+      "update_url": "/mvc/updates/ef?queries=",
+      "fortune_url": "/mvc/fortunes/ef",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Full",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-dapper": {
+      "setup_file": "setup-dapper",
+      "db_url": "/mvc/db/dapper",
+      "query_url": "/mvc/queries/dapper?queries=",
+      "update_url": "/mvc/updates/dapper?queries=",
+      "fortune_url": "/mvc/fortunes/dapper",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Micro",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
     "win": {
       "setup_file": "setup-windows",
       "json_url": "/json",
@@ -52,7 +222,8 @@
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "aspnetcore-win",
@@ -71,7 +242,8 @@
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-win",
@@ -90,7 +262,8 @@
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "WebListener",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "aspnetcore-weblistener",
@@ -109,7 +282,8 @@
       "language": "C#",
       "orm": "Raw",
       "platform": "NET",
-      "webserver": "WebListener",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-weblistener",

+ 10 - 2
frameworks/CSharp/aspnetcore/run-linux.sh

@@ -1,8 +1,16 @@
 #!/bin/bash
 fw_depends mono dotnetcore
-sudo apt-get install unzip libunwind8 -y
+
+threadCount=$2
+if [ "$threadCount" -lt "1" ]
+then
+    threadCount=1
+fi
 
 cd Benchmarks
+cp appsettings.postgresql.json appsettings.json
+sed -i 's|{db_server_placeholder}|'"${DBHOST}"'|g' appsettings.json
 dotnet restore
 dotnet build -c Release -f netcoreapp1.0
-dotnet run -c Release server.urls=http://*:8080 scenarios=$1 server=kestrel threadCount=1 NonInteractive=true &
+
+dotnet run -c Release server.urls=http://*:8080 scenarios=$1 server=kestrel threadCount=$threadCount NonInteractive=true &

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-dapper.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+source run-linux.sh dapper $(($(nproc)/2))

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-ef.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+source run-linux.sh ef $(($(nproc)/2))

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-json.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+source run-linux.sh json $(nproc)

+ 0 - 3
frameworks/CSharp/aspnetcore/setup-mvc.sh

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-source run-linux.sh mvcjson,mvcplain

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-plaintext.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+source run-linux.sh plaintext $(($(nproc)*3/10))

+ 3 - 0
frameworks/CSharp/aspnetcore/setup-raw.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+source run-linux.sh raw $(($(nproc)/2))

+ 0 - 3
frameworks/CSharp/aspnetcore/setup.sh

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-source run-linux.sh [default]

+ 17 - 4
frameworks/CSharp/revenj/Revenj.Bench/RestService.cs

@@ -98,7 +98,8 @@ namespace Revenj.Bench
 			return ReturnJSON(world, ctx.Stream);
 		}
 
-		private void LoadWorlds(int repeat, Context ctx)
+		/* bulk loading of worlds. use such pattern for production code */
+		private void LoadWorldsFast(int repeat, Context ctx)
 		{
 			var reader = ctx.BulkReader;
 			var lazyResult = ctx.LazyWorlds;
@@ -114,6 +115,18 @@ namespace Revenj.Bench
 				worlds[i] = lazyResult[i].Value;
 		}
 
+		/* multiple roundtrips loading of worlds. don't write such production code */
+		private void LoadWorldsSlow(int repeat, Context ctx)
+		{
+			var worlds = ctx.Worlds;
+			var repository = ctx.WorldRepository;
+			for (int i = 0; i < repeat; i++)
+			{
+				var id = Random.Next(10000) + 1;
+				worlds[i] = repository.Find(IDs[id]);
+			}
+		}
+
 		public Stream MultipleQueries(string count)
 		{
 			int repeat;
@@ -121,7 +134,7 @@ namespace Revenj.Bench
 			if (repeat < 1) repeat = 1;
 			else if (repeat > 500) repeat = 500;
 			var ctx = GetContext(Services);
-			LoadWorlds(repeat, ctx);
+			LoadWorldsSlow(repeat, ctx);
 			var cms = ctx.Stream;
 			ctx.Worlds.Serialize(cms, repeat);
 			ThreadContext.Response.ContentType = "application/json";
@@ -137,7 +150,7 @@ namespace Revenj.Bench
 			if (repeat < 1) repeat = 1;
 			else if (repeat > 500) repeat = 500;
 			var ctx = GetContext(Services);
-			LoadWorlds(repeat, ctx);
+			LoadWorldsSlow(repeat, ctx);
 			var result = new World[repeat];
 			Array.Copy(ctx.Worlds, result, repeat);
 			for (int i = 0; i < result.Length; i++)
@@ -169,6 +182,6 @@ namespace Revenj.Bench
 			cms.Position = 0;
 			ThreadContext.Response.ContentType = "text/html; charset=UTF-8";
 			return cms;
-	}
+		}
 	}
 }

+ 3 - 3
frameworks/Clojure/pedestal/project.clj

@@ -4,15 +4,15 @@
   :license {:name "Eclipse Public License"
             :url "http://www.eclipse.org/legal/epl-v10.html"}
   :dependencies [[org.clojure/clojure "1.8.0"]
-                 [io.pedestal/pedestal.service "0.4.1"]
-                 [io.pedestal/pedestal.jetty "0.4.1"]
+                 [io.pedestal/pedestal.service "0.5.0"]
+                 [io.pedestal/pedestal.jetty "0.5.0"]
                  [ch.qos.logback/logback-classic "1.1.2" :exclusions [org.slf4j/slf4j-api]]
                  [org.slf4j/jul-to-slf4j "1.7.7"]
                  [org.slf4j/jcl-over-slf4j "1.7.7"]
                  [org.slf4j/log4j-over-slf4j "1.7.7"]
                  [org.clojure/data.json "0.2.6"]
                  [org.clojure/java.jdbc "0.4.2"]
-                 [korma "0.4.0"]
+                 [korma "0.4.2"]
                  [mysql/mysql-connector-java "5.1.38"]
                  [hiccup "1.0.5"]]
   :min-lein-version "2.0.0"

+ 62 - 76
frameworks/Clojure/pedestal/src/pedestal/service.clj

@@ -13,19 +13,26 @@
             [clojure.data.json :as json]
             [clojure.java.jdbc :as jdbc]))
 
-
-(defn json-serialization
-  "Test 1: JSON serialization"
+(defn sanitize-queries-param
+  "Sanitizes the `queries` parameter. Clamps the value between 1 and 500.
+  Invalid (string) values become 1."
   [request]
-  (bootstrap/json-response {:message "Hello, World!"}))
-
-
-;; MySQL connection
-(defdb mysql-db
+  (let [queries (-> request
+                    :params
+                    :queries)
+        n (try (Integer/parseInt queries)
+               (catch Exception e 1))] ; default to 1 on parse failure
+    (cond
+      (< n 1) 1
+      (> n 500) 500
+      :else n)))
+
+;; MySQL database connection
+(defdb db-mysql
   (mysql {
     :classname "com.mysql.jdbc.Driver"
     :subprotocol "mysql"
-    :subname "//127.0.0.1:3306/hello_world"
+    :subname "//127.0.0.1:3306/hello_world?jdbcCompliantTruncation=false&elideSetAutoCommits=true&useLocalSessionState=true&cachePrepStmts=true&cacheCallableStmts=true&alwaysSendSetIsolation=false&prepStmtCacheSize=4096&cacheServerConfiguration=true&prepStmtCacheSqlLimit=2048&zeroDateTimeBehavior=convertToNull&traceProtocol=false&useUnbufferedInput=false&useReadAheadInput=false&maintainTimeStats=false&useServerPrepStmts&cacheRSMetadata=true"
     :user "benchmarkdbuser"
     :password "benchmarkdbpass"
     ;;OPTIONAL KEYS
@@ -37,65 +44,32 @@
 (defentity world
   (pk :id)
   (table :world)
-  (entity-fields :id :randomNumber) ;; Default fields for select
-  (database mysql-db))
+  (entity-fields :id :randomNumber) ; Default fields for select
+  (database db-mysql))
 
-
-(defn random-world
+(defn get-random-world-korma
   "Query a random World record from the database"
   []
   (let [id (inc (rand-int 9999))] ; Num between 1 and 10,000
     (select world
             (where {:id id }))))
 
-
 (defn run-queries
-  "Run query repeatedly -- Always returns an array"
+  "Run query repeatedly, return an array"
   [queries]
-  (flatten (take queries (repeatedly random-world))))
-
-
-(defn single-query-test
-  "Test 2: Single database query"
-  [request]
-  (bootstrap/json-response (first (run-queries 1))))
-
-
-(defn sanitizeQueriesParam
-  "Sanitizes the `queries` parameter. Caps the value between 1 and 500.
-  Invalid (stringy) values become 1"
-  [request]
-  (let [queries (-> request
-                    :params
-                    :queries)]
-    (if-let [n (if (= (re-find #"\A-?\d+" queries) nil)
-                   1
-                   (Integer/parseInt queries))]
-      (cond
-        (< n 1) 1
-        (> n 500) 500
-        :else n))))
+  (flatten ; Make it a list of maps
+    (take queries ; Number of queries to run
+          (repeatedly get-random-world-korma))))
 
-
-(defn multiple-query-test
-  "Test 3: Multiple database queries"
-  [request]
-  (-> request
-      (sanitizeQueriesParam)
-      (run-queries)
-      (bootstrap/json-response)))
-
-
-; Set up entity Fortune and the database representation
+;; Set up entity Fortune and the database representation
 (defentity fortune
   (pk :id)
   (table :fortune)
   (entity-fields :id :message)
-  (database mysql-db))
-
+  (database db-mysql))
 
-(defn get-all-fortunes
-  "Query all Fortune records from the database."
+(defn get-all-fortunes-korma
+  "Query all Fortune records from the database using Korma."
   []
   (select fortune
           (fields :id :message)))
@@ -107,10 +81,9 @@
   []
   (sort-by #(:message %)
     (conj
-      (get-all-fortunes)
+      (get-all-fortunes-korma)
       { :id 0 :message "Additional fortune added at request time." })))
 
-
 (defn fortunes-hiccup
   "Render the given fortunes to simple HTML using Hiccup."
   [fortunes]
@@ -128,6 +101,34 @@
         [:td (escape-html (:message x))]])
      ]]))
 
+(defn update-and-persist
+  "Using Korma: Changes the :randomNumber of a number of world entities.
+  Persists the changes to sql then returns the updated entities"
+  [queries]
+  (let [results (map #(assoc % :randomNumber (inc (rand-int 9999))) (run-queries queries))]
+    (doseq [{:keys [id randomNumber]} results]
+      (update world
+              (set-fields {:randomNumber randomNumber})
+              (where {:id id})))
+    results))
+
+(defn json-serialization
+  "Test 1: JSON serialization"
+  [request]
+  (bootstrap/json-response {:message "Hello, World!"}))
+
+(defn single-query-test
+  "Test 2: Single database query"
+  [request]
+  (bootstrap/json-response (first (run-queries 1))))
+
+(defn multiple-queries-test
+  "Test 3: Multiple database queries"
+  [request]
+  (-> request
+      (sanitize-queries-param)
+      (run-queries)
+      (bootstrap/json-response)))
 
 (defn fortune-test
   "Test 4: Fortunes"
@@ -137,28 +138,13 @@
     (fortunes-hiccup)
     (ring-resp/response)
     (ring-resp/content-type "text/html")
-    (ring-resp/charset "utf-8")))         ;; Apply charset after content type
-
-
-(defn update-and-persist
-  "Changes the :randomNumber of a number of world entities.
-  Persists the changes to sql then returns the updated entities"
-  [request]
-  (let [results (-> request
-                    (sanitizeQueriesParam)
-                    (run-queries))]
-    (for [w results]
-      (update-in w [:randomNumber (inc (rand-int 9999))]
-        (update world
-                (set-fields {:randomNumber (:randomNumber w)})
-                (where {:id [:id w]}))))
-    results))
-
+    (ring-resp/charset "utf-8")))
 
 (defn db-updates
   "Test 5: Database updates"
   [request]
   (-> request
+      (sanitize-queries-param)
       (update-and-persist)
       (bootstrap/json-response)))
 
@@ -168,15 +154,15 @@
   [request]
   (ring-resp/response "Hello, World!"))
 
-
+;; Define route handlers
 (defroutes routes
   [[
+  [  "/plaintext" {:get plaintext}]
   [  "/json"      {:get json-serialization}]
   [  "/db"        {:get single-query-test}]
-  [  "/queries"   {:get multiple-query-test}]
+  [  "/queries"   {:get multiple-queries-test}]
   [  "/fortunes"  {:get fortune-test}]
-  [  "/updates"   {:get db-updates}]
-  [  "/plaintext" {:get plaintext}]]])
+  [  "/updates"   {:get db-updates}]]])
 
 
 (def service

+ 4 - 27
frameworks/Crystal/kemal/server-postgres.cr

@@ -1,7 +1,6 @@
 require "kemal"
 require "pg"
 require "pool/connection"
-require "html_builder"
 
 # Compose Objects (like Hash) to have a to_json method
 require "json/to_json"
@@ -92,40 +91,17 @@ end
 
 # Postgres Test 4: Fortunes
 get "/fortunes" do |env|
+  env.response.content_type = CONTENT::HTML
   data = fortunes
-
   additional_fortune = {
     id:      0,
     message: "Additional fortune added at request time.",
   }
   data.push(additional_fortune)
 
-  data.sort_by! {|fortune| fortune[:message] }
+  data.sort_by! { |fortune| fortune[:message] }
 
-  env.response.content_type = CONTENT::HTML
-  # New builder for each request!
-  HTML::Builder.new.build do
-    doctype
-    html {
-      head {
-        title { html "Fortunes" }
-      }
-      body {
-        table {
-          tr {
-            th { html "id" }
-            th { html "message" }
-          }
-          data.each { |e|
-            tr {
-              td { html e[:id] }
-              td { text e[:message] }
-            }
-          }
-        }
-      }
-    }
-  end
+  render "views/fortunes.ecr"
 end
 
 # Postgres Test 5: Database Updates
@@ -138,4 +114,5 @@ get "/updates" do |env|
   updated.to_json
 end
 
+logging false
 Kemal.run

+ 2 - 25
frameworks/Crystal/kemal/server-redis.cr

@@ -1,6 +1,5 @@
 require "kemal"
 require "redis"
-require "html_builder"
 
 # Compose Objects (like Hash) to have a to_json method
 require "json/to_json"
@@ -96,30 +95,7 @@ get "/fortunes" do |env|
 
   data.sort_by! { |fortune| fortune[:message] }
 
-  env.response.content_type = CONTENT::HTML
-  # New builder for each request!
-  HTML::Builder.new.build do
-    doctype
-    html {
-      head {
-        title { html "Fortunes" }
-      }
-      body {
-        table {
-          tr {
-            th { html "id" }
-            th { html "message" }
-          }
-          data.each { |e|
-            tr {
-              td { html e[:id] }
-              td { text e[:message] }
-            }
-          }
-        }
-      }
-    }
-  end
+  render "views/fortunes.ecr"
 end
 
 # Redis Test 5: Database Updates
@@ -132,4 +108,5 @@ get "/updates" do |env|
   updated.to_json
 end
 
+logging false
 Kemal.run

+ 2 - 2
frameworks/Crystal/kemal/setup-postgres.sh

@@ -2,8 +2,8 @@
 
 fw_depends crystal
 
-crystal deps install
+shards install
 
 crystal build --release server-postgres.cr
 
-KEMAL_ENV=production ./server-postgres &
+./server-postgres &

+ 2 - 2
frameworks/Crystal/kemal/setup-redis.sh

@@ -2,8 +2,8 @@
 
 fw_depends crystal
 
-crystal deps install
+shards install
 
 crystal build --release server-redis.cr
 
-KEMAL_ENV=production ./server-redis &
+./server-redis &

+ 2 - 2
frameworks/Crystal/kemal/shard.lock

@@ -6,7 +6,7 @@ shards:
 
   kemal:
     github: sdogruyol/kemal
-    version: 0.14.1
+    version: 0.16.0
 
   kilt:
     github: jeromegn/kilt
@@ -22,7 +22,7 @@ shards:
 
   radix:
     github: luislavena/radix
-    version: 0.3.0
+    version: 0.3.1
 
   redis:
     github: stefanwille/crystal-redis

+ 3 - 5
frameworks/Crystal/kemal/shard.yml

@@ -10,11 +10,9 @@ dependencies:
     version: "0.2.3"
   kemal:
     github: "sdogruyol/kemal"
-    version: "0.14.1"
+    version: "0.16.0"
   redis:
     github: "stefanwille/crystal-redis"
-    version: "1.6.6"
-  html_builder:
-    github: "crystal-lang/html_builder"
-    version: "0.2.1"
+    version: "~> 1.6.6"
+
 license: "MIT"

+ 20 - 0
frameworks/Crystal/kemal/views/fortunes.ecr

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>Fortunes</title>
+</head>
+<body>
+	<table>
+		<tr>
+		<th>id</th>
+		<th>message</th>
+		</tr>
+		<% data.each do |fortune| %>
+			<tr>
+			<td><%= fortune[:id] %></td>
+			<td><%= HTML.escape(fortune[:message]) %></td>
+			</tr>
+		<% end %>
+	</table>
+</body>
+</html>

+ 1 - 0
frameworks/Go/echo/setup.sh

@@ -4,6 +4,7 @@ fw_depends go
 
 go get github.com/labstack/echo/...
 go get github.com/lib/pq
+go get github.com/valyala/tcplisten
 go install standard fasthttp
 
 fasthttp &

+ 1 - 0
frameworks/Go/echo/setup_prefork.sh

@@ -4,6 +4,7 @@ fw_depends go
 
 go get github.com/labstack/echo/...
 go get github.com/lib/pq
+go get github.com/valyala/tcplisten
 go install standard fasthttp
 
 fasthttp -prefork &

+ 1 - 0
frameworks/Go/echo/setup_std.sh

@@ -4,6 +4,7 @@ fw_depends go
 
 go get github.com/labstack/echo/...
 go get github.com/lib/pq
+go get github.com/valyala/tcplisten
 go install standard fasthttp
 
 standard &

+ 7 - 9
frameworks/Go/falcore/src/framework_benchmarks/falcore.go

@@ -148,14 +148,10 @@ var requiredHeaders = falcore.NewResponseFilter(func(req *falcore.Request, res *
 	res.Header.Set("Date", time.Now().Format(time.RFC1123))
 })
 
-var applicationJson = http.Header{"Content-Type": []string{"application/json"}}
-var textPlain = http.Header{"Content-Type": []string{"text/plain"}}
-var textHtml = http.Header{"Content-Type": []string{"text/html"}}
-
 // Test 1: JSON serialization
 var jsonFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Response {
 	if req.HttpRequest.URL.Path == "/json" {
-		resp, _ := responder.JSONResponse(req.HttpRequest, 200, applicationJson, &Message{helloWorldString})
+		resp, _ := responder.JSONResponse(req.HttpRequest, 200, nil, &Message{helloWorldString})
 		return resp
 	}
 	return nil
@@ -170,7 +166,7 @@ var dbFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Respons
 			log.Fatalf("Error scanning world row: %s", err.Error())
 		}
 
-		resp, _ := responder.JSONResponse(req.HttpRequest, 200, applicationJson, &world)
+		resp, _ := responder.JSONResponse(req.HttpRequest, 200, nil, &world)
 		return resp
 	}
 	return nil
@@ -204,7 +200,7 @@ var queriesFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Re
 				log.Fatalf("Error scanning world row: %s", err.Error())
 			}
 		}
-		resp, _ := responder.JSONResponse(req.HttpRequest, 200, applicationJson, &world)
+		resp, _ := responder.JSONResponse(req.HttpRequest, 200, nil, &world)
 		return resp
 	}
 	return nil
@@ -236,6 +232,7 @@ var fortuneFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Re
 			pipeWriter.Close()
 		}()
 
+		textHtml := http.Header{"Content-Type": []string{"text/html"}}
 		return falcore.SimpleResponse(req.HttpRequest, 200, textHtml, -1, pipeReader)
 	}
 	return nil
@@ -254,7 +251,7 @@ var updateFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Res
 			worldStatement.QueryRow(rand.Intn(worldRowCount)+1).Scan(&world.Id, &world.RandomNumber)
 			world.RandomNumber = uint16(rand.Intn(worldRowCount) + 1)
 			updateStatement.Exec(world.RandomNumber, world.Id)
-			resp, _ := responder.JSONResponse(req.HttpRequest, 200, applicationJson, &world)
+			resp, _ := responder.JSONResponse(req.HttpRequest, 200, nil, &world)
 			return resp
 		} else {
 			world := make([]World, n)
@@ -267,7 +264,7 @@ var updateFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Res
 					log.Fatalf("Error updating world row: %s", err.Error())
 				}
 			}
-			resp, _ := responder.JSONResponse(req.HttpRequest, 200, applicationJson, world)
+			resp, _ := responder.JSONResponse(req.HttpRequest, 200, nil, world)
 			return resp
 		}
 
@@ -278,6 +275,7 @@ var updateFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Res
 // Test 6: Plaintext
 var plaintextFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Response {
 	if req.HttpRequest.URL.Path == "/plaintext" {
+		textPlain := http.Header{"Content-Type": []string{"text/plain"}}
 		return falcore.ByteResponse(req.HttpRequest, 200, textPlain, helloWorldBytes)
 	}
 	return nil

+ 1 - 1
frameworks/Go/fasthttp/setup-mysql-prefork.sh

@@ -5,7 +5,7 @@ sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' src/server-mysql/server.go
 fw_depends go
 
 GOPATH=`pwd` go get -u github.com/go-sql-driver/mysql
-GOPATH=`pwd` go get -u github.com/valyala/fasthttp
+GOPATH=`pwd` go get -u github.com/valyala/fasthttp/...
 GOPATH=`pwd` go get -u github.com/valyala/quicktemplate/qtc
 
 rm -f ./server-mysql

+ 1 - 1
frameworks/Go/fasthttp/setup-mysql.sh

@@ -5,7 +5,7 @@ sed -i 's|tcp(.*:3306)|tcp('"${DBHOST}"':3306)|g' src/server-mysql/server.go
 fw_depends go
 
 GOPATH=`pwd` go get -u github.com/go-sql-driver/mysql
-GOPATH=`pwd` go get -u github.com/valyala/fasthttp
+GOPATH=`pwd` go get -u github.com/valyala/fasthttp/...
 GOPATH=`pwd` go get -u github.com/valyala/quicktemplate/qtc
 
 rm -f ./server-mysql

+ 1 - 1
frameworks/Go/fasthttp/setup-postgresql-prefork.sh

@@ -5,7 +5,7 @@ sed -i 's|localhost|'"${DBHOST}"'|g' src/server-postgresql/server.go
 fw_depends go
 
 GOPATH=`pwd` go get -u github.com/jackc/pgx
-GOPATH=`pwd` go get -u github.com/valyala/fasthttp
+GOPATH=`pwd` go get -u github.com/valyala/fasthttp/...
 GOPATH=`pwd` go get -u github.com/valyala/quicktemplate/qtc
 
 rm -f ./server-postgresql

+ 1 - 1
frameworks/Go/fasthttp/setup-postgresql.sh

@@ -5,7 +5,7 @@ sed -i 's|localhost|'"${DBHOST}"'|g' src/server-postgresql/server.go
 fw_depends go
 
 GOPATH=`pwd` go get -u github.com/jackc/pgx
-GOPATH=`pwd` go get -u github.com/valyala/fasthttp
+GOPATH=`pwd` go get -u github.com/valyala/fasthttp/...
 GOPATH=`pwd` go get -u github.com/valyala/quicktemplate/qtc
 
 rm -f ./server-postgresql

+ 1 - 1
frameworks/Go/fasthttp/source_code

@@ -4,5 +4,5 @@
 ./fasthttp/src/server-mysql/server.go
 ./fasthttp/src/server-postgresql/server.go
 ./fasthttp/src/templates/
-./fasthttp/src/templates/aux.go
+./fasthttp/src/templates/auxiliary.go
 ./fasthttp/src/templates/fortune.qtpl

+ 0 - 0
frameworks/Go/fasthttp/src/templates/aux.go → frameworks/Go/fasthttp/src/templates/auxiliary.go


+ 3 - 1
frameworks/Haskell/yesod/.gitignore

@@ -1 +1,3 @@
-/bench/.stack-work/
+yesod-postgres/.stack-work/
+yesod-mysql-mongo/.stack-work/
+

+ 27 - 4
frameworks/Haskell/yesod/benchmark_config.json

@@ -2,7 +2,7 @@
   "framework": "yesod",
   "tests": [{
     "default": {
-      "setup_file": "setup",
+      "setup_file": "run_yesod_mysql_mongo",
       "json_url": "/json",
       "db_url": "/db",
       "query_url": "/dbs/",
@@ -21,12 +21,12 @@
       "webserver": "Warp",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "yesod",
+      "display_name": "yesod-mysql",
       "notes": "",
       "versus": "wai"
     },
     "mongodb-raw": {
-      "setup_file": "setup",
+      "setup_file": "run_yesod_mysql_mongo",
       "db_url": "/mongo/raw/db",
       "query_url": "/mongo/raw/dbs/",
       "port": 8000,
@@ -41,7 +41,30 @@
       "webserver": "Warp",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "yesod",
+      "display_name": "yesod-mongo",
+      "notes": "",
+      "versus": "wai"
+    },
+    "postgres": {
+      "setup_file": "run_yesod_postgres",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "db_url": "/db",
+      "query_url": "/queries/",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates/",
+      "port": 8000,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "yesod",
+      "language": "Haskell",
+      "orm": "Raw",
+      "platform": "Wai",
+      "webserver": "Warp",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "yesod-postgres",
       "notes": "",
       "versus": "wai"
     }

+ 9 - 0
frameworks/Haskell/yesod/run_yesod_mysql_mongo.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+
+fw_depends stack
+
+cd yesod-mysql-mongo
+
+${IROOT}/stack --allow-different-user build --install-ghc
+
+${IROOT}/stack --allow-different-user exec yesod-mysql-mongo -- ${MAX_THREADS} ${DBHOST} +RTS -A32m -N${MAX_THREADS} &

+ 9 - 0
frameworks/Haskell/yesod/run_yesod_postgres.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+
+fw_depends stack
+
+cd yesod-postgres
+
+${IROOT}/stack --allow-different-user build --install-ghc
+
+${IROOT}/stack --allow-different-user exec yesod-postgres -- ${MAX_THREADS} ${DBHOST} +RTS -A32m -N${MAX_THREADS} &

+ 0 - 9
frameworks/Haskell/yesod/setup.sh

@@ -1,9 +0,0 @@
-#!/bin/bash
-
-fw_depends stack
-
-cd bench
-
-${IROOT}/stack --allow-different-user build --install-ghc
-
-${IROOT}/stack --allow-different-user exec bench -- ${MAX_THREADS} ${DBHOST} +RTS -A32m -N${MAX_THREADS} &

+ 0 - 0
frameworks/Haskell/yesod/bench/.gitignore → frameworks/Haskell/yesod/yesod-mysql-mongo/.gitignore


+ 0 - 0
frameworks/Haskell/yesod/bench/src/yesod.hs → frameworks/Haskell/yesod/yesod-mysql-mongo/src/yesod.hs


+ 0 - 0
frameworks/Haskell/yesod/bench/stack.yaml → frameworks/Haskell/yesod/yesod-mysql-mongo/stack.yaml


+ 2 - 2
frameworks/Haskell/yesod/bench/bench.cabal → frameworks/Haskell/yesod/yesod-mysql-mongo/yesod-mysql-mongo.cabal

@@ -1,9 +1,9 @@
-name:              bench
+name:              yesod-mysql-mongo
 version:           0.0.0
 cabal-version:     >= 1.8
 build-type:        Simple
 
-executable         bench
+executable         yesod-mysql-mongo
     main-is:           yesod.hs
     hs-source-dirs:    src
 

+ 10 - 0
frameworks/Haskell/yesod/yesod-postgres/.gitignore

@@ -0,0 +1,10 @@
+/bench/.stack-work/
+dist*
+static/tmp/
+config/client_session_key.aes
+*.hi
+*.o
+*.sqlite3
+.hsenv*
+yesod-devel/
+

+ 194 - 0
frameworks/Haskell/yesod/yesod-postgres/src/Main.hs

@@ -0,0 +1,194 @@
+{-# LANGUAGE EmptyDataDecls             #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE QuasiQuotes                #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE RecordWildCards            #-}
+{-# LANGUAGE TemplateHaskell            #-}
+{-# LANGUAGE TypeFamilies               #-}
+{-# LANGUAGE ViewPatterns               #-}
+{-# LANGUAGE LambdaCase                 #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Main (main, resourcesApp, Widget, WorldId) where
+import           Control.Concurrent            (runInUnboundThread)
+import           Control.Monad                 (forM)
+import           Control.Monad.Logger          (runNoLoggingT)
+import           Control.Monad.Primitive       (PrimState)
+import           Control.Monad.Trans.Resource  (InternalState)
+import qualified Data.ByteString.Char8 as C8
+import           Data.Pool                     (Pool)
+import           Data.IORef                    (newIORef)
+import           Data.Function                 (on)
+import           Data.List                     (sortBy)
+import           Data.Text                     (Text)
+import           Database.Persist             
+import qualified Database.Persist.Postgresql    as Pg
+import Database.Persist.Sql
+import           Database.Persist.TH           (mkPersist, mpsGeneric,
+                                                persistLowerCase, sqlSettings)
+import qualified Network.Wai.Handler.Warp      as Warp
+import           System.Environment            (getArgs)
+import           System.IO.Unsafe              (unsafePerformIO)
+import qualified System.Random.MWC             as R
+import Text.Blaze.Html
+import           Yesod
+import Data.Maybe (fromJust)
+
+mkPersist sqlSettings { mpsGeneric = True } [persistLowerCase|
+World sql=World
+    randomNumber Int sql=randomnumber
+|]
+
+mkPersist sqlSettings { mpsGeneric = True } [persistLowerCase|
+Fortune sql=Fortune
+    message Text sql=message
+|]
+
+instance ToJSON (Entity World) where
+  toJSON (Entity wId wRow) = object [
+    "id" .= wId
+    ,"randomNumber" .= (worldRandomNumber wRow)
+    ]
+
+instance ToMarkup FortuneId where
+  toMarkup = toMarkup . fromSqlKey
+
+data App = App
+    { appGen      :: !(R.Gen (PrimState IO))
+    , appDbPool   :: !(Pool Pg.SqlBackend)
+    }
+
+
+mkYesod "App" [parseRoutes|
+/json               JsonR     GET
+/plaintext          PlaintextR   GET
+/db                 DbR          GET
+
+/queries/#Int       QueriesR     GET
+!/queries/#Text      DefaultQueriesR     GET
+
+/fortunes           FortunesR    GET
+
+/updates/#Int       UpdatesR     GET
+!/updates/#Text     DefaultUpdatesR GET
+|]
+
+fakeInternalState :: InternalState
+fakeInternalState = unsafePerformIO $ newIORef $ error "fakeInternalState forced"
+{-# NOINLINE fakeInternalState #-}
+
+instance Yesod App where
+    makeSessionBackend _ = return Nothing
+    {-# INLINE makeSessionBackend #-}
+    shouldLog _ _ _ = False
+    {-# INLINE shouldLog #-}
+    yesodMiddleware = id
+    {-# INLINE yesodMiddleware #-}
+    cleanPath _ = Right
+    {-# INLINE cleanPath #-}
+    yesodWithInternalState _ _ = ($ fakeInternalState)
+    {-# INLINE yesodWithInternalState #-}
+    maximumContentLength _ _ = Nothing
+    {-# INLINE maximumContentLength #-}
+
+getJsonR :: Handler Value
+getJsonR = returnJson $ object ["message" .= ("Hello, World!" :: Text)]
+
+runPg dbAction = do
+  app <- getYesod
+  runSqlPool dbAction (appDbPool app)
+
+getRandomRow = do
+  app <- getYesod
+  randomNumber <- liftIO $ ((R.uniformR (1, 10000) (appGen app)) :: IO Int)
+  let wId = (toSqlKey $ fromIntegral randomNumber) :: WorldId
+  get wId >>= \case
+    Nothing -> return Nothing
+    Just x -> return $ Just (Entity wId x)
+
+getDbR :: Handler Value
+getDbR = do
+  (runPg getRandomRow) >>= \case
+    -- TODO: Throw appropriate HTTP response
+    Nothing -> error "This shouldn't be happening"
+    Just worldE -> returnJson worldE
+
+getQueriesR :: Int -> Handler Value
+getQueriesR cnt = do
+  result <- (runPg $ forM [1..sanitizedCnt] (\_ -> fmap fromJust getRandomRow))
+  returnJson result
+  where
+    sanitizedCnt
+      | cnt<1 = 1
+      | cnt>500 = 500
+      | otherwise = cnt
+
+getDefaultQueriesR :: Text -> Handler Value
+getDefaultQueriesR _ = getQueriesR 1
+
+getFortunesR :: Handler Html
+getFortunesR = do
+  fortunesFromDb <- runPg $ selectList [] []
+  let fortunes = sortBy (compare `on` fortuneMessage . entityVal) $ (Entity (toSqlKey 0) Fortune{fortuneMessage="Additional fortune added at request time."}):fortunesFromDb
+  defaultLayout $ do
+      setTitle "Fortunes"
+      [whamlet|
+              <table>
+                <tr>
+                  <th>id
+                  <th>message
+                $forall fortune <- fortunes
+                  <tr>
+                    <td>#{entityKey fortune}
+                    <td>#{fortuneMessage $ entityVal fortune}
+                    |]
+
+getUpdatesR :: Int -> Handler Value
+getUpdatesR cnt = do
+  worldRows <- runPg $ forM [1..sanitizedCount] (\_ -> fmap fromJust getRandomRow)
+  app <- getYesod
+  updatedWorldRows <- runPg $ mapM (replaceWorldRow app) worldRows
+  returnJson updatedWorldRows
+  where
+    sanitizedCount
+      | cnt<1 = 1
+      | cnt>500 = 500
+      | otherwise = cnt
+
+    replaceWorldRow app (Entity wId wRow) = do
+      randomNumber <- liftIO $ ((R.uniformR (1, 10000) (appGen app)) :: IO Int)
+      -- TODO: Should I be using replace, or update, or updateGet -- which is
+      -- idiomatic Yesod code for this operation?
+      let newRow = wRow{worldRandomNumber=randomNumber}
+      replace wId newRow
+      return (Entity wId newRow)
+
+
+getDefaultUpdatesR :: Text -> Handler Value
+getDefaultUpdatesR _ = getUpdatesR 1
+
+getPlaintextR :: Handler Text
+getPlaintextR = return "Hello, World!"
+
+main :: IO ()
+main = R.withSystemRandom $ \gen -> do
+    [cores, host] <- getArgs
+    let connString = ("host=" ++ host ++ " port=5432 user=benchmarkdbuser password=benchmarkdbpass dbname=hello_world") 
+    dbPool <- runNoLoggingT $ Pg.createPostgresqlPool (C8.pack connString) 256
+    app <- toWaiAppPlain App
+        { appGen = gen
+        , appDbPool = dbPool
+        }
+
+    runInUnboundThread $ Warp.runSettings
+        ( Warp.setPort 8000
+        $ Warp.setHost "*"
+        $ Warp.setOnException (\_ _ -> return ())
+          Warp.defaultSettings
+        ) app
+

+ 5 - 0
frameworks/Haskell/yesod/yesod-postgres/stack.yaml

@@ -0,0 +1,5 @@
+flags: {}
+packages:
+- '.'
+extra-deps: []
+resolver: lts-6.3

+ 43 - 0
frameworks/Haskell/yesod/yesod-postgres/yesod-postgres.cabal

@@ -0,0 +1,43 @@
+name:              yesod-postgres
+version:           0.0.0
+cabal-version:     >= 1.8
+build-type:        Simple
+
+executable         yesod-postgres
+    main-is:           Main.hs
+    hs-source-dirs:    src
+
+    ghc-options:       -Wall -threaded -O2 -rtsopts
+
+    extensions: TemplateHaskell
+                QuasiQuotes
+                OverloadedStrings
+                MultiParamTypeClasses
+                TypeFamilies
+                GADTs
+                EmptyDataDecls
+                CPP
+
+    build-depends: base
+                 , yesod
+                 , text
+                 , persistent
+                 , persistent-postgresql
+                 , persistent-template
+                 , warp
+                 , auto-update
+                 , primitive
+                 , mwc-random
+                 , resource-pool
+                 , network
+                 , monad-logger
+                 , mtl
+                 , wai
+                 , http-types
+                 , aeson
+                 , blaze-builder
+                 , blaze-html
+                 , bytestring
+                 , resource-pool
+                 , resourcet
+                 , shakespeare

+ 4 - 4
frameworks/Java/curacao/pom.xml

@@ -20,12 +20,12 @@
         <dependency>
             <groupId>curacao</groupId>
             <artifactId>curacao</artifactId>
-            <version>4.0.4</version>
+            <version>4.2.0</version>
         </dependency>
         <dependency>
             <groupId>curacao</groupId>
             <artifactId>curacao-gson</artifactId>
-            <version>4.0.4</version>
+            <version>4.2.0</version>
         </dependency>
 
         <!-- Servlet -->
@@ -40,12 +40,12 @@
         <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-core</artifactId>
-            <version>1.1.5</version>
+            <version>1.1.7</version>
         </dependency>
         <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
-            <version>1.1.5</version>
+            <version>1.1.7</version>
         </dependency>
 
     </dependencies>

+ 1 - 1
frameworks/Java/curacao/src/main/resources/application.conf

@@ -1,6 +1,6 @@
 curacao {
   boot-package = "benchmark"
-  async-context-timeout = 0
+  async-context-timeout = 30s
   thread-pool {
     name-format = "curacao-%d"
   }

+ 2 - 0
frameworks/Java/jooby/setup.sh

@@ -2,6 +2,8 @@
 
 fw_depends java maven
 
+sed -i 's|localhost|'"${DBHOST}"'|g' conf/application.conf
+
 mvn clean package
 
 cd target

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません