Bladeren bron

Merge remote-tracking branch 'upstream/round-14'

A. Shawn Bandy 8 jaren geleden
bovenliggende
commit
91b2d985e8
100 gewijzigde bestanden met toevoegingen van 1909 en 546 verwijderingen
  1. 1 1
      .github/PULL_REQUEST_TEMPLATE.md
  2. 2 0
      .travis.yml
  3. 1 2
      frameworks/C++/cutelyst/setup_thread.sh
  4. 186 2
      frameworks/C++/ffead-cpp/benchmark_config.json
  5. 16 0
      frameworks/C++/ffead-cpp/setup-apache2-mysql.sh
  6. 16 0
      frameworks/C++/ffead-cpp/setup-apache2-postgresql.sh
  7. 16 0
      frameworks/C++/ffead-cpp/setup-apache2.sh
  8. 16 0
      frameworks/C++/ffead-cpp/setup-mysql.sh
  9. 16 0
      frameworks/C++/ffead-cpp/setup-nginx-mysql.sh
  10. 15 0
      frameworks/C++/ffead-cpp/setup-nginx-postgresql.sh
  11. 15 0
      frameworks/C++/ffead-cpp/setup-nginx.sh
  12. 16 0
      frameworks/C++/ffead-cpp/setup-postgresql.sh
  13. 4 0
      frameworks/C++/ffead-cpp/setup.sh
  14. 1 1
      frameworks/C/h2o/setup.sh
  15. 4 5
      frameworks/C/h2o/src/database.c
  16. 11 14
      frameworks/C/h2o/src/event_loop.c
  17. 2 2
      frameworks/C/h2o/src/event_loop.h
  18. 37 28
      frameworks/C/h2o/src/fortune.c
  19. 1 1
      frameworks/C/h2o/src/fortune.h
  20. 25 10
      frameworks/C/h2o/src/main.c
  21. 18 17
      frameworks/C/h2o/src/request_handler.c
  22. 2 1
      frameworks/C/h2o/src/request_handler.h
  23. 49 31
      frameworks/C/h2o/src/thread.c
  24. 20 12
      frameworks/C/h2o/src/thread.h
  25. 3 3
      frameworks/C/h2o/src/tls.c
  26. 1 1
      frameworks/C/h2o/src/tls.h
  27. 6 3
      frameworks/C/h2o/src/utility.h
  28. 72 66
      frameworks/C/h2o/src/world.c
  29. 2 7
      frameworks/Elixir/phoenix/web/controllers/page_controller.ex
  30. 8 0
      frameworks/Java/rapidoid/README.md
  31. 58 21
      frameworks/Java/rapidoid/benchmark_config.json
  32. 8 0
      frameworks/Java/rapidoid/setup-high-level.sh
  33. 6 0
      frameworks/Java/rapidoid/source_code
  34. 48 0
      frameworks/Java/rapidoid/src/main/java/common/Fortune.java
  35. 12 0
      frameworks/Java/rapidoid/src/main/java/common/Helper.java
  36. 36 0
      frameworks/Java/rapidoid/src/main/java/highlevel/FortunesHandler.java
  37. 44 0
      frameworks/Java/rapidoid/src/main/java/highlevel/Main.java
  38. 8 0
      frameworks/Java/rapidoid/src/main/resources/fortunes.html
  39. 0 13
      frameworks/Java/servlet/.classpath
  40. 0 14
      frameworks/Java/servlet/.project
  41. 0 5
      frameworks/Java/servlet/.settings/org.eclipse.jdt.core.prefs
  42. 2 1
      frameworks/Java/servlet/benchmark_config.json
  43. 75 0
      frameworks/Java/servlet/src/main/java/hello/PostgreFortunesServlet.java
  44. 6 0
      frameworks/Java/servlet/src/main/webapp/WEB-INF/web.xml
  45. 2 0
      frameworks/Java/spark/benchmark_config.json
  46. 10 5
      frameworks/Java/spark/pom.xml
  47. 16 0
      frameworks/Java/spark/src/main/java/hello/domain/Fortune.java
  48. 9 7
      frameworks/Java/spark/src/main/java/hello/web/HibernateUtil.java
  49. 0 30
      frameworks/Java/spark/src/main/java/hello/web/JsonTransformer.java
  50. 110 60
      frameworks/Java/spark/src/main/java/hello/web/SparkApplication.java
  51. 2 2
      frameworks/Java/spring/benchmark_config.json
  52. 13 1
      frameworks/JavaScript/express/app.js
  53. 2 1
      frameworks/JavaScript/express/benchmark_config.json
  54. 1 0
      frameworks/JavaScript/sailsjs/benchmark_config.json
  55. 1 0
      frameworks/Lua/octopus/.gitignore
  56. 35 0
      frameworks/Lua/octopus/README.md
  57. 16 0
      frameworks/Lua/octopus/app/config.lua
  58. 34 0
      frameworks/Lua/octopus/app/src/FortunesController.lua
  59. 4 0
      frameworks/Lua/octopus/app/src/JsonSerializationController.lua
  60. 36 0
      frameworks/Lua/octopus/app/src/MultipleQueriesController.lua
  61. 2 0
      frameworks/Lua/octopus/app/src/PlaintextController.lua
  62. 24 0
      frameworks/Lua/octopus/app/src/SingleQueryController.lua
  63. 39 0
      frameworks/Lua/octopus/app/src/UpdateController.lua
  64. 12 0
      frameworks/Lua/octopus/app/src/types.lua
  65. 28 0
      frameworks/Lua/octopus/benchmark_config.json
  66. 39 0
      frameworks/Lua/octopus/config.lua
  67. 13 0
      frameworks/Lua/octopus/setup.sh
  68. 50 43
      frameworks/Lua/openresty/app.lua
  69. 23 6
      frameworks/Lua/openresty/nginx.conf
  70. 1 1
      frameworks/PHP/cygnite/deploy/nginx.conf
  71. 24 0
      frameworks/PHP/laravel/app/routes.php
  72. 4 73
      frameworks/PHP/lithium/app/config/routes.php
  73. 43 1
      frameworks/PHP/lithium/app/controllers/BenchController.php
  74. 12 0
      frameworks/PHP/lithium/app/models/Fortune.php
  75. 0 0
      frameworks/PHP/lithium/app/resources/tmp/cache/templates/blank
  76. 12 0
      frameworks/PHP/lithium/app/views/bench/fortunes.html.php
  77. 3 0
      frameworks/PHP/lithium/benchmark_config.json
  78. 9 0
      frameworks/PHP/slim/benchmark_config.json
  79. 2 1
      frameworks/PHP/slim/composer.json
  80. 66 13
      frameworks/PHP/slim/index.php
  81. 18 0
      frameworks/PHP/slim/templates/fortunes.php
  82. 13 1
      frameworks/PHP/symfony2-stripped/app/config/routing.yml
  83. 3 0
      frameworks/PHP/symfony2-stripped/benchmark_config.json
  84. 46 1
      frameworks/PHP/symfony2-stripped/src/Skamander/BenchmarkBundle/Controller/BenchController.php
  85. 13 1
      frameworks/PHP/symfony2/app/config/routing.yml
  86. 5 0
      frameworks/PHP/symfony2/benchmark_config.json
  87. 46 1
      frameworks/PHP/symfony2/src/Skamander/BenchmarkBundle/Controller/BenchController.php
  88. 37 0
      frameworks/PHP/yaf/app/modules/Bench/controllers/Raw.php
  89. 2 0
      frameworks/PHP/yaf/benchmark_config.json
  90. 3 1
      frameworks/PHP/yii2/benchmark_config.json
  91. 2 0
      frameworks/PHP/zend/benchmark_config.json
  92. 37 1
      frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/Controller/BenchController.php
  93. 0 23
      frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/Controller/JsonController.php
  94. 26 9
      frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/Module.php
  95. 3 3
      frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/ServiceFactory/BenchControllerServiceFactory.php
  96. 15 0
      frameworks/PHP/zend1/application/controllers/FortunesController.php
  97. 12 0
      frameworks/PHP/zend1/application/controllers/PlaintextController.php
  98. 26 0
      frameworks/PHP/zend1/application/controllers/UpdatesController.php
  99. 10 0
      frameworks/PHP/zend1/application/models/Fortune.php
  100. 10 0
      frameworks/PHP/zend1/application/models/FortuneRow.php

+ 1 - 1
.github/PULL_REQUEST_TEMPLATE.md

@@ -1,7 +1,7 @@
 <!--
 ....................................
 
-MAKE SURE YOU ARE OPENING A PULL 
+MAKE SURE YOU ARE OPENING A PULL
 REQUEST AGAINST THE CORRECT BRANCH
 
 ....................................

+ 2 - 0
.travis.yml

@@ -116,6 +116,7 @@ env:
     - "TESTDIR=JavaScript/sailsjs"
     - "TESTDIR=Kotlin/hexagon"
     - "TESTDIR=Lua/lapis"
+    - "TESTDIR=Lua/octopus"
     - "TESTDIR=Lua/openresty"
     - "TESTDIR=Nim/jester"
     - "TESTDIR=Nim/nawak"
@@ -164,6 +165,7 @@ env:
     - "TESTDIR=Python/turbogears"
     - "TESTDIR=Python/uwsgi"
     - "TESTDIR=Python/web2py"
+    - "TESTDIR=Python/weppy"
     - "TESTDIR=Python/wheezyweb"
     - "TESTDIR=Python/wsgi"
     - "TESTDIR=Racket/racket-ws"

+ 1 - 2
frameworks/C++/cutelyst/setup_thread.sh

@@ -17,6 +17,5 @@ cmake $TROOT -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$IROOT
 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 --master --http-socket :8080 -a ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -t $MAX_THREADS &
+${IROOT}/bin/cutelyst-wsgi --ini ${TROOT}/config/config.ini --http-socket :8080 -a ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -t $MAX_THREADS &

+ 186 - 2
frameworks/C++/ffead-cpp/benchmark_config.json

@@ -21,9 +21,193 @@
       "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "ffead-cpp",
+      "display_name": "ffead-cpp-mongo",
+      "notes": "",
+      "versus": ""
+    },
+	"mysql": {
+      "setup_file": "setup-mysql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mysql",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-mysql",
+      "notes": "",
+      "versus": ""
+    },
+	"postgresql": {
+      "setup_file": "setup-postgresql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "postgres",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-postgresql",
+      "notes": "",
+      "versus": ""
+    },
+    "apache2-mongo": {
+      "setup_file": "setup-apache2",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mongodb",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-apache2",
+      "webserver": "apache2",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-apache2-mongo",
+      "notes": "",
+      "versus": ""
+    },
+	"apache2-mysql": {
+      "setup_file": "setup-apache2-mysql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mysql",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-apache2",
+      "webserver": "apache2",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-apache2-mysql",
+      "notes": "",
+      "versus": ""
+    },
+	"apache2-postgresql": {
+      "setup_file": "setup-apache2-postgresql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "postgres",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-apache2",
+      "webserver": "apache2",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-apache2-postgresql",
+      "notes": "",
+      "versus": ""
+    },
+    "nginx-mongo": {
+      "setup_file": "setup-nginx",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mongodb",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-nginx",
+      "webserver": "nginx",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-nginx-mongo",
+      "notes": "",
+      "versus": ""
+    },
+	"nginx-mysql": {
+      "setup_file": "setup-nginx-mysql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "mysql",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-nginx",
+      "webserver": "nginx",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-nginx-mysql",
+      "notes": "",
+      "versus": ""
+    },
+	"nginx-postgresql": {
+      "setup_file": "setup-nginx-postgresql",
+      "json_url": "/te-benchmark/json",
+      "plaintext_url": "/te-benchmark/plaintext",
+      "db_url": "/te-benchmark/db",
+      "query_url": "/te-benchmark/queries?queries=",
+      "fortune_url": "/te-benchmark/fortunes",
+      "update_url": "/te-benchmark/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "postgres",
+      "framework": "ffead-cpp",
+      "language": "C++",
+      "orm": "Full",
+      "platform": "ffead-cpp-nginx",
+      "webserver": "nginx",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "ffead-cpp-nginx-postgresql",
       "notes": "",
       "versus": "ffead-cpp"
     }
   }]
-}
+}

+ 16 - 0
frameworks/C++/ffead-cpp/setup-apache2-mysql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends apache
+fw_depends ffead-cpp-apache
+
+export FFEAD_CPP_PATH=/var/www/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+sudo rm -f $FFEAD_CPP_PATH/*.cntrl
+sudo rm -f $FFEAD_CPP_PATH/tmp/*.sess
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmysql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+sudo rm -rf $FFEAD_CPP_PATH/lib
+sudo cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+sudo /etc/init.d/apache2 restart > ffead.log 2>&1

+ 16 - 0
frameworks/C++/ffead-cpp/setup-apache2-postgresql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends apache
+fw_depends ffead-cpp-apache
+
+export FFEAD_CPP_PATH=/var/www/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+sudo rm -f $FFEAD_CPP_PATH/*.cntrl
+sudo rm -f $FFEAD_CPP_PATH/tmp/*.sess
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormpostgresql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+sudo rm -rf $FFEAD_CPP_PATH/lib
+sudo cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+sudo /etc/init.d/apache2 restart > ffead.log 2>&1

+ 16 - 0
frameworks/C++/ffead-cpp/setup-apache2.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends apache
+fw_depends ffead-cpp-apache
+
+export FFEAD_CPP_PATH=/var/www/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+sudo rm -f $FFEAD_CPP_PATH/*.cntrl
+sudo rm -f $FFEAD_CPP_PATH/tmp/*.sess
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmongo.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+sudo rm -rf $FFEAD_CPP_PATH/lib
+sudo cp -Rf $FFEAD_CPP_PATH/libmongo $FFEAD_CPP_PATH/lib
+sudo cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldmongo.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+sudo /etc/init.d/apache2 restart > ffead.log 2>&1

+ 16 - 0
frameworks/C++/ffead-cpp/setup-mysql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends ffead-cpp
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmysql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$TROOT/ffead-cpp-2.0/CHS $FFEAD_CPP_PATH > ffead.log 2>&1
+

+ 16 - 0
frameworks/C++/ffead-cpp/setup-nginx-mysql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends ffead-cpp-nginx
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmysql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$IROOT/nginxfc/sbin/nginx > ffead.log 2>&1
+

+ 15 - 0
frameworks/C++/ffead-cpp/setup-nginx-postgresql.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+fw_depends ffead-cpp-nginx
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormpostgresql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$IROOT/nginxfc/sbin/nginx > ffead.log 2>&1

+ 15 - 0
frameworks/C++/ffead-cpp/setup-nginx.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+fw_depends ffead-cpp-nginx
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmongo.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libmongo $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldmongo.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$IROOT/nginxfc/sbin/nginx > ffead.log 2>&1

+ 16 - 0
frameworks/C++/ffead-cpp/setup-postgresql.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+fw_depends ffead-cpp
+
+export FFEAD_CPP_PATH=$TROOT/ffead-cpp-2.0
+export LD_LIBRARY_PATH=$IROOT:$FFEAD_CPP_PATH/lib:$LD_LIBRARY_PATH
+echo $FFEAD_CPP_PATH
+echo $LD_LIBRARY_PATH
+rm -f $FFEAD_CPP_PATH/*.cntrl
+rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormpostgresql.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libsql $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldsql.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
+$TROOT/ffead-cpp-2.0/CHS $FFEAD_CPP_PATH > ffead.log 2>&1
+

+ 4 - 0
frameworks/C++/ffead-cpp/setup.sh

@@ -8,5 +8,9 @@ echo $FFEAD_CPP_PATH
 echo $LD_LIBRARY_PATH
 rm -f $FFEAD_CPP_PATH/*.cntrl
 rm -f $FFEAD_CPP_PATH/tmp/*.sess
+cp $FFEAD_CPP_PATH/web/te-benchmark/config/sdormmongo.xml $FFEAD_CPP_PATH/web/te-benchmark/config/sdorm.xml
+rm -rf $FFEAD_CPP_PATH/lib
+cp -Rf $FFEAD_CPP_PATH/libmongo $FFEAD_CPP_PATH/lib
+cp $FFEAD_CPP_PATH/web/te-benchmark/sql-src/TeBkWorldmongo.h $FFEAD_CPP_PATH/web/te-benchmark/include/TeBkWorld.h
 $TROOT/ffead-cpp-2.0/CHS $FFEAD_CPP_PATH > ffead.log 2>&1
 

+ 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" -m8 "$3" "$4" \
+	"$1/h2o_app" -a1 -f "$2/template/fortunes.mustache" -m5 "$3" "$4" \
 		-d "host=$DBHOST dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass" &
 }
 

+ 4 - 5
frameworks/C/h2o/src/database.c

@@ -353,8 +353,7 @@ static void start_database_connect(thread_context_t *ctx, db_conn_t *db_conn)
 			goto error;
 		}
 
-		const char * const conninfo =
-			ctx->global_data->config->db_host ? ctx->global_data->config->db_host : "";
+		const char * const conninfo = ctx->config->db_host ? ctx->config->db_host : "";
 
 		db_conn->conn = PQconnectStart(conninfo);
 
@@ -441,7 +440,7 @@ static void stop_database_write_polling(db_conn_t *db_conn)
 
 void connect_to_database(thread_context_t *ctx)
 {
-	for (size_t i = ctx->db_state.db_conn_num; i < ctx->global_data->config->max_db_conn_num; i++)
+	for (size_t i = ctx->db_state.db_conn_num; i < ctx->config->max_db_conn_num; i++)
 		start_database_connect(ctx, NULL);
 }
 
@@ -475,13 +474,13 @@ int execute_query(thread_context_t *ctx, db_query_param_t *param)
 		db_conn->param = param;
 		do_execute_query(db_conn);
 	}
-	else if (ctx->db_state.query_num < ctx->global_data->config->max_query_num) {
+	else if (ctx->db_state.query_num < ctx->config->max_query_num) {
 		param->l.next = NULL;
 		*ctx->db_state.queries.tail = &param->l;
 		ctx->db_state.queries.tail = &param->l.next;
 		ctx->db_state.query_num++;
 
-		if (ctx->db_state.db_conn_num < ctx->global_data->config->max_db_conn_num)
+		if (ctx->db_state.db_conn_num < ctx->config->max_db_conn_num)
 			start_database_connect(ctx, NULL);
 	}
 	else

+ 11 - 14
frameworks/C/h2o/src/event_loop.c

@@ -24,7 +24,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/epoll.h>
-#include <sys/syscall.h>
 
 #include "error.h"
 #include "event_loop.h"
@@ -57,7 +56,7 @@ static void accept_connection(h2o_socket_t *listener, const char *err)
 				sock->on_close.cb = on_close_connection;
 				sock->on_close.data = &ctx->event_loop.conn_num;
 				h2o_accept(&ctx->event_loop.h2o_accept_ctx, sock);
-			} while (++accepted < ctx->global_data->config->max_accept);
+			} while (++accepted < ctx->config->max_accept);
 		}
 	}
 }
@@ -96,11 +95,11 @@ static void process_messages(h2o_multithread_receiver_t *receiver, h2o_linklist_
 {
 	IGNORE_FUNCTION_PARAMETER(messages);
 
-	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
-	                                                      event_loop.h2o_receiver,
-	                                                      receiver);
+	global_thread_data_t * const global_thread_data = H2O_STRUCT_FROM_MEMBER(global_thread_data_t,
+	                                                                         h2o_receiver,
+	                                                                         receiver);
 
-	h2o_socket_read_stop(ctx->event_loop.h2o_socket);
+	h2o_socket_read_stop(global_thread_data->ctx->event_loop.h2o_socket);
 }
 
 static void shutdown_server(h2o_socket_t *listener, const char *err)
@@ -113,23 +112,20 @@ static void shutdown_server(h2o_socket_t *listener, const char *err)
 		ctx->global_data->shutdown = true;
 		h2o_socket_read_stop(ctx->event_loop.h2o_socket);
 
-		for (size_t i = 1; i < ctx->global_data->config->thread_num; i++)
-			h2o_multithread_send_message(&ctx[i].event_loop.h2o_receiver, NULL);
+		for (size_t i = 1; i < ctx->config->thread_num; i++)
+			h2o_multithread_send_message(&ctx->global_thread_data[i].h2o_receiver, NULL);
 	}
 }
 
 void event_loop(thread_context_t *ctx)
 {
-	ctx->tid = syscall(SYS_gettid);
-	ctx->random_seed = ctx->tid;
-
 	while (!ctx->global_data->shutdown || ctx->event_loop.conn_num)
 		h2o_evloop_run(ctx->event_loop.h2o_ctx.loop);
 }
 
-void free_event_loop(event_loop_t *event_loop)
+void free_event_loop(event_loop_t *event_loop, h2o_multithread_receiver_t *h2o_receiver)
 {
-	h2o_multithread_unregister_receiver(event_loop->h2o_ctx.queue, &event_loop->h2o_receiver);
+	h2o_multithread_unregister_receiver(event_loop->h2o_ctx.queue, h2o_receiver);
 	h2o_socket_close(event_loop->h2o_socket);
 	h2o_socket_close(event_loop->epoll_socket);
 	h2o_context_dispose(&event_loop->h2o_ctx);
@@ -137,6 +133,7 @@ void free_event_loop(event_loop_t *event_loop)
 
 void initialize_event_loop(bool is_main_thread,
                            global_data_t *global_data,
+                           h2o_multithread_receiver_t *h2o_receiver,
                            event_loop_t *loop)
 {
 	memset(loop, 0, sizeof(*loop));
@@ -165,7 +162,7 @@ void initialize_event_loop(bool is_main_thread,
 	loop->h2o_socket->data = loop;
 	h2o_socket_read_start(loop->h2o_socket, accept_connection);
 	h2o_multithread_register_receiver(loop->h2o_ctx.queue,
-	                                  &loop->h2o_receiver,
+	                                  h2o_receiver,
 	                                  process_messages);
 	// libh2o's event loop does not support write polling unless it
 	// controls sending the data as well, so do read polling on the

+ 2 - 2
frameworks/C/h2o/src/event_loop.h

@@ -35,13 +35,13 @@ typedef struct {
 	int epoll_fd;
 	h2o_accept_ctx_t h2o_accept_ctx;
 	h2o_context_t h2o_ctx;
-	h2o_multithread_receiver_t h2o_receiver;
 } event_loop_t;
 
 void event_loop(thread_context_t *ctx);
-void free_event_loop(event_loop_t *event_loop);
+void free_event_loop(event_loop_t *event_loop, h2o_multithread_receiver_t *h2o_receiver);
 void initialize_event_loop(bool is_main_thread,
                            global_data_t *global_data,
+                           h2o_multithread_receiver_t *h2o_receiver,
                            event_loop_t *loop);
 int start_write_polling(int fd,
                         void (**on_write_ready)(void *),

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

@@ -59,6 +59,7 @@ static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
                            const char *buffer,
                            uintmax_t buffer_size);
+static void cleanup_fortunes(void *data);
 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);
@@ -109,6 +110,22 @@ static uintmax_t add_iovec(mustache_api_t *api,
 	return ret;
 }
 
+static void cleanup_fortunes(void *data)
+{
+	fortune_ctx_t * const fortune_ctx = data;
+	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);
@@ -142,12 +159,7 @@ static list_t *get_sorted_sublist(list_t *head)
 	if (head) {
 		head = head->next;
 
-		while (head && compare_fortunes(tail, head) < 0) {
-			tail = head;
-			head = head->next;
-		}
-
-		while (head && !compare_fortunes(tail, head)) {
+		while (head && compare_fortunes(tail, head) <= 0) {
 			tail = head;
 			head = head->next;
 		}
@@ -209,40 +221,33 @@ 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,
-			                                               fortune_size);
+			                                               sizeof(*fortune));
 
 			if (fortune) {
-				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;
+				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));
 				fortune->l.next = fortune_ctx->result;
 				fortune_ctx->result = &fortune->l;
 				fortune_ctx->num_result++;
+
+				if (!i)
+					fortune->data = result;
 			}
 			else {
 				send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
 				ret = DONE;
+
+				if (!i)
+					PQclear(result);
+
 				break;
 			}
 		}
-
-		PQclear(result);
 	}
 	else if (result) {
 		PQclear(result);
@@ -365,7 +370,9 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
 	                                                      event_loop.h2o_ctx,
 	                                                      req->conn->ctx);
-	fortune_ctx_t * const fortune_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune_ctx));
+	fortune_ctx_t * const fortune_ctx = h2o_mem_alloc_shared(&req->pool,
+	                                                         sizeof(*fortune_ctx),
+	                                                         cleanup_fortunes);
 
 	if (fortune_ctx) {
 		fortune_t * const fortune = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune));
@@ -390,6 +397,8 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 			if (execute_query(ctx, &fortune_ctx->param))
 				send_service_unavailable_error(DB_REQ_ERROR, req);
 		}
+		else
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 	}
 	else
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);

+ 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);

+ 25 - 10
frameworks/C/h2o/src/main.c

@@ -24,6 +24,7 @@
 #include <netdb.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -44,6 +45,7 @@
 #include "tls.h"
 #include "utility.h"
 
+#define DEFAULT_CACHE_LINE_SIZE 128
 #define DEFAULT_TCP_FASTOPEN_QUEUE_LEN 4096
 #define USAGE_MESSAGE \
 	"Usage:\n%s [-a <max connections accepted simultaneously>] [-b <bind address>] " \
@@ -63,8 +65,12 @@ static void setup_process(void);
 
 static void free_global_data(global_data_t *global_data)
 {
-	if (global_data->ctx)
-		free_thread_contexts(global_data);
+	if (global_data->global_thread_data) {
+		for (size_t i = 1; i < global_data->global_thread_data->config->thread_num; i++)
+			CHECK_ERROR(pthread_join, global_data->global_thread_data[i].thread, NULL);
+
+		free(global_data->global_thread_data);
+	}
 
 	if (global_data->file_logger)
 		global_data->file_logger->dispose(global_data->file_logger);
@@ -177,9 +183,7 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 	sigset_t signals;
 
 	memset(global_data, 0, sizeof(*global_data));
-	global_data->config = config;
 	global_data->memory_alignment = get_maximum_cache_line_size();
-	assert(global_data->memory_alignment <= DEFAULT_CACHE_LINE_SIZE);
 	CHECK_ERRNO(sigemptyset, &signals);
 #ifdef NDEBUG
 	CHECK_ERRNO(sigaddset, &signals, SIGINT);
@@ -194,7 +198,7 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 		goto error;
 
 	if (config->cert && config->key)
-		initialize_openssl(global_data);
+		initialize_openssl(config, global_data);
 
 	const h2o_iovec_t host = h2o_iovec_init(H2O_STRLIT("default"));
 	h2o_hostconf_t * const hostconf = h2o_config_register_host(&global_data->h2o_config,
@@ -220,10 +224,14 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 			global_data->file_logger = h2o_access_log_register(pathconf, log_handle);
 	}
 
-	global_data->ctx = initialize_thread_contexts(global_data);
+	global_data->global_thread_data = initialize_global_thread_data(config, global_data);
 
-	if (global_data->ctx)
+	if (global_data->global_thread_data) {
+		printf("Number of processors: %zu\nMaximum cache line size: %zu\n",
+		       h2o_numproc(),
+		       global_data->memory_alignment);
 		return EXIT_SUCCESS;
+	}
 
 error:
 	free_global_data(global_data);
@@ -353,10 +361,17 @@ int main(int argc, char *argv[])
 		global_data_t global_data;
 
 		if (initialize_global_data(&config, &global_data) == EXIT_SUCCESS) {
+			thread_context_t ctx;
+
 			setup_process();
-			start_threads(global_data.ctx);
-			connect_to_database(global_data.ctx);
-			event_loop(global_data.ctx);
+			start_threads(global_data.global_thread_data);
+			initialize_thread_context(global_data.global_thread_data, true, &ctx);
+			connect_to_database(&ctx);
+			event_loop(&ctx);
+			// Even though this is global data, we need to close
+			// it before the associated event loop is cleaned up.
+			h2o_socket_close(global_data.signals);
+			free_thread_context(&ctx);
 			free_global_data(&global_data);
 			rc = EXIT_SUCCESS;
 		}

+ 18 - 17
frameworks/C/h2o/src/request_handler.c

@@ -20,6 +20,7 @@
 #include <assert.h>
 #include <h2o.h>
 #include <stdalign.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
 #include <yajl/yajl_gen.h>
@@ -48,7 +49,9 @@ static int json_serializer(struct st_h2o_handler_t *self, h2o_req_t *req)
 		CHECK_YAJL_STATUS(yajl_gen_string, gen, YAJL_STRLIT(HELLO_RESPONSE));
 		CHECK_YAJL_STATUS(yajl_gen_map_close, gen);
 
-		if (!send_json_response(gen, NULL, req))
+		// The response is small enough, so that it is simpler to copy it
+		// instead of doing a delayed deallocation of the JSON generator.
+		if (!send_json_response(gen, true, req))
 			return 0;
 
 error_yajl:
@@ -169,31 +172,29 @@ void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req
 	h2o_send_error_generic(req, status_code, status_code_to_string(status_code), body, 0);
 }
 
-int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req)
+int send_json_response(yajl_gen gen, bool free_gen, h2o_req_t *req)
 {
 	const unsigned char *buf;
-	h2o_iovec_t h2o_iovec = {.len = 0};
+	size_t len;
 	int ret = EXIT_FAILURE;
 
-	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 (yajl_gen_get_buf(gen, &buf, &len) == yajl_gen_status_ok) {
+		if (free_gen) {
+			char * const body = h2o_mem_alloc_pool(&req->pool, len);
 
-			if (h2o_iovec.base) {
-				memcpy(h2o_iovec.base, buf, h2o_iovec.len);
+			if (body) {
+				memcpy(body, buf, len);
 				yajl_gen_free(gen);
-				set_default_response_param(JSON, h2o_iovec.len, req);
-				h2o_send_inline(req, h2o_iovec.base, h2o_iovec.len);
+				set_default_response_param(JSON, len, req);
+				h2o_send_inline(req, body, len);
 				ret = EXIT_SUCCESS;
 			}
 		}
+		else {
+			set_default_response_param(JSON, len, req);
+			h2o_send_inline(req, (char *) buf, len);
+			ret = EXIT_SUCCESS;
+		}
 	}
 
 	return ret;

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

@@ -22,6 +22,7 @@
 #define REQUEST_H_
 
 #include <h2o.h>
+#include <stdbool.h>
 #include <yajl/yajl_gen.h>
 
 typedef enum {
@@ -44,7 +45,7 @@ const char *get_query_param(const char *query,
                             size_t param_len);
 void register_request_handlers(h2o_hostconf_t *hostconf, h2o_access_log_filehandle_t *log_handle);
 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);
+int send_json_response(yajl_gen gen, bool free_gen, 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,
                                 size_t content_length,

+ 49 - 31
frameworks/C/h2o/src/thread.c

@@ -22,9 +22,12 @@
 #include <errno.h>
 #include <h2o.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
+#include <string.h>
 #include <h2o/serverutil.h>
 #include <sys/epoll.h>
+#include <sys/syscall.h>
 
 #include "database.h"
 #include "error.h"
@@ -35,71 +38,86 @@ static void *run_thread(void *arg);
 
 static void *run_thread(void *arg)
 {
-	connect_to_database(arg);
-	event_loop(arg);
+	thread_context_t ctx;
+
+	initialize_thread_context(arg, false, &ctx);
+	connect_to_database(&ctx);
+	event_loop(&ctx);
+	free_thread_context(&ctx);
 	pthread_exit(NULL);
 }
 
-void free_thread_contexts(global_data_t *global_data)
+void free_thread_context(thread_context_t *ctx)
 {
-	thread_context_t * const ctx = global_data->ctx;
-
-	for (size_t i = 0; i < ctx->global_data->config->thread_num; i++) {
-		if (i)
-			CHECK_ERROR(pthread_join, ctx[i].thread, NULL);
-		else
-			// Even though this is global data, we need to close
-			// it before the associated event loop is cleaned up.
-			h2o_socket_close(global_data->signals);
-
-		free_database_state(ctx[i].event_loop.h2o_ctx.loop, &ctx[i].db_state);
-		free_event_loop(&ctx[i].event_loop);
-	}
-
-	free(ctx);
+	free_database_state(ctx->event_loop.h2o_ctx.loop, &ctx->db_state);
+	free_event_loop(&ctx->event_loop, &ctx->global_thread_data->h2o_receiver);
 }
 
-thread_context_t *initialize_thread_contexts(global_data_t *global_data)
+global_thread_data_t *initialize_global_thread_data(const config_t *config,
+                                                    global_data_t *global_data)
 {
-	const size_t sz = global_data->config->thread_num * sizeof(thread_context_t);
-	thread_context_t * const ret = aligned_alloc(global_data->memory_alignment, sz);
+	const size_t sz = config->thread_num * sizeof(thread_context_t);
+	// The global thread data is modified only at program initialization and termination,
+	// and is not accessed by performance-sensitive code, so false sharing is not a concern.
+	global_thread_data_t * const ret = aligned_alloc(global_data->memory_alignment, sz);
 
 	if (ret) {
 		memset(ret, 0, sz);
 
-		for (size_t i = 0; i < global_data->config->thread_num; i++) {
+		for (size_t i = 0; i < config->thread_num; i++) {
+			ret[i].config = config;
 			ret[i].global_data = global_data;
-			initialize_event_loop(!i, global_data, &ret[i].event_loop);
-			initialize_database_state(ret[i].event_loop.h2o_ctx.loop, &ret[i].db_state);
 		}
 	}
 
 	return ret;
 }
 
-void start_threads(thread_context_t *ctx)
+void initialize_thread_context(global_thread_data_t *global_thread_data,
+                               bool is_main_thread,
+                               thread_context_t *ctx)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->config = global_thread_data->config;
+	ctx->global_data = global_thread_data->global_data;
+	ctx->global_thread_data = global_thread_data;
+	ctx->tid = syscall(SYS_gettid);
+	ctx->random_seed = ctx->tid;
+	initialize_event_loop(is_main_thread,
+	                      global_thread_data->global_data,
+	                      &global_thread_data->h2o_receiver,
+	                      &ctx->event_loop);
+	initialize_database_state(ctx->event_loop.h2o_ctx.loop, &ctx->db_state);
+	global_thread_data->ctx = ctx;
+}
+
+void start_threads(global_thread_data_t *global_thread_data)
 {
 	const size_t num_cpus = h2o_numproc();
 
 	// The first thread context is used by the main thread.
-	ctx->thread = pthread_self();
+	global_thread_data->thread = pthread_self();
 
-	for (size_t i = 1; i < ctx->global_data->config->thread_num; i++)
-		CHECK_ERROR(pthread_create, &ctx[i].thread, NULL, run_thread, ctx + i);
+	for (size_t i = 1; i < global_thread_data->config->thread_num; i++)
+		CHECK_ERROR(pthread_create,
+		            &global_thread_data[i].thread,
+		            NULL,
+		            run_thread,
+		            global_thread_data + i);
 
 	// If the number of threads is not equal to the number of processors, then let the scheduler
 	// decide how to balance the load.
-	if (ctx->global_data->config->thread_num == num_cpus) {
+	if (global_thread_data->config->thread_num == num_cpus) {
 		const size_t cpusetsize = CPU_ALLOC_SIZE(num_cpus);
 		cpu_set_t * const cpuset = CPU_ALLOC(num_cpus);
 
 		if (!cpuset)
 			abort();
 
-		for (size_t i = 0; i < ctx->global_data->config->thread_num; i++) {
+		for (size_t i = 0; i < global_thread_data->config->thread_num; i++) {
 			CPU_ZERO_S(cpusetsize, cpuset);
 			CPU_SET_S(i, cpusetsize, cpuset);
-			CHECK_ERROR(pthread_setaffinity_np, ctx[i].thread, cpusetsize, cpuset);
+			CHECK_ERROR(pthread_setaffinity_np, global_thread_data[i].thread, cpusetsize, cpuset);
 		}
 
 		CPU_FREE(cpuset);

+ 20 - 12
frameworks/C/h2o/src/thread.h

@@ -24,33 +24,41 @@
 #include <assert.h>
 #include <h2o.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <sys/types.h>
 
 #include "database.h"
 #include "event_loop.h"
 #include "utility.h"
 
-#define DEFAULT_CACHE_LINE_SIZE 64
-
 typedef struct thread_context_t thread_context_t;
 
+typedef struct global_thread_data_t {
+	const config_t *config;
+	thread_context_t *ctx;
+	global_data_t *global_data;
+	h2o_multithread_receiver_t h2o_receiver;
+	pthread_t thread;
+} global_thread_data_t;
+
 struct thread_context_t {
+	const config_t *config;
 	global_data_t *global_data;
+	// global_thread_data contains config and global_data as well,
+	// but keep copies here to avoid some pointer chasing.
+	global_thread_data_t *global_thread_data;
 	unsigned random_seed;
 	pid_t tid;
 	db_state_t db_state;
 	event_loop_t event_loop;
-	pthread_t thread;
-	// Align on the cache line size to prevent false sharing.
-	char padding[49];
 };
 
-static_assert(!(sizeof(thread_context_t) % DEFAULT_CACHE_LINE_SIZE),
-              "The size of the thread_context_t structure must be a "
-              "multiple of the cache line size.");
-
-void free_thread_contexts(global_data_t *global_data);
-thread_context_t *initialize_thread_contexts(global_data_t *global_data);
-void start_threads(thread_context_t *ctx);
+void free_thread_context(thread_context_t *ctx);
+global_thread_data_t *initialize_global_thread_data(const config_t *config,
+                                                    global_data_t *global_data);
+void initialize_thread_context(global_thread_data_t *global_thread_data,
+                               bool is_main_thread,
+                               thread_context_t *ctx);
+void start_threads(global_thread_data_t *global_thread_data);
 
 #endif // THREAD_H_

+ 3 - 3
frameworks/C/h2o/src/tls.c

@@ -136,7 +136,7 @@ void cleanup_openssl(global_data_t *global_data)
 	CHECK_ERROR(pthread_mutexattr_destroy, &openssl_global_data.lock_attr);
 }
 
-void initialize_openssl(global_data_t *global_data)
+void initialize_openssl(const config_t *config, global_data_t *global_data)
 {
 	SSL_library_init();
 	SSL_load_error_strings();
@@ -160,10 +160,10 @@ void initialize_openssl(global_data_t *global_data)
 	global_data->ssl_ctx = SSL_CTX_new(TLSv1_2_server_method());
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_certificate_file,
 	                    global_data->ssl_ctx,
-	                    global_data->config->cert,
+	                    config->cert,
 	                    SSL_FILETYPE_PEM);
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_PrivateKey_file,
 	                    global_data->ssl_ctx,
-	                    global_data->config->key,
+	                    config->key,
 	                    SSL_FILETYPE_PEM);
 }

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

@@ -24,6 +24,6 @@
 #include "utility.h"
 
 void cleanup_openssl(global_data_t *global_data);
-void initialize_openssl(global_data_t *global_data);
+void initialize_openssl(const config_t *config, global_data_t *global_data);
 
 #endif // TLS_H_

+ 6 - 3
frameworks/C/h2o/src/utility.h

@@ -36,7 +36,7 @@
 #define TOSTRING(x) # x
 #define YAJL_STRLIT(s) (const unsigned char *) (s), sizeof(s) - 1
 
-typedef struct thread_context_t thread_context_t;
+typedef struct global_thread_data_t global_thread_data_t;
 
 typedef struct {
 	const char *bind_address;
@@ -54,12 +54,11 @@ typedef struct {
 } config_t;
 
 typedef struct {
-	const config_t *config;
-	thread_context_t *ctx;
 	h2o_logger_t *file_logger;
 	mustache_template_t *fortunes_template;
 	h2o_socket_t *signals;
 	SSL_CTX *ssl_ctx;
+	global_thread_data_t *global_thread_data;
 	size_t memory_alignment;
 	int listener_sd;
 	int signal_fd;
@@ -67,7 +66,11 @@ typedef struct {
 	h2o_globalconf_t h2o_config;
 } global_data_t;
 
+// Call yajl_gen_free() on the result, even though the JSON generator
+// uses a memory pool; in this way the code remains correct if the
+// underlying memory allocator is changed (e.g. for debugging purposes).
 yajl_gen get_json_generator(h2o_mem_pool_t *pool);
+
 uint32_t get_random_number(uint32_t max_rand, unsigned int *seed);
 
 #endif // UTILITY_H_

+ 72 - 66
frameworks/C/h2o/src/world.c

@@ -53,7 +53,6 @@ typedef struct {
 
 typedef struct {
 	db_query_param_t param;
-	yajl_gen gen;
 	const char *id_pointer;
 	h2o_req_t *req;
 	uint32_t id;
@@ -63,16 +62,18 @@ typedef struct {
 
 typedef struct {
 	single_query_ctx_t single;
+	yajl_gen gen;
 	size_t num_query;
 	size_t num_result;
 	update_state_t update_state;
 	query_result_t res[];
 } multiple_query_ctx_t;
 
+static void cleanup_request(void *data);
 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,
-                                           single_query_ctx_t *query_ctx);
+static void initialize_single_query_context(h2o_req_t *req,
+                                            on_result_t on_result,
+                                            single_query_ctx_t *query_ctx);
 static void on_database_error(db_query_param_t *param, const char *error_string);
 static void on_database_timeout(db_query_param_t *param);
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result);
@@ -85,6 +86,14 @@ static void serialize_items(const query_result_t *res,
                             yajl_gen gen,
                             h2o_req_t *req);
 
+static void cleanup_request(void *data)
+{
+	const multiple_query_ctx_t * const query_ctx = data;
+
+	if (query_ctx->gen)
+		yajl_gen_free(query_ctx->gen);
+}
+
 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,
@@ -108,16 +117,13 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 		num_query = MAX_QUERIES;
 
 	const size_t sz = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
-	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sz);
+	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_shared(&req->pool, sz, cleanup_request);
 
-	if (!query_ctx || initialize_single_query_context(req,
-	                                                  on_multiple_query_result,
-	                                                  &query_ctx->single))
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
-	else {
+	if (query_ctx) {
 		// MAX_ID is a relatively small number, so allocate on the stack.
 		DEFINE_BITSET(bitset, MAX_ID);
 
+		initialize_single_query_context(req, on_multiple_query_result, &query_ctx->single);
 		memset(&query_ctx->single + 1,
 		       0,
 		       offsetof(multiple_query_ctx_t, res) - sizeof(query_ctx->single));
@@ -138,43 +144,37 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 
 		query_ctx->single.id = htonl(query_ctx->res->id);
 
-		if (execute_query(ctx, &query_ctx->single.param)) {
-			yajl_gen_free(query_ctx->single.gen);
+		if (execute_query(ctx, &query_ctx->single.param))
 			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
+		else
+			// Create a JSON generator while the query is processed.
+			query_ctx->gen = get_json_generator(&req->pool);
 	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 
 	return 0;
 }
 
-static int initialize_single_query_context(h2o_req_t *req,
-                                           on_result_t on_result,
-                                           single_query_ctx_t *query_ctx)
+static void initialize_single_query_context(h2o_req_t *req,
+                                            on_result_t on_result,
+                                            single_query_ctx_t *query_ctx)
 {
-	int ret = EXIT_FAILURE;
-
 	memset(query_ctx, 0, sizeof(*query_ctx));
-	query_ctx->gen = get_json_generator(&req->pool);
-
-	if (query_ctx->gen) {
-		query_ctx->id_format = 1;
-		query_ctx->id_len = sizeof(query_ctx->id);
-		query_ctx->id_pointer = (const char *) &query_ctx->id;
-		query_ctx->param.command = WORLD_TABLE_NAME;
-		query_ctx->param.nParams = 1;
-		query_ctx->param.on_error = on_database_error;
-		query_ctx->param.on_result = on_result;
-		query_ctx->param.on_timeout = on_database_timeout;
-		query_ctx->param.paramFormats = &query_ctx->id_format;
-		query_ctx->param.paramLengths = &query_ctx->id_len;
-		query_ctx->param.paramValues = &query_ctx->id_pointer;
-		query_ctx->param.flags = IS_PREPARED;
-		query_ctx->param.resultFormat = 1;
-		query_ctx->req = req;
-		ret = EXIT_SUCCESS;
-	}
-
-	return ret;
+	query_ctx->id_format = 1;
+	query_ctx->id_len = sizeof(query_ctx->id);
+	query_ctx->id_pointer = (const char *) &query_ctx->id;
+	query_ctx->param.command = WORLD_TABLE_NAME;
+	query_ctx->param.nParams = 1;
+	query_ctx->param.on_error = on_database_error;
+	query_ctx->param.on_result = on_result;
+	query_ctx->param.on_timeout = on_database_timeout;
+	query_ctx->param.paramFormats = &query_ctx->id_format;
+	query_ctx->param.paramLengths = &query_ctx->id_len;
+	query_ctx->param.paramValues = &query_ctx->id_pointer;
+	query_ctx->param.flags = IS_PREPARED;
+	query_ctx->param.resultFormat = 1;
+	query_ctx->req = req;
 }
 
 static void on_database_error(db_query_param_t *param, const char *error_string)
@@ -183,7 +183,6 @@ static void on_database_error(db_query_param_t *param, const char *error_string)
 	                                                              param,
 	                                                              param);
 
-	yajl_gen_free(query_ctx->gen);
 	send_error(BAD_GATEWAY, error_string, query_ctx->req);
 }
 
@@ -193,7 +192,6 @@ static void on_database_timeout(db_query_param_t *param)
 	                                                              param,
 	                                                              param);
 
-	yajl_gen_free(query_ctx->gen);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
 }
 
@@ -228,7 +226,7 @@ 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.gen,
+			                query_ctx->gen,
 			                query_ctx->single.req);
 			return DONE;
 		}
@@ -253,7 +251,6 @@ static result_return_t on_multiple_query_result(db_query_param_t *param, PGresul
 		PQclear(result);
 	}
 
-	yajl_gen_free(query_ctx->single.gen);
 	return DONE;
 }
 
@@ -275,9 +272,17 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		random_number = ntohl(random_number);
 		PQclear(result);
 
-		if (!serialize_item(ntohl(query_ctx->id), random_number, query_ctx->gen) &&
-		    !send_json_response(query_ctx->gen, NULL, query_ctx->req))
-			return DONE;
+		const yajl_gen gen = get_json_generator(&query_ctx->req->pool);
+
+		if (gen) {
+			// The response is small enough, so that it is simpler to copy it
+			// instead of doing a delayed deallocation of the JSON generator.
+			if (!serialize_item(ntohl(query_ctx->id), random_number, gen) &&
+			    !send_json_response(gen, true, query_ctx->req))
+				return DONE;
+
+			yajl_gen_free(gen);
+		}
 
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, query_ctx->req);
 	}
@@ -286,7 +291,6 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		PQclear(result);
 	}
 
-	yajl_gen_free(query_ctx->gen);
 	return DONE;
 }
 
@@ -318,7 +322,7 @@ 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.gen,
+			                query_ctx->gen,
 			                query_ctx->single.req);
 			ret = DONE;
 			break;
@@ -329,7 +333,6 @@ 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;
@@ -419,19 +422,25 @@ static void serialize_items(const query_result_t *res,
                             yajl_gen gen,
                             h2o_req_t *req)
 {
-	CHECK_YAJL_STATUS(yajl_gen_array_open, gen);
+	// In principle the JSON generator can be created here, but we do it earlier,
+	// so that it happens in parallel with the database query; we assume that the
+	// allocation will rarely fail, so that the delayed error handling is not
+	// problematic.
+	if (gen) {
+		CHECK_YAJL_STATUS(yajl_gen_array_open, gen);
 
-	for (size_t i = 0; i < num_result; i++)
-		if (serialize_item(res[i].id, res[i].random_number, gen))
-			goto error_yajl;
+		for (size_t i = 0; i < num_result; i++)
+			if (serialize_item(res[i].id, res[i].random_number, gen))
+				goto error_yajl;
 
-	CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
+		CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
 
-	if (send_json_response(gen, NULL, req)) {
-error_yajl:
-		yajl_gen_free(gen);
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+		if (!send_json_response(gen, false, req))
+			return;
 	}
+
+error_yajl:
+	send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 }
 
 int multiple_queries(struct st_h2o_handler_t *self, h2o_req_t *req)
@@ -450,18 +459,15 @@ int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
 	                                                      req->conn->ctx);
 	single_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*query_ctx));
 
-	if (!query_ctx || initialize_single_query_context(req,
-	                                                  on_single_query_result,
-	                                                  query_ctx))
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
-	else {
+	if (query_ctx) {
+		initialize_single_query_context(req, on_single_query_result, query_ctx);
 		query_ctx->id = htonl(get_random_number(MAX_ID, &ctx->random_seed) + 1);
 
-		if (execute_query(ctx, &query_ctx->param)) {
-			yajl_gen_free(query_ctx->gen);
+		if (execute_query(ctx, &query_ctx->param))
 			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
 	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 
 	return 0;
 }

+ 2 - 7
frameworks/Elixir/phoenix/web/controllers/page_controller.ex

@@ -5,20 +5,17 @@ defmodule Hello.PageController do
 
   def index(conn, _params) do
     conn
-    |> merge_resp_headers(%{"content-type" => "application/json"})
     |> json(%{"TE Benchmarks\n" => "Started"})
   end
 
   # avoid namespace collision
   def _json(conn, _params) do
     conn
-    |> merge_resp_headers(%{"content-type" => "application/json"})
     |> json(%{message: "Hello, world!"})
   end
 
   def db(conn, _params) do
     conn
-    |> merge_resp_headers(%{"content-type" => "application/json"})
     |> json(Repo.get(World, :rand.uniform(10000)))
   end
 
@@ -34,7 +31,6 @@ defmodule Hello.PageController do
     end
 
     conn
-    |> merge_resp_headers(%{"content-type" => "application/json"})
     |> json(Enum.map(1..q, fn _ -> Repo.get(World, :rand.uniform(10000)) end))
   end
 
@@ -61,17 +57,16 @@ defmodule Hello.PageController do
     end
 
     conn
-    |> merge_resp_headers(%{"content-type" => "application/json"})
     |> json(Enum.map(1..q, fn _ ->
       w = Repo.get(World, :rand.uniform(10000))
       changeset = World.changeset(w, %{randomNumber: :rand.uniform(10000)})
       Repo.update(changeset)
-      w end))
+      w
+    end))
   end
 
   def plaintext(conn, _params) do
     conn
-    |> merge_resp_headers(%{"content-type" => "text/plain"})
     |> text("Hello, world!")
   end
 end

+ 8 - 0
frameworks/Java/rapidoid/README.md

@@ -2,6 +2,14 @@
 
 This is the Rapidoid portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
+### Plaintext and JSON Encoding tests - using Rapidoid's high-level API (rapidoid-quick)
+* [highlevel/Main.java](src/main/java/highlevel/Main.java)
+
+### Fortunes test - using Rapidoid's high-level API (rapidoid-quick)
+* [highlevel/Main.java](src/main/java/highlevel/Main.java)
+* [highlevel/FortunesHandler.java](src/main/java/highlevel/FortunesHandler.java)
+* [fortunes.html](src/main/resources/fortunes.html)
+
 ### Plaintext and JSON Encoding tests - using Rapidoid's low-level API (rapidoid-http-fast)
 * [lowlevel/Main.java](src/main/java/lowlevel/Main.java)
 * [lowlevel/PlaintextAndJsonServer.java](src/main/java/lowlevel/PlaintextAndJsonServer.java)

+ 58 - 21
frameworks/Java/rapidoid/benchmark_config.json

@@ -1,25 +1,62 @@
 {
   "framework": "rapidoid",
-  "tests": [{
-    "default": {
-      "setup_file": "setup-low-level",
-      "json_url": "/json",
-      "plaintext_url": "/plaintext",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "None",
-      "language": "Java",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "None",
-      "webserver": "None",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "Rapidoid",
-      "notes": "",
-      "versus": "rapidoid"
-    }
+  "tests": [
+    {
+      "default": {
+        "setup_file": "setup-high-level",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "None",
+        "framework": "rapidoid",
+        "language": "Java",
+        "orm": "raw",
+        "platform": "Rapidoid",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "rapidoid",
+        "notes": "",
+        "versus": ""
+      },
+      "mysql": {
+        "setup_file": "setup-high-level",
+        "fortune_url": "/fortunes/mysql",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "MySQL",
+        "framework": "rapidoid",
+        "language": "Java",
+        "orm": "micro",
+        "platform": "Rapidoid",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "rapidoid",
+        "notes": "",
+        "versus": ""
+      },
+      "http-fast": {
+        "setup_file": "setup-low-level",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Platform",
+        "database": "None",
+        "framework": "rapidoid-http-fast",
+        "language": "Java",
+        "orm": "Raw",
+        "platform": "Rapidoid",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "rapidoid-http-fast",
+        "notes": "",
+        "versus": ""
+      }
   }]
 }

+ 8 - 0
frameworks/Java/rapidoid/setup-high-level.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+
+fw_depends java maven
+
+mvn clean compile assembly:single
+
+cd target
+java -server -XX:+UseNUMA -XX:+UseParallelGC -XX:+AggressiveOpts -cp rapidoid-1.0-jar-with-dependencies.jar highlevel.Main dbhost="$DBHOST" &

+ 6 - 0
frameworks/Java/rapidoid/source_code

@@ -1,5 +1,11 @@
 ./rapidoid/src/main/java/common
+./rapidoid/src/main/java/common/Fortune.java
+./rapidoid/src/main/java/common/Helper.java
 ./rapidoid/src/main/java/common/Message.java
+./rapidoid/src/main/java/highlevel
+./rapidoid/src/main/java/highlevel/Main.java
 ./rapidoid/src/main/java/lowlevel
 ./rapidoid/src/main/java/lowlevel/Main.java
 ./rapidoid/src/main/java/lowlevel/PlaintextAndJsonServer.java
+./rapidoid/src/main/java/resources
+./rapidoid/src/main/java/resources/fortunes.html

+ 48 - 0
frameworks/Java/rapidoid/src/main/java/common/Fortune.java

@@ -0,0 +1,48 @@
+package common;
+
+import org.rapidoid.u.U;
+
+public class Fortune implements Comparable<Fortune> {
+
+	private int id;
+	private String message;
+
+	public Fortune() {
+	}
+
+	public Fortune(int id, String message) {
+		this.id = id;
+		this.message = message;
+	}
+
+	public int getId() {
+		return id;
+	}
+
+	public Fortune setId(int id) {
+		this.id = id;
+		return this;
+	}
+
+	public String getMessage() {
+		return message;
+	}
+
+	public Fortune setMessage(String message) {
+		this.message = message;
+		return this;
+	}
+
+	@Override
+	public String toString() {
+		return "Fortune{" +
+			"id=" + id +
+			", message='" + message + '\'' +
+			'}';
+	}
+
+	@Override
+	public int compareTo(Fortune o) {
+		return U.compare(this.message, o.message);
+	}
+}

+ 12 - 0
frameworks/Java/rapidoid/src/main/java/common/Helper.java

@@ -0,0 +1,12 @@
+package common;
+
+public class Helper {
+
+	// this configuration was copied from the undertow-example project
+	public static final String MYSQL_CONFIG = "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";
+
+}

+ 36 - 0
frameworks/Java/rapidoid/src/main/java/highlevel/FortunesHandler.java

@@ -0,0 +1,36 @@
+package highlevel;
+
+import common.Fortune;
+import org.rapidoid.http.Req;
+import org.rapidoid.http.ReqRespHandler;
+import org.rapidoid.http.Resp;
+import org.rapidoid.render.Template;
+import org.rapidoid.render.Templates;
+import org.rapidoid.sql.JdbcClient;
+
+import java.util.Collections;
+import java.util.List;
+
+public class FortunesHandler implements ReqRespHandler {
+
+	private final Template fortunesTmpl = Templates.load("fortunes.html");
+
+	private final JdbcClient jdbc;
+
+	public FortunesHandler(JdbcClient jdbc) {
+		this.jdbc = jdbc;
+	}
+
+	@Override
+	public Object execute(Req req, Resp resp) throws Exception {
+
+		List<Fortune> fortunes = jdbc.query(Fortune.class, "SELECT * FROM fortune");
+
+		fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+
+		Collections.sort(fortunes);
+
+		return fortunesTmpl.renderToBytes(fortunes);
+	}
+
+}

+ 44 - 0
frameworks/Java/rapidoid/src/main/java/highlevel/Main.java

@@ -0,0 +1,44 @@
+package highlevel;
+
+import common.Helper;
+import common.Message;
+import org.rapidoid.config.Conf;
+import org.rapidoid.http.MediaType;
+import org.rapidoid.log.Log;
+import org.rapidoid.setup.App;
+import org.rapidoid.setup.On;
+import org.rapidoid.sql.JDBC;
+import org.rapidoid.sql.JdbcClient;
+
+public class Main {
+
+	public static void main(String[] args) {
+
+		App.args(args,
+			"production",
+			"c3p0.maxPoolSize=256",
+			"c3p0.maxIdleTimeExcessConnections=256",
+			"c3p0.maxStatementsPerConnection=3",
+			"http.serverName=X",
+			"http.mandatoryHeaders.connection=false",
+			"http.timeout=0");
+
+		String dbHost = Conf.ROOT.entry("dbhost").or("localhost");
+		Log.info("Database hostname is: " + dbHost);
+
+		On.port(8080);
+
+		On.get("/plaintext").managed(false).contentType(MediaType.TEXT_PLAIN).serve("Hello, world!");
+
+		On.get("/json").managed(false).json(() -> new Message("Hello, world!"));
+
+		JdbcClient mysqlJdbc = JDBC.newApi()
+			.url("jdbc:mysql://" + dbHost + ":3306/hello_world?" + Helper.MYSQL_CONFIG)
+			.username("benchmarkdbuser")
+			.password("benchmarkdbpass")
+			.pooled();
+
+		On.get("/fortunes/mysql").html(new FortunesHandler(mysqlJdbc));
+	}
+
+}

+ 8 - 0
frameworks/Java/rapidoid/src/main/resources/fortunes.html

@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body><table>
+<tr><th>id</th><th>message</th></tr>
+{{#.}}<tr><td>${id}</td><td>${message}</td></tr>{{/.}}
+</table></body>
+</html>

+ 0 - 13
frameworks/Java/servlet/.classpath

@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry including="**/*.java" kind="src" path="src/main/java"/>
-	<classpathentry kind="var" path="M2_REPO/javax/inject/javax.inject/1/javax.inject-1.jar"/>
-	<classpathentry kind="var" path="M2_REPO/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="var" path="M2_REPO/mysql/mysql-connector-java/5.1.38/mysql-connector-java-5.1.38.jar"/>
-	<classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.1.2/jackson-databind-2.1.2.jar"/>
-	<classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.1.1/jackson-annotations-2.1.1.jar"/>
-	<classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.1.1/jackson-core-2.1.1.jar"/>
-	<classpathentry kind="var" path="M2_REPO/org/apache/commons/commons-lang3/3.1/commons-lang3-3.1.jar"/>
-	<classpathentry kind="output" path="src/main/webapp/WEB-INF/classes"/>
-</classpath>

+ 0 - 14
frameworks/Java/servlet/.project

@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-  <name>world</name>
-  <comment>NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse.</comment>
-  <projects/>
-  <buildSpec>
-    <buildCommand>
-      <name>org.eclipse.jdt.core.javabuilder</name>
-    </buildCommand>
-  </buildSpec>
-  <natures>
-    <nature>org.eclipse.jdt.core.javanature</nature>
-  </natures>
-</projectDescription>

+ 0 - 5
frameworks/Java/servlet/.settings/org.eclipse.jdt.core.prefs

@@ -1,5 +0,0 @@
-#Thu Apr 11 13:42:30 PDT 2013
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.source=1.7
-org.eclipse.jdt.core.compiler.compliance=1.7

+ 2 - 1
frameworks/Java/servlet/benchmark_config.json

@@ -25,7 +25,7 @@
       "setup_file": "setup",
       "db_url": "/servlet/db",
       "query_url": "/servlet/db?queries=",
-      "fortune_url": "/servlet//fortunes",
+      "fortune_url": "/servlet/fortunes",
       "update_url": "/servlet/update?queries=",
       "port": 8080,
       "approach": "Realistic",
@@ -47,6 +47,7 @@
       "setup_file": "setup",
       "db_url": "/servlet/postgres",
       "query_url": "/servlet/postgres?queries=",
+      "fortune_url": "/servlet/postgre-fortunes",
       "update_url": "/servlet/postgres-update?queries=",
       "port": 8080,
       "approach": "Realistic",

+ 75 - 0
frameworks/Java/servlet/src/main/java/hello/PostgreFortunesServlet.java

@@ -0,0 +1,75 @@
+package hello;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Resource;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.sql.DataSource;
+
+/**
+ * Fortunes test, returns a list of fortune cookie messages fetched from
+ * a database table and then composed by server-side templates.
+ */
+@SuppressWarnings("serial")
+public class PostgreFortunesServlet extends HttpServlet
+{
+  
+  // Database details.
+  private static final String DB_QUERY = "SELECT * FROM Fortune";
+  private static final String UTF8 = "UTF-8";
+  private static final String CONTENT_TYPE_HTML_UTF8 = "text/html;charset=UTF-8";
+
+  // Database connection pool.
+  @Resource(name="jdbc/postgres_hello_world")
+  private DataSource postgresDataSource;
+  
+  @Override
+  protected void doGet(HttpServletRequest req, HttpServletResponse res)
+			throws ServletException, IOException
+  {
+    // Set content type to JSON
+    res.setCharacterEncoding(UTF8);
+    res.setContentType(CONTENT_TYPE_HTML_UTF8);
+
+    final List<Fortune> fortunes = new ArrayList<>();
+    
+    try (
+         Connection conn = postgresDataSource.getConnection();
+         PreparedStatement statement = conn.prepareStatement(DB_QUERY, 
+             ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+         ResultSet results = statement.executeQuery()
+        )
+    {
+      while (results.next())
+      {
+        fortunes.add(new Fortune(results.getInt("id"), results.getString("message")));
+      }
+    }
+    catch (SQLException sqlex)
+    {
+      System.err.println("SQL Exception: " + sqlex);
+    }
+    
+    fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+    Collections.sort(fortunes);
+    
+    // Set the list of Fortunes as an attribute of the request, making it
+    // available to the JSP.
+    req.setAttribute("fortunes", fortunes);
+    
+    // Dispatch to the JSP.
+    RequestDispatcher disp = req.getRequestDispatcher("/WEB-INF/jsp/fortunes.jsp");
+    disp.forward(req, res);
+  }
+}

+ 6 - 0
frameworks/Java/servlet/src/main/webapp/WEB-INF/web.xml

@@ -35,6 +35,12 @@
     <load-on-startup/>
   </servlet>
   <servlet-mapping url-regexp='^/postgres$' servlet-name='postgres'/>
+  <servlet>
+    <servlet-name>postgre-fortunes</servlet-name>
+    <servlet-class>hello.PostgreFortunesServlet</servlet-class>
+    <load-on-startup/>
+  </servlet>
+  <servlet-mapping url-regexp='^/postgre-fortunes$' servlet-name='postgre-fortunes'/>
   <servlet>
     <servlet-name>postgres-update</servlet-name>
     <servlet-class>hello.PostgresUpdateServlet</servlet-class>

+ 2 - 0
frameworks/Java/spark/benchmark_config.json

@@ -6,7 +6,9 @@
       "json_url": "/spark/json",
       "db_url": "/spark/db",
       "query_url": "/spark/db?queries=",
+      "update_url": "/spark/updates?queries=",
       "plaintext_url": "/spark/plaintext",
+      "fortune_url": "/spark/fortunes",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",

+ 10 - 5
frameworks/Java/spark/pom.xml

@@ -9,8 +9,8 @@
     <version>1.0.0-BUILD-SNAPSHOT</version>
 
     <properties>
-        <java-version>1.7</java-version>
-        <spark-version>1.1.1</spark-version>
+        <java-version>1.8</java-version>
+        <spark-version>2.3</spark-version>
         <hibernate-version>4.3.0.Final</hibernate-version>
         <gson-version>2.2.4</gson-version>
         <mysql-connector-version>5.1.38</mysql-connector-version>
@@ -57,8 +57,13 @@
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.j2html</groupId>
+            <artifactId>j2html</artifactId>
+            <version>0.7</version>
+        </dependency>
     </dependencies>
-    
+
     <dependencyManagement>
         <dependencies>
             <dependency>
@@ -88,7 +93,7 @@
             </dependency>
         </dependencies>
     </dependencyManagement>
-    
+
     <profiles>
         <profile>
             <id>standalone</id>
@@ -103,7 +108,7 @@
             </dependencyManagement>
         </profile>
     </profiles>
-    
+
     <build>
         <resources>
             <resource>

+ 16 - 0
frameworks/Java/spark/src/main/java/hello/domain/Fortune.java

@@ -0,0 +1,16 @@
+package hello.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class Fortune {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    public int id;
+    public String message;
+
+}

+ 9 - 7
frameworks/Java/spark/src/main/java/hello/web/HibernateUtil.java

@@ -1,6 +1,7 @@
 package hello.web;
 
 import hello.domain.World;
+import hello.domain.Fortune;
 
 import org.hibernate.Session;
 import org.hibernate.SessionFactory;
@@ -14,10 +15,10 @@ import org.slf4j.LoggerFactory;
 public class HibernateUtil {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(HibernateUtil.class);
-    
+
     private static final SessionFactory SESSION_FACTORY = createSessionFactory();
     private static final ThreadLocal<Session> SESSIONS = new ThreadLocal<>();
-    
+
     public static Session getSession() {
         Session session = SESSIONS.get();
         if (session == null) {
@@ -26,7 +27,7 @@ public class HibernateUtil {
         }
         return session;
     }
-    
+
     public static void closeSession() {
         Session session = SESSIONS.get();
         if (session != null) {
@@ -34,7 +35,7 @@ public class HibernateUtil {
             SESSIONS.remove();
         }
     }
-    
+
     private static SessionFactory createSessionFactory() {
         try {
             Configuration configuration = configuration();
@@ -45,6 +46,7 @@ public class HibernateUtil {
             configuration.setProperty(AvailableSettings.SHOW_SQL, "false");
             configuration.setProperty(AvailableSettings.CURRENT_SESSION_CONTEXT_CLASS, "thread");
             configuration.addAnnotatedClass(World.class);
+            configuration.addAnnotatedClass(Fortune.class);
             StandardServiceRegistryBuilder serviceRegistryBuilder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
             return configuration.buildSessionFactory(serviceRegistryBuilder.build());
         } catch (RuntimeException ex) {
@@ -52,11 +54,11 @@ public class HibernateUtil {
             throw ex;
         }
     }
-    
+
     private static Configuration configuration() {
         boolean jndi = Boolean.parseBoolean(System.getProperty("jndi", "true"));
         Configuration configuration = new Configuration();
-        // We're always going to use the -local config now since there were previous 
+        // We're always going to use the -local config now since there were previous
         // problems with the jndi config.
         /*
         if (jndi) {
@@ -68,5 +70,5 @@ public class HibernateUtil {
         configuration.configure("/hibernate-local.cfg.xml");
         return configuration;
     }
-    
+
 }

+ 0 - 30
frameworks/Java/spark/src/main/java/hello/web/JsonTransformer.java

@@ -1,30 +0,0 @@
-package hello.web;
-
-import spark.Request;
-import spark.Response;
-import spark.ResponseTransformerRoute;
-
-import com.google.gson.Gson;
-
-public abstract class JsonTransformer extends ResponseTransformerRoute {
-
-    private static final Gson   GSON              = new Gson();
-    private static final String CONTENT_TYPE_JSON = "application/json";
-    
-    protected JsonTransformer(final String path) {
-        super(path);
-    }
-
-    @Override
-    public String render(final Object model) {
-        return GSON.toJson(model);
-    }
-
-    @Override
-    public Object handle(final Request request, final Response response) {
-        response.type(CONTENT_TYPE_JSON);
-        return handleInternal(request, response);
-    }
-    
-    protected abstract Object handleInternal(Request request, Response response);
-}

+ 110 - 60
frameworks/Java/spark/src/main/java/hello/web/SparkApplication.java

@@ -4,89 +4,139 @@ import static spark.Spark.after;
 import static spark.Spark.get;
 import hello.domain.Message;
 import hello.domain.World;
+import hello.domain.Fortune;
 
 import java.util.Date;
 import java.util.Random;
+import java.util.List;
+import java.util.Collections;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
 
 import org.hibernate.Session;
+import org.hibernate.Transaction;
 
-import spark.Filter;
 import spark.Request;
 import spark.Response;
-import spark.Route;
+
+import com.google.gson.Gson;
+
+import static j2html.TagCreator.*;
 
 public class SparkApplication implements spark.servlet.SparkApplication {
 
     private static final int DB_ROWS = 10000;
+    private static final int FORTUNE_ROWS = 12;
     private static final String MESSAGE = "Hello, World!";
+    private static final String ADDITIONAL_FORTUNE = "Additional fortune added at request time.";
+    private static final String CONTENT_TYPE_JSON = "application/json";
     private static final String CONTENT_TYPE_TEXT = "text/plain";
-    
+    private static final Gson GSON = new Gson();
+
+    private int getQueries(final Request request) {
+      try {
+        String param = request.queryParams("queries");
+        if (param == null) {
+          return 1;
+        }
+
+        int queries = Integer.parseInt(param);
+        if (queries < 1) {
+          return 1;
+        }
+        if (queries > 500) {
+          return 500;
+        }
+        return queries;
+      } catch (NumberFormatException ex) {
+        return 1;
+      }
+    }
+
     @Override
     public void init() {
-        get(new JsonTransformer("/json") {
-            @Override
-            protected Object handleInternal(final Request request, final Response response) {
-                return new Message();
-            }
-        });
-        get(new JsonTransformer("/db") {
-            @Override
-            protected Object handleInternal(final Request request, final Response response) {
-                final int queries = getQueries(request);
-                
-                final World[] worlds = new World[queries];
-                final Session session = HibernateUtil.getSession();
-                final Random random = ThreadLocalRandom.current();
-                
-                for (int i = 0; i < queries; i++) {
-                    worlds[i] = (World) session.byId(World.class).load(random.nextInt(DB_ROWS) + 1);
-                }
-                
-                return (request.queryParams("queries") == null ? worlds[0] : worlds);
-            }
-            
-            private int getQueries(final Request request) {
-                try {
-                    String param = request.queryParams("queries");
-                    if (param == null) {
-                        return 1;
-                    }
-                    
-                    int queries = Integer.parseInt(param);
-                    if (queries < 1) {
-                        return 1;
-                    }
-                    if (queries > 500) {
-                        return 500;
-                    }
-                    return queries;
-                } catch (NumberFormatException ex) {
-                    return 1;
-                }
+
+        get("/json", (request, response) -> {
+          response.type(CONTENT_TYPE_JSON);
+          return new Message(); }
+        , GSON::toJson);
+        get("/db", (request, response) -> {
+            response.type(CONTENT_TYPE_JSON);
+
+            final int queries = getQueries(request);
+
+            final World[] worlds = new World[queries];
+            final Session session = HibernateUtil.getSession();
+            final Random random = ThreadLocalRandom.current();
+
+            for (int i = 0; i < queries; i++) {
+                worlds[i] = (World) session.byId(World.class).load(random.nextInt(DB_ROWS) + 1);
             }
-        });
-        get(new Route("/plaintext") {
-            @Override
-            public Object handle(final Request request, final Response response) {
-                response.type(CONTENT_TYPE_TEXT);
-                return MESSAGE;
+
+            return (request.queryParams("queries") == null ? worlds[0] : worlds);
+        }, GSON::toJson);
+        get("/updates", (request, response) -> {
+            response.type(CONTENT_TYPE_JSON);
+
+            final int queries = getQueries(request);
+
+            final World[] worlds = new World[queries];
+            final Session session = HibernateUtil.getSession();
+            final Random random = ThreadLocalRandom.current();
+
+            for (int i = 0; i < queries; i++) {
+                int id = random.nextInt(DB_ROWS) + 1;
+                int randomNumber = random.nextInt(DB_ROWS) + 1;
+                World world = (World) session.byId(World.class).load(id);
+                world.randomNumber = randomNumber;
+                Transaction transaction = session.beginTransaction();
+                session.update(world);
+                transaction.commit();
+                worlds[i] = world;
             }
+
+            return worlds;
+        }, GSON::toJson);
+        get("/plaintext", (request, response) -> {
+            response.type(CONTENT_TYPE_TEXT);
+            return MESSAGE;
         });
-        after(new Filter("/db") {
-            @Override
-            public void handle(final Request request, final Response response) {
-                HibernateUtil.closeSession();
-            }
+        get("/fortunes", (request, response) -> {
+          final Session session = HibernateUtil.getSession();
+          Fortune newFortune = new Fortune();
+          newFortune.id = 0;
+          newFortune.message = ADDITIONAL_FORTUNE;
+          List<Fortune> fortunes = session.createCriteria(Fortune.class).list();
+          fortunes.add(newFortune);
+          Collections.sort(fortunes, (f1, f2) -> f1.message.compareTo(f2.message));
+          return document().render() +
+            html().with(
+                head().with(
+                    title("Fortunes")
+                ),
+                body().with(
+                    table().with(
+                        tr().with(
+                            th("id"),
+                            th("message")
+                        )).with(
+                        fortunes.stream().map((fortune) ->
+                            tr().with(
+                                td(Integer.toString(fortune.id)),
+                                td(fortune.message)
+                            )
+                        ).collect(Collectors.toList())
+                    )
+                )
+          ).render();
+
         });
-        after(new Filter() {
-            @Override
-            public void handle(final Request request, final Response response) {
-                response.raw().addDateHeader("Date", new Date().getTime());
-            }
+        after((request, response) -> {
+            HibernateUtil.closeSession();
+            response.raw().addDateHeader("Date", new Date().getTime());
         });
     }
-    
+
     public static void main(final String[] args) {
         System.setProperty("jndi", "false");
         new SparkApplication().init();

+ 2 - 2
frameworks/Java/spring/benchmark_config.json

@@ -17,8 +17,8 @@
       "language": "Java",
       "flavor": "None",
       "orm": "Full",
-      "platform": "Servlet",
-      "webserver": "Tomcat",
+      "platform": "Undertow",
+      "webserver": "None",
       "os": "Linux",
       "database_os": "Linux",
       "display_name": "spring",

+ 13 - 1
frameworks/JavaScript/express/app.js

@@ -156,7 +156,19 @@ if (cluster.isMaster) {
     });
   });
 
-  app.get('/fortune', function(req, res) {
+  app.get('/mongoose-fortune', function(req, res) {
+    MFortune.find({}, function(err, fortunes) {
+      var newFortune = {id: 0, message: "Additional fortune added at request time."};
+      fortunes.push(newFortune);
+      fortunes.sort(function (a, b) {
+        return (a.message < b.message) ? -1 : 1;
+      });
+
+      res.render('fortunes', {fortunes: fortunes});
+    });
+  });
+
+  app.get('/mysql-orm-fortune', function(req, res) {
     Fortune.findAll().then(function (fortunes) {
       var newFortune = {id: 0, message: "Additional fortune added at request time."};
       fortunes.push(newFortune);

+ 2 - 1
frameworks/JavaScript/express/benchmark_config.json

@@ -26,6 +26,7 @@
       "db_url": "/mongoose",
       "query_url": "/mongoose?queries=",
       "update_url": "/mongoose-update?queries=",
+      "fortune_url": "/mysql-orm-fortune",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
@@ -46,7 +47,7 @@
       "setup_file": "setup",
       "db_url": "/mysql-orm",
       "query_url": "/mysql-orm?queries=",
-      "fortune_url": "/fortune",
+      "fortune_url": "/mysql-orm-fortune",
       "update_url": "/mysql-orm-update?queries=",
       "port": 8080,
       "approach": "Realistic",

+ 1 - 0
frameworks/JavaScript/sailsjs/benchmark_config.json

@@ -30,6 +30,7 @@
       "db_url": "/postgres/db",
       "query_url": "/postgres/queries?queries=",
       "update_url": "/postgres/updates?queries=",
+      "fortune_url": "/postgres/fortunes",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Fullstack",

+ 1 - 0
frameworks/Lua/octopus/.gitignore

@@ -0,0 +1 @@
+octopus

+ 35 - 0
frameworks/Lua/octopus/README.md

@@ -0,0 +1,35 @@
+# Octopus (Nginx + LuaJIT)  Benchmark Test
+
+The test lua app is inside [app](app) directory.
+The configuration is in [config.lua](config.lua).
+Clones, compiles and runs nginx with ngx_lua module, see [openresty.org](http://openresty.org).
+Prerequisites: ```libssl-dev gcc g++ build-essential```.
+Requires [git](https://git-scm.com).
+Requires postgresql hostname specified as IP address, if not possible then add resolver conf to [config.lua](config.lua).
+The Octopus benchmark is using its ORM, and no raw queries.
+
+
+## Test URLs
+### Plaintext URL
+
+http://localhost:8080/plaintext
+
+### JSON Encoding 
+
+http://localhost:8080/json
+
+### Single Row Random Query
+
+http://localhost:8080/db
+
+### Multiple Row Query Test
+
+http://localhost:8080/queries?queries=2
+
+### Fortune URL
+
+http://localhost:8080/fortunes
+
+### DB Updates URL
+
+http://localhost:8080/update?queries=2

+ 16 - 0
frameworks/Lua/octopus/app/config.lua

@@ -0,0 +1,16 @@
+local config = {} -- extension configuration
+
+config.locations = {
+	{name = "/plaintext", script = "PlaintextController.lua"},
+	{name = "/json", script = "JsonSerializationController.lua"},
+	{name = "/db", script = "SingleQueryController.lua"},
+	{name = "/queries", script = "MultipleQueriesController.lua"},
+	{name = "/fortunes", script = "FortunesController.lua"},
+	{name = "/update", script = "UpdateController.lua"},
+}
+
+config.types = {
+	"types.lua"
+}
+
+return config -- return extension configuration

+ 34 - 0
frameworks/Lua/octopus/app/src/FortunesController.lua

@@ -0,0 +1,34 @@
+local json = require "json"
+local database = require "database"
+local exit = require "exit"
+
+local template = require'template'
+template.caching(false)
+-- Compile template, disable cache, enable plain text view to skip filesystem loading
+local view = template.compile([[<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>{% for _,f in ipairs(fortunes) do %}<tr><td>{{ f.id }}</td><td>{{ f.message }}</td></tr>{% end %}</table></body></html>]], nil, true)
+
+
+local function process (db)
+	local fortunes = db:find({Fortune = {}})
+	table.insert(fortunes, {id = 0, message = "Additional fortune added at request time."})
+	table.sort(fortunes, function(a, b)
+		return a.message < b.message
+	end)
+    return fortunes
+end
+
+
+local status, db = pcall(database.connect)
+if not status then exit(db) end
+
+local status, res = pcall(process, db)
+db:close()
+
+if status then
+	local html = view({fortunes = res})
+	ngx.header.content_type = 'text/html; charset=utf-8'
+	ngx.header.content_length = #html
+	ngx.print(html)
+else
+	exit(res)
+end

+ 4 - 0
frameworks/Lua/octopus/app/src/JsonSerializationController.lua

@@ -0,0 +1,4 @@
+local json = require "json"
+
+ngx.header.content_type = 'application/json'
+ngx.print(json.encode({message = "Hello, World!"}))

+ 36 - 0
frameworks/Lua/octopus/app/src/MultipleQueriesController.lua

@@ -0,0 +1,36 @@
+local json = require "json"
+local database = require "database"
+local param = require "param"
+local exit = require "exit"
+
+
+local function process (db)
+	local op = db:operators()
+	
+	local num_queries = tonumber(param.queries) or 1
+	if num_queries < 1 then 
+		num_queries = 1 
+	elseif num_queries > 500 then
+		num_queries = 500
+	end
+	
+	local worlds = {}
+	for i=1, num_queries do
+		worlds[#worlds + 1] = db:findOne({World = {id = op.equal(math.random(1,10000))}})
+	end
+	return worlds
+end
+
+
+local status, db = pcall(database.connect)
+if not status then exit(db) end
+
+local status, res = pcall(process, db)
+db:close()
+
+if status then
+	ngx.header.content_type = 'application/json'
+	ngx.print(json.encode(res))
+else
+	exit(res)
+end

+ 2 - 0
frameworks/Lua/octopus/app/src/PlaintextController.lua

@@ -0,0 +1,2 @@
+ngx.header.content_type = 'text/plain'
+ngx.print("Hello, World!")

+ 24 - 0
frameworks/Lua/octopus/app/src/SingleQueryController.lua

@@ -0,0 +1,24 @@
+local json = require "json"
+local database = require "database"
+local exit = require "exit"
+
+
+local function process (db)
+	local op = db:operators()
+	
+	return db:findOne({World = {id = op.equal(math.random(1,10000))}})
+end
+
+
+local status, db = pcall(database.connect)
+if not status then exit(db) end
+
+local status, res = pcall(process, db)
+db:close()
+
+if status then
+	ngx.header.content_type = 'application/json'
+	ngx.print(json.encode(res))
+else
+	exit(res)
+end

+ 39 - 0
frameworks/Lua/octopus/app/src/UpdateController.lua

@@ -0,0 +1,39 @@
+local json = require "json"
+local database = require "database"
+local param = require "param"
+local exit = require "exit"
+
+
+local function process (db)
+	local op = db:operators()
+	
+	local num_queries = tonumber(param.queries) or 1
+	if num_queries < 1 then 
+		num_queries = 1 
+	elseif num_queries > 500 then
+		num_queries = 500
+	end
+	
+	local worlds = {}
+	for i=1, num_queries do
+		local world = db:findOne({World = {id = op.equal(math.random(1,10000))}})
+		world.randomNumber = math.random(1,10000)
+		db:update({World = world})
+		worlds[#worlds + 1] = world
+	end
+	return worlds
+end
+
+
+local status, db = pcall(database.connect)
+if not status then exit(db) end
+
+local status, res = pcall(process, db)
+db:close()
+
+if status then
+	ngx.header.content_type = 'application/json'
+	ngx.print(json.encode(res))
+else
+	exit(res)
+end

+ 12 - 0
frameworks/Lua/octopus/app/src/types.lua

@@ -0,0 +1,12 @@
+return {
+
+	World = {
+		-- id field is implicitly defined by the ORM
+		randomNumber = "integer",
+	},
+
+	Fortune = {
+		-- id field is implicitly defined by the ORM
+		message = "string",
+	},
+}

+ 28 - 0
frameworks/Lua/octopus/benchmark_config.json

@@ -0,0 +1,28 @@
+{
+  "framework": "octopus",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "plaintext_url": "/plaintext",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/update?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "MySQL",
+      "framework": "Octopus",
+      "language": "Lua",
+      "orm": "Full",
+      "platform": "OpenResty",
+      "webserver": "nginx",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Octopus",
+      "notes": "",
+      "versus": "openresty"
+    }
+  }]
+}

+ 39 - 0
frameworks/Lua/octopus/config.lua

@@ -0,0 +1,39 @@
+return {
+	extensions = {
+		{octopusExtensionsDir, "core"}, 
+		{octopusExtensionsDir, "baseline"}, 
+		{octopusExtensionsDir, "orm"}, 
+		{octopusExtensionsDir, "app"},
+	},
+	
+	octopusExtensionsDir = octopusExtensionsDir,
+	octopusHostDir = octopusHostDir,
+	port = 8080,
+	securePort = 38080,
+	luaCodeCache = "on",
+	serverName = "localhost",
+	errorLog = "error_log logs/error.log;",
+	accessLog = "access_log logs/access.log;",
+	includeDrop = [[#include drop.conf;]],
+	maxBodySize = "50k",
+	minifyJavaScript = false,
+	minifyCommand = [[java -jar ../yuicompressor-2.4.8.jar %s -o %s]],
+	
+	databaseConnection = {
+		rdbms       =   "mysql",
+		host        =   "DBHOSTNAME",
+		port        =   3306, 
+		database    =   "hello_world",
+		user        =   "benchmarkdbuser",
+		password    =   "benchmarkdbpass",
+		compact     =   false
+	},
+
+	globalParameters = {
+		octopusHostDir = octopusHostDir,
+		sourceCtxPath = "",
+		requireSecurity = false,
+		sessionTimeout = 3600,
+		usePreparedStatement = false,
+	},
+}

+ 13 - 0
frameworks/Lua/octopus/setup.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+
+
+rm -rf octopus
+git clone https://github.com/cyberz-eu/octopus.git
+cp -avr app octopus/extensions
+cp -vf config.lua octopus/extensions
+sed -i 's|DBHOSTNAME|'"${DBHOST}"'|g' octopus/extensions/config.lua
+
+cd octopus/bin/unix
+. ./server.sh install
+. ./server.sh build
+. ./server.sh start

+ 50 - 43
frameworks/Lua/openresty/app.lua

@@ -6,6 +6,8 @@ local min = math.min
 local insert = table.insert
 local sort = table.sort
 local template = require'resty.template'
+local ngx = ngx
+local ngx_print = ngx.print
 template.caching(false)
 -- Compile template, disable cache, enable plain text view to skip filesystem loading
 local view = template.compile([[<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>{% for _,f in ipairs(fortunes) do %}<tr><td>{{ f.id }}</td><td>{{ f.message }}</td></tr>{% end %}</table></body></html>]], nil, true)
@@ -17,48 +19,53 @@ local mysqlconn = {
 	user = "benchmarkdbuser",
 	password = "benchmarkdbpass"
 }
-return {
-    db = function(ngx)
-        local db = mysql:new()
-        assert(db:connect(mysqlconn))
-        ngx.print(encode(db:query('SELECT * FROM World WHERE id = '..random(1,10000))[1]))
-        db:set_keepalive(0, 256)
-    end,
-    queries = function(ngx)
-        local db = mysql:new()
-        assert(db:connect(mysqlconn))
-        local num_queries = tonumber(ngx.var.arg_queries) or 1
-        -- May seem like a stupid branch, but since we know that
-        -- at a benchmark it will always be taken one way,
-        -- it doesn't matter. For me, after a small warmup, the performance
-        -- is identical to a version without the branch
-        -- http://wiki.luajit.org/Numerical-Computing-Performance-Guide
-        if num_queries < 2 then
-            ngx.print(encode({db:query('SELECT * FROM World WHERE id = '..random(1,10000))[1]}))
-        else
-            local worlds = {}
-            num_queries = min(500, num_queries)
-            for i=1, num_queries do
-                worlds[#worlds+1] = db:query('SELECT * FROM World WHERE id = '..random(1,10000))[1]
-            end
-            ngx.print( encode(worlds) )
+
+local _M = {}
+
+function _M.db()
+    local db = mysql:new()
+    assert(db:connect(mysqlconn))
+    ngx_print(encode(db:query('SELECT * FROM World WHERE id = '..random(1,10000))[1]))
+    db:set_keepalive(0, 256)
+end
+
+function _M.queries()
+    local db = mysql:new()
+    assert(db:connect(mysqlconn))
+    local num_queries = tonumber(ngx.var.arg_queries) or 1
+    -- May seem like a stupid branch, but since we know that
+    -- at a benchmark it will always be taken one way,
+    -- it doesn't matter. For me, after a small warmup, the performance
+    -- is identical to a version without the branch
+    -- http://wiki.luajit.org/Numerical-Computing-Performance-Guide
+    if num_queries < 2 then
+        ngx_print(encode({db:query('SELECT * FROM World WHERE id = '..random(1,10000))[1]}))
+    else
+        local worlds = {}
+        num_queries = min(500, num_queries)
+        for i=1, num_queries do
+            worlds[#worlds+1] = db:query('SELECT * FROM World WHERE id = '..random(1,10000))[1]
         end
-        db:set_keepalive(0, 256)
-    end,
-    fortunes = function(ngx)
-        local db = mysql:new()
-        assert(db:connect(mysqlconn))
-        local fortunes = db:query('SELECT * FROM Fortune')
-        insert(fortunes, {
-            id = 0,
-            message = "Additional fortune added at request time."
-        })
-        sort(fortunes, function(a, b)
-            return a.message < b.message
-        end)
-        local res = view{fortunes=fortunes}
-        ngx.header['Content-Length'] = #res
-        ngx.print(res)
-        db:set_keepalive(0, 256)
+        ngx_print( encode(worlds) )
     end
-}
+    db:set_keepalive(0, 256)
+end
+
+function _M.fortunes()
+    local db = mysql:new()
+    assert(db:connect(mysqlconn))
+    local fortunes = db:query('SELECT * FROM Fortune')
+    insert(fortunes, {
+        id = 0,
+        message = "Additional fortune added at request time."
+    })
+    sort(fortunes, function(a, b)
+        return a.message < b.message
+    end)
+    local res = view{fortunes=fortunes}
+    ngx.header['Content-Length'] = #res
+    ngx_print(res)
+    db:set_keepalive(0, 256)
+end
+
+return _M

+ 23 - 6
frameworks/Lua/openresty/nginx.conf

@@ -9,29 +9,46 @@ http {
     resolver 127.0.0.1;
     access_log off;
     lua_package_path 'CWD/?.lua;;';
-    init_by_lua 'jit.opt.start("minstitch=10"); require "resty.core" encode = require("cjson").encode mysql = require("resty.mysql") app = require("app")';
+    init_by_lua_block {
+        jit.opt.start("minstitch=10")
+        require "resty.core"
+        encode = require("cjson").encode
+        mysql = require("resty.mysql")
+        app = require("app")
+    }
+
     server {
         listen       8080;
         location /plaintext {
             default_type "text/plain";
-            content_by_lua 'ngx.print("Hello, world!")';
+            content_by_lua_block {
+                ngx.print("Hello, world!")
+            }
         }
 
         location /json {
             default_type "application/json";
-            content_by_lua 'ngx.print(encode({message = "Hello, World!"}))';
+            content_by_lua_block {
+                ngx.print(encode({message = "Hello, World!"}))
+            }
         }
         location /fortunes {
             default_type "text/html; charset=UTF-8";
-            content_by_lua 'app.fortunes(ngx)';
+            content_by_lua_block {
+                app.fortunes()
+            }
         }
         location /db {
             default_type "application/json";
-            content_by_lua 'app.db(ngx)';
+            content_by_lua_block {
+                app.db()
+            }
         }
         location / {
             default_type "application/json";
-            content_by_lua 'app.queries(ngx)';
+            content_by_lua_block {
+                app.queries()
+            }
         }
     }
 }

+ 1 - 1
frameworks/PHP/cygnite/deploy/nginx.conf

@@ -32,7 +32,7 @@ http {
     keepalive_timeout  65;
 
     #gzip  on;
-    
+
     upstream fastcgi_backend {
         server 127.0.0.1:9001;
     }

+ 24 - 0
frameworks/PHP/laravel/app/routes.php

@@ -45,4 +45,28 @@ Route::get('/db', function()
     return Response::json(DB::table('World')->find(mt_rand(1, 10000)));
 });
 
+Route::get('/updates', function()
+{
+    $queries = Input::get('queries', 1);
+
+    if (!is_numeric($queries) || $queries <= 1) {
+    	$queries = 1;
+    }
+    else if ($queries > 500) {
+        $queries = 500;
+    }
+
+    $worlds = array();
+
+    for($i = 0; $i < $queries; $i++) {
+        $id = mt_rand(1, 10000);
+        $random_number = mt_rand(1, 10000);
+        $world = DB::table('World')->find($id);
+        $world->randomNumber = $random_number;
+        DB::table('World')->where('id', $id)->update(['randomNumber' => $random_number]);
+        $worlds[] = $world;
+    }
+    return Response::json($worlds);
+});
+
 Route::get('/fortunes', 'BenchController@fortunes');

+ 4 - 73
frameworks/PHP/lithium/app/config/routes.php

@@ -19,79 +19,10 @@
 use lithium\net\http\Router;
 use lithium\core\Environment;
 
-/**
- * With globalization enabled a localized route is configured by connecting a
- * continuation route. Once the route has been connected, all the other
- * application routes become localized and may now carry a locale.
- *
- * Requests to routes like `/en/posts/edit/1138` or `/fr/posts/edit/1138` will
- * carry a locale, while `/posts/edit/1138` keeps on working as it did before.
- */
-if ($locales = Environment::get('locales')) {
-	$template = '/{:locale:' . join('|', array_keys($locales)) . '}/{:args}';
-	Router::connect($template, array(), array('continue' => true));
-}
-
-/**
- * Here, we are connecting `'/'` (the base path) to controller called `'Pages'`,
- * its action called `view()`, and we pass a param to select the view file
- * to use (in this case, `/views/pages/home.html.php`; see `app\controllers\PagesController`
- * for details).
- *
- * @see app\controllers\PagesController
- */
 Router::connect('/json', 'Bench::json');
-
-/**
- * Connect the rest of `PagesController`'s URLs. This will route URLs like `/pages/about` to
- * `PagesController`, rendering `/views/pages/about.html.php` as a static page.
- */
 Router::connect('/db/{:queries}', array('Bench::db', 'queries' => 1));
+Router::connect('/plaintext', 'Bench::plaintext');
+Router::connect('/update/{:queries}', array('Bench::update', 'queries' => 1));
+Router::connect('/fortunes', 'Bench::fortunes');
 
-/**
- * Add the testing routes. These routes are only connected in non-production environments, and allow
- * browser-based access to the test suite for running unit and integration tests for the Lithium
- * core, as well as your own application and any other loaded plugins or frameworks. Browse to
- * [http://path/to/app/test](/test) to run tests.
- */
-if (!Environment::is('production')) {
-	Router::connect('/test/{:args}', array('controller' => 'lithium\test\Controller'));
-	Router::connect('/test', array('controller' => 'lithium\test\Controller'));
-}
-
-/**
- * ### Database object routes
- *
- * The routes below are used primarily for accessing database objects, where `{:id}` corresponds to
- * the primary key of the database object, and can be accessed in the controller as
- * `$this->request->id`.
- *
- * If you're using a relational database, such as MySQL, SQLite or Postgres, where the primary key
- * is an integer, uncomment the routes below to enable URLs like `/posts/edit/1138`,
- * `/posts/view/1138.json`, etc.
- */
-// Router::connect('/{:controller}/{:action}/{:id:\d+}.{:type}', array('id' => null));
-// Router::connect('/{:controller}/{:action}/{:id:\d+}');
-
-/**
- * If you're using a document-oriented database, such as CouchDB or MongoDB, or another type of
- * database which uses 24-character hexidecimal values as primary keys, uncomment the routes below.
- */
-// Router::connect('/{:controller}/{:action}/{:id:[0-9a-f]{24}}.{:type}', array('id' => null));
-// Router::connect('/{:controller}/{:action}/{:id:[0-9a-f]{24}}');
-
-/**
- * Finally, connect the default route. This route acts as a catch-all, intercepting requests in the
- * following forms:
- *
- * - `/foo/bar`: Routes to `FooController::bar()` with no parameters passed.
- * - `/foo/bar/param1/param2`: Routes to `FooController::bar('param1, 'param2')`.
- * - `/foo`: Routes to `FooController::index()`, since `'index'` is assumed to be the action if none
- *   is otherwise specified.
- *
- * In almost all cases, custom routes should be added above this one, since route-matching works in
- * a top-down fashion.
- */
-Router::connect('/{:controller}/{:action}/{:args}');
-
-?>
+?>

+ 43 - 1
frameworks/PHP/lithium/app/controllers/BenchController.php

@@ -4,9 +4,14 @@ namespace app\controllers;
 
 use  lithium\action\Controller;
 use  app\models\World;
+use  app\models\Fortune;
 
 class BenchController extends Controller {
 
+    public function plaintext() {
+      return $this->render(array('text' => 'Hello, World!'));
+    }
+
     public function json() {
         return $this->render(array(
             'json' => array('message' => 'Hello, World!')
@@ -17,6 +22,7 @@ class BenchController extends Controller {
         $queries = isset($this->request->query['queries'])
             ? $this->request->query['queries']
             : 1;
+        $queries = min(max(1, $queries), 500);
         $worlds = array();
 
         for ($i = 0; $i < $queries; ++$i) {
@@ -31,4 +37,40 @@ class BenchController extends Controller {
             'json' => $worlds
         ));
     }
-}
+
+    public function update() {
+      $queries = isset($this->request->query['queries'])
+          ? $this->request->query['queries']
+          : 1;
+      $queries = min(max(1, $queries), 500);
+      $worlds = array();
+
+      for ($i = 0; $i < $queries; ++$i) {
+          $id = mt_rand(1, 10000);
+          $random_number = mt_rand(1, 10000);
+          $world = World::first(array(
+              'conditions' => array(
+                  'id' => $id
+              )
+          ));
+          $world->randomNumber = $random_number;
+          $world->save();
+          $worlds[] = $world;
+      }
+
+      return $this->render(array(
+          'json' => $worlds
+      ));
+    }
+
+    public function fortunes() {
+        $fortunes = Fortune::find('all')->to('array');
+        $fortunes[] = array('id' => 0, 'message' => 'Additional fortune added at request time.');
+        usort($fortunes, function($left, $right) {
+           return strcmp($left['message'], $right['message']);
+        });
+
+        $this->set(['fortunes' => $fortunes]);
+        return $this->render(array('layout' => false));
+    }
+}

+ 12 - 0
frameworks/PHP/lithium/app/models/Fortune.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace app\models;
+
+use \lithium\data\Model;
+
+class Fortune extends Model {
+    // stop lithium from pluralizing the table name
+    protected $_meta = array(
+        'source' => 'Fortune'
+    );
+}

+ 0 - 0
frameworks/PHP/lithium/app/resources/tmp/cache/templates/blank


+ 12 - 0
frameworks/PHP/lithium/app/views/bench/fortunes.html.php

@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+<head><title>Fortunes</title></head>
+<body>
+<table>
+<tr><th>id</th><th>message</th></tr>
+<?php foreach($fortunes as $fortune) { ?>
+  <tr><td><?php echo $fortune['id']; ?></td><td><?php echo $h($fortune['message']); ?></td></tr>
+<?php } ?>
+</table>
+</body>
+</html>

+ 3 - 0
frameworks/PHP/lithium/benchmark_config.json

@@ -3,9 +3,12 @@
   "tests": [{
     "default": {
       "setup_file": "setup",
+      "plaintext_url": "/plaintext",
       "json_url": "/json",
       "db_url": "/db",
+      "update_url": "/update?queries=",
       "query_url": "/db?queries=",
+      "fortune_url": "/fortunes",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Fullstack",

+ 9 - 0
frameworks/PHP/slim/benchmark_config.json

@@ -3,9 +3,12 @@
   "tests": [{
     "default": {
       "setup_file": "setup",
+      "plaintext_url": "/plaintext",
       "json_url": "/json",
       "db_url": "/db",
       "query_url": "/dbs?queries=",
+      "update_url": "/updates?queries=",
+      "fortune_url": "/fortunes",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
@@ -24,9 +27,12 @@
     },
     "hhvm": {
       "setup_file": "setup_hhvm",
+      "plaintext_url": "/plaintext",
       "json_url": "/json",
       "db_url": "/db",
       "query_url": "/dbs?queries=",
+      "update_url": "/updates?queries=",
+      "fortune_url": "/fortunes",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
@@ -45,9 +51,12 @@
     },
     "php5": {
       "setup_file": "setup_php5",
+      "plaintext_url": "/plaintext",
       "json_url": "/json",
       "db_url": "/db",
       "query_url": "/dbs?queries=",
+      "update_url": "/updates?queries=",
+      "fortune_url": "/fortunes",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",

+ 2 - 1
frameworks/PHP/slim/composer.json

@@ -1,5 +1,6 @@
 {
     "require": {
-        "slim/slim": "3.3.0"
+        "slim/slim": "3.3.0",
+        "slim/php-view": "2.1.0" 
     }
 }

+ 66 - 13
frameworks/PHP/slim/index.php

@@ -3,9 +3,29 @@ error_reporting(-1);
 
 require_once __DIR__.'/vendor/autoload.php';
 
-$app = new \Slim\App;
+$app = new Slim\App(array(
+    'db' => function ($c) {
+        $pdo = new PDO('mysql:host=localhost;dbname=hello_world;charset=utf8', 'benchmarkdbuser', 'benchmarkdbpass');
+        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+        $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
 
-// Test 1: JSON serialization
+        return $pdo;
+    },
+
+    'view' => function ($c) {
+        return new Slim\Views\PhpRenderer("templates/");
+    }
+));
+
+// Test 1: Plaintext
+$app->get('/plaintext', function ($request, $response) {
+    return $response
+        ->write('Hello, World!')
+        ->withHeader('Content-Type', 'text/plain')
+        ;
+});
+
+// Test 2: JSON serialization
 $app->get('/json', function ($request, $response) {
     return $response
         ->withJson(array('message' => 'Hello, World!'))
@@ -13,16 +33,7 @@ $app->get('/json', function ($request, $response) {
         ;
 });
 
-$container = $app->getContainer();
-$container['db'] = function ($c) {
-    $db = $c['settings']['db'];
-    $pdo = new PDO('mysql:host=localhost;dbname=hello_world', 'benchmarkdbuser', 'benchmarkdbpass');
-    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-    $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
-    return $pdo;
-};
-
-// Test 2: Single database query
+// Test 3: Single database query
 $app->get('/db', function ($request, $response) {
     $sth = $this->db->prepare('SELECT * FROM World WHERE id = ?');
     $sth->execute(array(mt_rand(1, 10000)));
@@ -37,7 +48,7 @@ $app->get('/db', function ($request, $response) {
         ;
 });
 
-// Test 3: Multiple database queries
+// Test 4: Multiple database queries
 $app->get('/dbs', function ($request, $response) {
     $queries = max(1, min($request->getParam('queries'), 500));
 
@@ -58,4 +69,46 @@ $app->get('/dbs', function ($request, $response) {
         ;
 });
 
+// Test 5: Updates
+$app->get('/updates', function ($request, $response) {
+    $queries = max(1, min($request->getParam('queries'), 500));
+
+    $sth = $this->db->prepare('SELECT * FROM World WHERE id = ?');
+    $updateSth = $this->db->prepare('UPDATE World SET randomNumber = ? WHERE id = ?');
+
+    $worlds = array();
+    for ($i = 0; $i < $queries; ++$i) {
+        $id = mt_rand(1, 10000);
+        $random_number = mt_rand(1, 10000);
+        $sth->execute(array($id));
+        $world = $sth->fetch();
+        # Cast fields to int so they don't get wrapped with quotes
+        $world['id'] = (int) $world['id'];
+        $world['randomNumber'] = $random_number;
+
+        $updateSth->execute(array($world['randomNumber'], $world['id']));
+
+        $worlds[] = $world;
+    }
+
+    return $response
+        ->withJson($worlds)
+        ->withHeader('Content-Type', 'application/json') // fixes utf-8 warning
+        ;
+});
+
+// Test 6: Fortunes
+$app->get('/fortunes', function ($request, $response) {
+    $sth = $this->db->prepare('SELECT * FROM Fortune');
+    $sth->execute();
+    $fortunes = $sth->fetchAll();
+
+    array_push($fortunes, array('id'=> 0, 'message' => 'Additional fortune added at request time.'));
+    usort($fortunes, function($left, $right) {
+        return strcmp($left['message'], $right['message']);
+    });
+
+    return $this->view->render($response, "fortunes.php", ["fortunes" => $fortunes]);
+});
+
 $app->run();

+ 18 - 0
frameworks/PHP/slim/templates/fortunes.php

@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head><title>Fortunes</title></head>
+  <body>
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>
+        <?php foreach($data['fortunes'] as $fortune){ ?>
+            <tr>
+                <td><?php echo $fortune['id']; ?></td>
+                <td><?php echo htmlspecialchars($fortune['message']); ?></td>
+            </tr>
+        <?php } ?>
+    </table>
+  </body>
+</html>

+ 13 - 1
frameworks/PHP/symfony2-stripped/app/config/routing.yml

@@ -1,3 +1,7 @@
+_plaintext:
+    pattern:  /plaintext
+    defaults: { _controller: SkamanderBenchmarkBundle:Bench:plaintext }
+
 _json:
     pattern:  /json
     defaults: { _controller: SkamanderBenchmarkBundle:Bench:json }
@@ -10,6 +14,14 @@ _dbRaw:
     pattern:  /db-raw
     defaults: { _controller: SkamanderBenchmarkBundle:Bench:dbRaw }
 
+_update:
+    pattern:  /update
+    defaults: { _controller: SkamanderBenchmarkBundle:Bench:update }
+
+_updateRaw:
+    pattern:  /update-raw
+    defaults: { _controller: SkamanderBenchmarkBundle:Bench:updateRaw }
+
 _fortunes:
     pattern:  /fortunes
     defaults: { _controller: SkamanderBenchmarkBundle:Bench:fortunes }
@@ -20,4 +32,4 @@ _fortunesPhp:
 
 _fortunesRaw:
     pattern:  /fortunes-raw
-    defaults: { _controller: SkamanderBenchmarkBundle:Bench:fortunesRaw }
+    defaults: { _controller: SkamanderBenchmarkBundle:Bench:fortunesRaw }

+ 3 - 0
frameworks/PHP/symfony2-stripped/benchmark_config.json

@@ -3,8 +3,10 @@
   "tests": [{
     "default": {
       "setup_file": "setup",
+      "plaintext_url": "/plaintext",
       "json_url": "/json",
       "db_url": "/db",
+      "update_url": "/update?queries=",
       "query_url": "/db?queries=",
       "fortune_url": "/fortunes",
       "port": 8080,
@@ -45,6 +47,7 @@
     "raw": {
       "setup_file": "setup",
       "db_url": "/db-raw",
+      "update_url": "/update-raw?queries=",
       "query_url": "/db-raw?queries=",
       "fortune_url": "/fortunes-raw",
       "port": 8080,

+ 46 - 1
frameworks/PHP/symfony2-stripped/src/Skamander/BenchmarkBundle/Controller/BenchController.php

@@ -11,6 +11,11 @@ use Skamander\BenchmarkBundle\Entity\Fortune;
 class BenchController extends Controller
 {
 
+    public function plaintextAction()
+    {
+      return new Response("Hello, World!", 200, array('Content-Type' => 'text/plain'));
+    }
+
     public function jsonAction()
     {
         return new JsonResponse(array('message' => 'Hello, World!'));
@@ -49,7 +54,7 @@ class BenchController extends Controller
         for($i = 0; $i < $queries; ++$i) {
             $worlds[] =  $conn->fetchAssoc('SELECT * FROM World WHERE id = ?', array(mt_rand(1, 10000)));
         }
-        
+
         if ($queries == 1) {
             $worlds = $worlds[0];
         }
@@ -57,6 +62,46 @@ class BenchController extends Controller
         return new JsonResponse($worlds);
     }
 
+    public function updateAction(Request $request)
+    {
+      $queries = $request->query->getInt('queries', 1);
+      $queries = min(500, max(1, $queries));
+
+      $worlds = array();
+      $em = $this->getDoctrine()->getManager();
+      $repo = $this->getDoctrine()
+          ->getRepository('SkamanderBenchmarkBundle:World');
+
+      for ($i = 0; $i < $queries; ++$i) {
+        $world = $repo->find(mt_rand(1, 10000));
+        $random_number = mt_rand(1, 10000);
+        $world->setRandomNumber($random_number);
+        $em->persist($world);
+        $worlds[] =  $world;
+      }
+
+      $em->flush();
+      return new JsonResponse($worlds);
+    }
+
+    public function updateRawAction(Request $request)
+    {
+      $queries = $request->query->getInt('queries', 1);
+      $queries = min(500, max(1, $queries));
+
+      $worlds = array();
+      $conn = $this->get('database_connection');
+
+      for($i = 0; $i < $queries; ++$i) {
+          $id = mt_rand(1, 10000);
+          $random_number = mt_rand(1, 10000);
+          $conn->executeUpdate('UPDATE World SET randomNumber=? WHERE id=?', array($random_number, $id));
+          $worlds[] =  array('id' => $id, 'randomNumber' => $random_number);
+      }
+
+      return new JsonResponse($worlds);
+    }
+
     public function fortunesAction()
     {
         $repo = $this->getDoctrine()

+ 13 - 1
frameworks/PHP/symfony2/app/config/routing.yml

@@ -1,3 +1,7 @@
+_plaintext:
+    pattern:  /plaintext
+    defaults: { _controller: SkamanderBenchmarkBundle:Bench:plaintext }
+
 _json:
     pattern:  /json
     defaults: { _controller: SkamanderBenchmarkBundle:Bench:json }
@@ -10,6 +14,14 @@ _dbRaw:
     pattern:  /db-raw
     defaults: { _controller: SkamanderBenchmarkBundle:Bench:dbRaw }
 
+_update:
+    pattern:  /update
+    defaults: { _controller: SkamanderBenchmarkBundle:Bench:update }
+
+_updateRaw:
+    pattern:  /update-raw
+    defaults: { _controller: SkamanderBenchmarkBundle:Bench:updateRaw }
+
 _fortunes:
     pattern:  /fortunes
     defaults: { _controller: SkamanderBenchmarkBundle:Bench:fortunes }
@@ -20,4 +32,4 @@ _fortunesPhp:
 
 _fortunesRaw:
     pattern:  /fortunes-raw
-    defaults: { _controller: SkamanderBenchmarkBundle:Bench:fortunesRaw }
+    defaults: { _controller: SkamanderBenchmarkBundle:Bench:fortunesRaw }

+ 5 - 0
frameworks/PHP/symfony2/benchmark_config.json

@@ -3,8 +3,10 @@
   "tests": [{
     "default": {
       "setup_file": "setup",
+      "plaintext_url": "/plaintext",
       "json_url": "/json",
       "db_url": "/db",
+      "update_url": "/update?queries=",
       "query_url": "/db?queries=",
       "fortune_url": "/fortunes",
       "port": 8080,
@@ -45,6 +47,7 @@
     "raw": {
       "setup_file": "setup",
       "db_url": "/db-raw",
+      "update_url": "/update-raw?queries=",
       "query_url": "/db-raw?queries=",
       "fortune_url": "/fortunes-raw",
       "port": 8080,
@@ -65,7 +68,9 @@
     },
     "hhvm": {
       "setup_file": "setup_hhvm",
+      "plaintext_url": "/plaintext",
       "json_url": "/json",
+      "update_url": "/update?queries=",
       "db_url": "/db",
       "query_url": "/db?queries=",
       "fortune_url": "/fortunes",

+ 46 - 1
frameworks/PHP/symfony2/src/Skamander/BenchmarkBundle/Controller/BenchController.php

@@ -11,6 +11,11 @@ use Skamander\BenchmarkBundle\Entity\Fortune;
 class BenchController extends Controller
 {
 
+    public function plaintextAction()
+    {
+      return new Response("Hello, World!", 200, array('Content-Type' => 'text/plain'));
+    }
+
     public function jsonAction()
     {
         return new JsonResponse(array('message' => 'Hello, World!'));
@@ -49,7 +54,7 @@ class BenchController extends Controller
         for($i = 0; $i < $queries; ++$i) {
             $worlds[] =  $conn->fetchAssoc('SELECT * FROM World WHERE id = ?', array(mt_rand(1, 10000)));
         }
-        
+
         if ($queries == 1) {
             $worlds = $worlds[0];
         }
@@ -57,6 +62,46 @@ class BenchController extends Controller
         return new JsonResponse($worlds);
     }
 
+    public function updateAction(Request $request)
+    {
+      $queries = $request->query->getInt('queries', 1);
+      $queries = min(500, max(1, $queries));
+
+      $worlds = array();
+      $em = $this->getDoctrine()->getManager();
+      $repo = $this->getDoctrine()
+          ->getRepository('SkamanderBenchmarkBundle:World');
+
+      for ($i = 0; $i < $queries; ++$i) {
+        $world = $repo->find(mt_rand(1, 10000));
+        $random_number = mt_rand(1, 10000);
+        $world->setRandomNumber($random_number);
+        $em->persist($world);
+        $worlds[] =  $world;
+      }
+
+      $em->flush();
+      return new JsonResponse($worlds);
+    }
+
+    public function updateRawAction(Request $request)
+    {
+      $queries = $request->query->getInt('queries', 1);
+      $queries = min(500, max(1, $queries));
+
+      $worlds = array();
+      $conn = $this->get('database_connection');
+
+      for($i = 0; $i < $queries; ++$i) {
+          $id = mt_rand(1, 10000);
+          $random_number = mt_rand(1, 10000);
+          $conn->executeUpdate('UPDATE World SET randomNumber=? WHERE id=?', array($random_number, $id));
+          $worlds[] =  array('id' => $id, 'randomNumber' => $random_number);
+      }
+
+      return new JsonResponse($worlds);
+    }
+
     public function fortunesAction()
     {
         $repo = $this->getDoctrine()

+ 37 - 0
frameworks/PHP/yaf/app/modules/Bench/controllers/Raw.php

@@ -5,6 +5,12 @@ use Yaf\Controller_Abstract as AbstractController;
 class RawController extends AbstractController
 {
 
+    public function plaintextAction ()
+    {
+      header('Content-Type: text/plain');
+      die("Hello, World!");
+    }
+
     public function jsonAction ()
     {
         header('Content-type: application/json');
@@ -43,6 +49,37 @@ class RawController extends AbstractController
         die(json_encode($arr));
     }
 
+    public function updatesAction ()
+    {
+      $dbh = DatabaseManager::getInstance()->getConnection();
+
+      $query_count = (int) $this->getRequest()->get('queries', 1);
+
+      if (0 >= $query_count) {
+          $query_count = 1;
+      } elseif (500 < $query_count) {
+          $query_count = 500;
+      }
+
+      $arr = array();
+      $id = mt_rand(1, 10000);
+      $random_number = mt_rand(1, 10000);
+
+      $statement = $dbh->prepare('UPDATE `World` SET `randomNumber` = :random_number WHERE `id` = :id');
+      $statement->bindParam(':id', $id, \PDO::PARAM_INT);
+      $statement->bindParam(':random_number', $random_number, \PDO::PARAM_INT);
+
+      while (0 < $query_count--) {
+          $statement->execute();
+          $arr[] = array('id' => $id, 'randomNumber' => $random_number);
+          $random_number = mt_rand(1, 10000);
+          $id = mt_rand(1, 10000);
+      }
+
+      header('Content-type: application/json');
+      die(json_encode($arr));
+    }
+
     public function fortunesAction ()
     {
         $view = $this->getView();

+ 2 - 0
frameworks/PHP/yaf/benchmark_config.json

@@ -3,8 +3,10 @@
   "tests": [{
     "default": {
       "setup_file": "setup",
+      "plaintext_url": "/bench/raw/plaintext",
       "json_url": "/bench/raw/json",
       "db_url": "/bench/raw/db",
+      "update_url": "/bench/raw/updates?queries=",
       "query_url": "/bench/raw/db?queries=",
       "fortune_url": "/bench/raw/fortunes",
       "port": 8080,

+ 3 - 1
frameworks/PHP/yii2/benchmark_config.json

@@ -3,6 +3,7 @@
   "tests": [{
     "default": {
       "setup_file": "setup",
+      "plaintext_url": "/site/plaintext",
       "json_url": "/site/json",
       "db_url": "/site/db",
       "query_url": "/site/db?queries=",
@@ -26,6 +27,7 @@
     },
     "hhvm": {
       "setup_file": "setup_hhvm",
+      "plaintext_url": "/site/plaintext",
       "json_url": "/site/json",
       "db_url": "/site/db",
       "query_url": "/site/db?queries=",
@@ -48,4 +50,4 @@
       "versus": "php5"
     }
   }]
-}
+}

+ 2 - 0
frameworks/PHP/zend/benchmark_config.json

@@ -3,8 +3,10 @@
   "tests": [{
     "default": {
       "setup_file": "setup",
+      "plaintext_url": "/plaintext",
       "json_url": "/json",
       "db_url": "/db",
+      "update_url": "/update?queries=",
       "query_url": "/db-multi?queries=",
       "port": 8080,
       "approach": "Realistic",

+ 37 - 1
frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/Controller/DbController.php → frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/Controller/BenchController.php

@@ -5,6 +5,7 @@ namespace FrameworkBenchmarks\Controller;
 use Zend\Mvc\Controller\AbstractActionController;
 use Zend\Stdlib\ArrayUtils;
 use Zend\View\Model\JsonModel;
+use Zend\Http\Headers;
 use Zend\Db\TableGateway\TableGateway;
 
 /**
@@ -13,7 +14,7 @@ use Zend\Db\TableGateway\TableGateway;
  * @author Marco Pivetta <[email protected]>
  * @link   http://www.techempower.com/benchmarks
  */
-class DbController extends AbstractActionController
+class BenchController extends AbstractActionController
 {
     /**
      * @var \Zend\Db\TableGateway\TableGateway
@@ -28,6 +29,11 @@ class DbController extends AbstractActionController
         $this->tableGateway = $tableGateway;
     }
 
+    public function jsonAction()
+    {
+        return new JsonModel(array('message' => 'Hello, World!'));
+    }
+
     /**
      * @return \Zend\View\Model\JsonModel
      */
@@ -63,4 +69,34 @@ class DbController extends AbstractActionController
 
         return new JsonModel($worlds);
     }
+
+    public function updateAction()
+    {
+      $request = $this->getRequest();
+      $queries = (int) $request->getQuery('queries', 1);
+      $queries = max(1, $queries);
+      $queries = min(500, $queries);
+
+      $worlds  = array();
+
+      for ($i = 0; $i < $queries; $i += 1) {
+          $id = mt_rand(1, 10000);
+          foreach ($this->tableGateway->select(array('id' => $id)) as $found) {
+              $random_number = mt_rand(1, 10000);
+              $found->randomNumber = $random_number;
+              $this->tableGateway->update(array('randomNumber' => $random_number), array('id' => $id));
+              $worlds[] = $found;
+          }
+      }
+
+      return new JsonModel($worlds);
+    }
+
+    public function plaintextAction()
+    {
+      $response = $this->getResponse();
+      $response->getHeaders()->addHeaders(array('COntent-Type' => 'text/plain'));
+      $response->setContent('Hello, World!');
+      return $response;
+    }
 }

+ 0 - 23
frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/Controller/JsonController.php

@@ -1,23 +0,0 @@
-<?php
-
-namespace FrameworkBenchmarks\Controller;
-
-use Zend\Mvc\Controller\AbstractActionController;
-use Zend\View\Model\JsonModel;
-
-/**
- * Controller that produces the `hello world` json for the benchmarks of FrameworkBenchmarks
- *
- * @author Marco Pivetta <[email protected]>
- * @link   http://www.techempower.com/benchmarks
- */
-class JsonController extends AbstractActionController
-{
-    /**
-     * @return \Zend\View\Model\JsonModel
-     */
-    public function indexAction()
-    {
-        return new JsonModel(array('message' => 'Hello, World!'));
-    }
-}

+ 26 - 9
frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/Module.php

@@ -18,13 +18,23 @@ class Module
         return array(
             'router' => array(
                 'routes' => array(
+                    'plaintext' => array(
+                      'type' => 'Zend\Mvc\Router\Http\Literal',
+                      'options' => array(
+                          'route' => '/plaintext',
+                          'defaults' => array(
+                              'controller' => 'FrameworkBenchmarks\Controller\BenchController',
+                              'action' => 'plaintext',
+                          ),
+                      ),
+                    ),
                     'json' => array(
                         'type' => 'Zend\Mvc\Router\Http\Literal',
                         'options' => array(
                             'route' => '/json',
                             'defaults' => array(
-                                'controller' => 'FrameworkBenchmarks\Controller\JsonController',
-                                'action' => 'index',
+                                'controller' => 'FrameworkBenchmarks\Controller\BenchController',
+                                'action' => 'json',
                             ),
                         ),
                     ),
@@ -33,7 +43,7 @@ class Module
                         'options' => array(
                             'route' => '/db',
                             'defaults' => array(
-                                'controller' => 'FrameworkBenchmarks\Controller\DbController',
+                                'controller' => 'FrameworkBenchmarks\Controller\BenchController',
                                 'action' => 'db',
                             ),
                         ),
@@ -43,11 +53,21 @@ class Module
                         'options' => array(
                             'route' => '/db-multi',
                             'defaults' => array(
-                                'controller' => 'FrameworkBenchmarks\Controller\DbController',
+                                'controller' => 'FrameworkBenchmarks\Controller\BenchController',
                                 'action' => 'db-multi',
                             ),
                         ),
                     ),
+                    'update' => array(
+                        'type' => 'Zend\Mvc\Router\Http\Literal',
+                        'options' => array(
+                            'route' => '/update',
+                            'defaults' => array(
+                                'controller' => 'FrameworkBenchmarks\Controller\BenchController',
+                                'action' => 'update',
+                            ),
+                        ),
+                    ),
                 ),
             ),
             'db' => array(
@@ -55,12 +75,9 @@ class Module
                 'dsn'    => 'mysql:dbname=hello_world;host=localhost',
             ),
             'controllers' => array(
-                'invokables' => array(
-                    'FrameworkBenchmarks\Controller\JsonController' => 'FrameworkBenchmarks\Controller\JsonController',
-                ),
                 'factories' => array(
-                    'FrameworkBenchmarks\Controller\DbController'
-                        => 'FrameworkBenchmarks\ServiceFactory\DbControllerServiceFactory'
+                    'FrameworkBenchmarks\Controller\BenchController'
+                        => 'FrameworkBenchmarks\ServiceFactory\BenchControllerServiceFactory'
                 ),
             ),
             'service_manager' => array(

+ 3 - 3
frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/ServiceFactory/DbControllerServiceFactory.php → frameworks/PHP/zend/module/FrameworkBenchmarks/src/FrameworkBenchmarks/ServiceFactory/BenchControllerServiceFactory.php

@@ -9,7 +9,7 @@
 
 namespace FrameworkBenchmarks\ServiceFactory;
 
-use FrameworkBenchmarks\Controller\DbController;
+use FrameworkBenchmarks\Controller\BenchController;
 use FrameworkBenchmarks\Entity\World;
 use Zend\Db\ResultSet\ResultSet;
 use Zend\Db\TableGateway\TableGateway;
@@ -22,7 +22,7 @@ use Zend\ServiceManager\ServiceLocatorInterface;
  * @author Marco Pivetta <[email protected]>
  * @link   http://www.techempower.com/benchmarks
  */
-class DbControllerServiceFactory implements FactoryInterface
+class BenchControllerServiceFactory implements FactoryInterface
 {
     /**
      * {@inheritDoc}
@@ -38,6 +38,6 @@ class DbControllerServiceFactory implements FactoryInterface
 
         $resultSetPrototype->setArrayObjectPrototype(new World());
 
-        return new DbController(new TableGateway('World', $dbAdapter, null, $resultSetPrototype));
+        return new BenchController(new TableGateway('World', $dbAdapter, null, $resultSetPrototype));
     }
 }

+ 15 - 0
frameworks/PHP/zend1/application/controllers/FortunesController.php

@@ -0,0 +1,15 @@
+<?php
+
+class FortunesController extends Zend_Controller_Action
+{
+    public function indexAction()
+    {
+        $table = new Model_Fortune();
+        $fortunes = $table->fetchAll()->toArray();
+        array_push($fortunes, array('id'=> 0, 'message' => 'Additional fortune added at request time.'));
+        usort($fortunes, function($left, $right) {
+            return strcmp($left['message'], $right['message']);
+        });
+        $this->view->fortunes = $fortunes;
+    }
+}

+ 12 - 0
frameworks/PHP/zend1/application/controllers/PlaintextController.php

@@ -0,0 +1,12 @@
+<?php
+
+class PlaintextController extends Zend_Controller_Action
+{
+    public function indexAction()
+    {
+      $this->_helper->viewRenderer->setNoRender(true);
+      $this->getResponse()
+           ->setHeader('Content-Type', 'text/plain')
+           ->appendBody('Hello, World!');
+    }
+}

+ 26 - 0
frameworks/PHP/zend1/application/controllers/UpdatesController.php

@@ -0,0 +1,26 @@
+<?php
+
+class UpdatesController extends Zend_Controller_Action
+{
+    public function indexAction()
+    {
+        $queries = (int) $this->getParam('queries', 1);
+        $queries = max(1, $queries);
+        $queries = min(500, $queries);
+
+        $table = new Model_World();
+
+        $worlds = array();
+        for ($i = 0; $i < $queries; $i += 1) {
+            $id = mt_rand(1, 10000);
+            $random_number = mt_rand(1, 10000);
+            $world = $table->fetchRow(array('id = ?' => $id))->toArray();
+            $world['randomNumber'] = $random_number;
+            $where = $table->getAdapter()->quoteInto('id = ?', $id);
+            $table->update(array('randomNumber' => $random_number), $where);
+            $worlds[] = $world;
+        }
+
+        $this->_helper->json->sendJson($worlds);
+    }
+}

+ 10 - 0
frameworks/PHP/zend1/application/models/Fortune.php

@@ -0,0 +1,10 @@
+<?php
+class Model_Fortune extends Zend_Db_Table_Abstract {
+	protected $_name = 'Fortune';
+
+	public function __construct() {
+		parent::__construct(array(
+			'rowClass' => 'Model_FortuneRow'
+		));
+	}
+}

+ 10 - 0
frameworks/PHP/zend1/application/models/FortuneRow.php

@@ -0,0 +1,10 @@
+<?php
+class Model_FortuneRow extends Zend_Db_Table_Row_Abstract {
+	public function __construct($config = array()) {
+		if ( array_key_exists('data', $config) ) {
+			$config['data']['id'] = (int) $config['data']['id'];
+			$config['data']['message'] = (int) $config['data']['message'];
+		}
+		parent::__construct($config);
+	}
+}

Some files were not shown because too many files changed in this diff