浏览代码

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

Damien Solimando 8 年之前
父节点
当前提交
cf9085a9c8
共有 100 个文件被更改,包括 1982 次插入928 次删除
  1. 19 0
      .github/PULL_REQUEST_TEMPLATE.md
  2. 3 0
      .gitignore
  3. 4 7
      .travis.yml
  4. 0 2
      config/create-postgres-database.sql
  5. 0 29
      config/create-postgres-old.sql
  6. 0 29
      config/create-postgres-upper-quote.sql
  7. 4 0
      config/create-postgres.sql
  8. 2 53
      config/create.sql
  9. 1 1
      config/travis_setup.sh
  10. 19 0
      config/upstart.example/tfb.conf
  11. 9 9
      deployment/vagrant-aws/README.md
  12. 6 2
      deployment/vagrant-aws/setup_aws.py
  13. 13 9
      frameworks/C++/cpoll_cppsp/benchmark_config.json
  14. 67 6
      frameworks/C++/cutelyst/benchmark_config.json
  15. 2 1
      frameworks/C++/cutelyst/config/config_socket.ini
  16. 3 3
      frameworks/C++/cutelyst/nginx.conf
  17. 4 4
      frameworks/C++/cutelyst/setup.sh
  18. 21 0
      frameworks/C++/cutelyst/setup_thread.sh
  19. 1 3
      frameworks/C++/cutelyst/setup_uwsgi_nginx.sh
  20. 12 4
      frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp
  21. 6 6
      frameworks/C++/cutelyst/src/databaseupdatestest.cpp
  22. 2 2
      frameworks/C++/cutelyst/src/fortunetest.cpp
  23. 4 4
      frameworks/C++/cutelyst/src/multipledatabasequeriestest.cpp
  24. 4 4
      frameworks/C++/cutelyst/src/singledatabasequerytest.cpp
  25. 188 4
      frameworks/C++/ffead-cpp/benchmark_config.json
  26. 16 0
      frameworks/C++/ffead-cpp/setup-apache2-mysql.sh
  27. 16 0
      frameworks/C++/ffead-cpp/setup-apache2-postgresql.sh
  28. 16 0
      frameworks/C++/ffead-cpp/setup-apache2.sh
  29. 16 0
      frameworks/C++/ffead-cpp/setup-mysql.sh
  30. 16 0
      frameworks/C++/ffead-cpp/setup-nginx-mysql.sh
  31. 15 0
      frameworks/C++/ffead-cpp/setup-nginx-postgresql.sh
  32. 15 0
      frameworks/C++/ffead-cpp/setup-nginx.sh
  33. 16 0
      frameworks/C++/ffead-cpp/setup-postgresql.sh
  34. 4 0
      frameworks/C++/ffead-cpp/setup.sh
  35. 5 5
      frameworks/C++/poco/benchmark_config.json
  36. 1 1
      frameworks/C++/silicon/CMakeLists.txt
  37. 9 6
      frameworks/C++/silicon/benchmark_config.json
  38. 1 1
      frameworks/C++/silicon/setup_lwan_mysql.sh
  39. 1 1
      frameworks/C++/silicon/setup_mhd_epoll_mysql.sh
  40. 1 1
      frameworks/C++/silicon/setup_mhd_tpc_mysql.sh
  41. 8 6
      frameworks/C++/treefrog/benchmark_config.json
  42. 6 80
      frameworks/C++/ulib/benchmark_config.json
  43. 4 2
      frameworks/C++/ulib/setup_json.sh
  44. 8 4
      frameworks/C++/wt/benchmark_config.json
  45. 5 4
      frameworks/C/duda/benchmark_config.json
  46. 0 1
      frameworks/C/h2o/CMakeLists.txt
  47. 5 4
      frameworks/C/h2o/benchmark_config.json
  48. 1 1
      frameworks/C/h2o/setup.sh
  49. 4 5
      frameworks/C/h2o/src/database.c
  50. 11 14
      frameworks/C/h2o/src/event_loop.c
  51. 2 2
      frameworks/C/h2o/src/event_loop.h
  52. 25 40
      frameworks/C/h2o/src/fortune.c
  53. 25 10
      frameworks/C/h2o/src/main.c
  54. 38 53
      frameworks/C/h2o/src/request_handler.c
  55. 5 2
      frameworks/C/h2o/src/request_handler.h
  56. 8 2
      frameworks/C/h2o/src/template.c
  57. 49 31
      frameworks/C/h2o/src/thread.c
  58. 20 12
      frameworks/C/h2o/src/thread.h
  59. 3 3
      frameworks/C/h2o/src/tls.c
  60. 1 1
      frameworks/C/h2o/src/tls.h
  61. 6 3
      frameworks/C/h2o/src/utility.h
  62. 68 85
      frameworks/C/h2o/src/world.c
  63. 4 3
      frameworks/C/haywire/benchmark_config.json
  64. 3 2
      frameworks/C/libreactor/benchmark_config.json
  65. 12 9
      frameworks/C/lwan/benchmark_config.json
  66. 8 6
      frameworks/C/onion/benchmark_config.json
  67. 25 21
      frameworks/CSharp/HttpListener/benchmark_config.json
  68. 25 20
      frameworks/CSharp/aspnet-stripped/benchmark_config.json
  69. 98 80
      frameworks/CSharp/aspnet/benchmark_config.json
  70. 1 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/AppSettings.cs
  71. 12 0
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/DatabaseServer.cs
  72. 30 12
      frameworks/CSharp/aspnetcore/Benchmarks/Configuration/Scenarios.cs
  73. 11 51
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/FortunesController.cs
  74. 42 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleQueriesController.cs
  75. 42 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/MultipleUpdatesController.cs
  76. 41 0
      frameworks/CSharp/aspnetcore/Benchmarks/Controllers/SingleQueryController.cs
  77. 49 2
      frameworks/CSharp/aspnetcore/Benchmarks/Data/ApplicationDbContext.cs
  78. 24 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/BatchUpdateString.cs
  79. 50 12
      frameworks/CSharp/aspnetcore/Benchmarks/Data/DapperDb.cs
  80. 27 3
      frameworks/CSharp/aspnetcore/Benchmarks/Data/EfDb.cs
  81. 4 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/Fortune.cs
  82. 19 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/IDb.cs
  83. 43 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/NoTransactionSqlServerConnection.cs
  84. 6 3
      frameworks/CSharp/aspnetcore/Benchmarks/Data/Random.cs
  85. 89 39
      frameworks/CSharp/aspnetcore/Benchmarks/Data/RawDb.cs
  86. 5 0
      frameworks/CSharp/aspnetcore/Benchmarks/Data/World.cs
  87. 0 47
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/ErrorHandlerMiddleware.cs
  88. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/FortunesEfMiddleware.cs
  89. 14 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MiddlewareHelpers.cs
  90. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleQueriesEfMiddleware.cs
  91. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesDapperMiddleware.cs
  92. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesEfMiddleware.cs
  93. 61 0
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MultipleUpdatesRawMiddleware.cs
  94. 5 5
      frameworks/CSharp/aspnetcore/Benchmarks/Middleware/SingleQueryEfMiddleware.cs
  95. 2 2
      frameworks/CSharp/aspnetcore/Benchmarks/NuGet.Config
  96. 41 8
      frameworks/CSharp/aspnetcore/Benchmarks/Startup.cs
  97. 0 3
      frameworks/CSharp/aspnetcore/Benchmarks/appsettings.json
  98. 4 0
      frameworks/CSharp/aspnetcore/Benchmarks/appsettings.postgresql.json
  99. 21 10
      frameworks/CSharp/aspnetcore/Benchmarks/project.json
  100. 188 13
      frameworks/CSharp/aspnetcore/benchmark_config.json

+ 19 - 0
.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,19 @@
+<!--
+....................................
+
+MAKE SURE YOU ARE OPENING A PULL
+REQUEST AGAINST THE CORRECT BRANCH
+
+....................................
+
+master = currently not accepting pull
+requests as we prepare for our Round 13
+final release.
+
+round-14 = new features, frameworks, 
+tests, bug fixes, and any other 
+changes that you would like to see in 
+the next round.
+
+....................................
+-->

+ 3 - 0
.gitignore

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

+ 4 - 7
.travis.yml

@@ -55,7 +55,6 @@ env:
     - "TESTDIR=Erlang/cowboy"
     - "TESTDIR=Erlang/cowboy"
     - "TESTDIR=Erlang/elli"
     - "TESTDIR=Erlang/elli"
     - "TESTDIR=Erlang/mochiweb"
     - "TESTDIR=Erlang/mochiweb"
-    - "TESTDIR=Erlang/misultin"
     - "TESTDIR=Go/beego"
     - "TESTDIR=Go/beego"
     - "TESTDIR=Go/echo"
     - "TESTDIR=Go/echo"
     - "TESTDIR=Go/falcore"
     - "TESTDIR=Go/falcore"
@@ -98,11 +97,9 @@ env:
     - "TESTDIR=Java/servlet"
     - "TESTDIR=Java/servlet"
     - "TESTDIR=Java/servlet3-cass"
     - "TESTDIR=Java/servlet3-cass"
     - "TESTDIR=Java/spark"
     - "TESTDIR=Java/spark"
-    - "TESTDIR=Java/sabina"
     - "TESTDIR=Java/spring"
     - "TESTDIR=Java/spring"
     - "TESTDIR=Java/tapestry"
     - "TESTDIR=Java/tapestry"
     - "TESTDIR=Java/undertow"
     - "TESTDIR=Java/undertow"
-    - "TESTDIR=Java/undertow-edge"
     - "TESTDIR=Java/undertow-jersey-c3p0"
     - "TESTDIR=Java/undertow-jersey-c3p0"
     - "TESTDIR=Java/undertow-jersey-hikaricp"
     - "TESTDIR=Java/undertow-jersey-hikaricp"
     - "TESTDIR=Java/vertx"
     - "TESTDIR=Java/vertx"
@@ -119,6 +116,7 @@ env:
     - "TESTDIR=JavaScript/sailsjs"
     - "TESTDIR=JavaScript/sailsjs"
     - "TESTDIR=Kotlin/hexagon"
     - "TESTDIR=Kotlin/hexagon"
     - "TESTDIR=Lua/lapis"
     - "TESTDIR=Lua/lapis"
+    - "TESTDIR=Lua/octopus"
     - "TESTDIR=Lua/openresty"
     - "TESTDIR=Lua/openresty"
     - "TESTDIR=Nim/jester"
     - "TESTDIR=Nim/jester"
     - "TESTDIR=Nim/nawak"
     - "TESTDIR=Nim/nawak"
@@ -130,7 +128,7 @@ env:
     - "TESTDIR=PHP/cakephp"
     - "TESTDIR=PHP/cakephp"
     - "TESTDIR=PHP/hhvm"
     - "TESTDIR=PHP/hhvm"
     - "TESTDIR=PHP/php"
     - "TESTDIR=PHP/php"
-    - "TESTDIR=PHP/cygnite-php-framework"
+    - "TESTDIR=PHP/cygnite"
     - "TESTDIR=PHP/codeigniter"
     - "TESTDIR=PHP/codeigniter"
     - "TESTDIR=PHP/clancats"
     - "TESTDIR=PHP/clancats"
     - "TESTDIR=PHP/fat-free"
     - "TESTDIR=PHP/fat-free"
@@ -140,7 +138,6 @@ env:
     - "TESTDIR=PHP/limonade"
     - "TESTDIR=PHP/limonade"
     - "TESTDIR=PHP/lithium"
     - "TESTDIR=PHP/lithium"
     - "TESTDIR=PHP/lumen"
     - "TESTDIR=PHP/lumen"
-    - "TESTDIR=PHP/micromvc"
     - "TESTDIR=PHP/phalcon"
     - "TESTDIR=PHP/phalcon"
     - "TESTDIR=PHP/phalcon-micro"
     - "TESTDIR=PHP/phalcon-micro"
     - "TESTDIR=PHP/phpixie"
     - "TESTDIR=PHP/phpixie"
@@ -168,6 +165,7 @@ env:
     - "TESTDIR=Python/turbogears"
     - "TESTDIR=Python/turbogears"
     - "TESTDIR=Python/uwsgi"
     - "TESTDIR=Python/uwsgi"
     - "TESTDIR=Python/web2py"
     - "TESTDIR=Python/web2py"
+    - "TESTDIR=Python/weppy"
     - "TESTDIR=Python/wheezyweb"
     - "TESTDIR=Python/wheezyweb"
     - "TESTDIR=Python/wsgi"
     - "TESTDIR=Python/wsgi"
     - "TESTDIR=Racket/racket-ws"
     - "TESTDIR=Racket/racket-ws"
@@ -188,13 +186,11 @@ env:
     - "TESTDIR=Scala/finatra"
     - "TESTDIR=Scala/finatra"
     - "TESTDIR=Scala/fintrospect"
     - "TESTDIR=Scala/fintrospect"
     - "TESTDIR=Scala/lift-stateless"
     - "TESTDIR=Scala/lift-stateless"
-    - "TESTDIR=Scala/plain"
     - "TESTDIR=Scala/play2-scala"
     - "TESTDIR=Scala/play2-scala"
     - "TESTDIR=Scala/scalatra"
     - "TESTDIR=Scala/scalatra"
     - "TESTDIR=Scala/scruffy"
     - "TESTDIR=Scala/scruffy"
     - "TESTDIR=Scala/spray"
     - "TESTDIR=Scala/spray"
     - "TESTDIR=Scala/s-server"
     - "TESTDIR=Scala/s-server"
-    - "TESTDIR=Scala/spray-es"
     - "TESTDIR=Scala/unfiltered"
     - "TESTDIR=Scala/unfiltered"
     - "TESTDIR=Scala/http4s"
     - "TESTDIR=Scala/http4s"
     - "TESTDIR=Scala/finch"
     - "TESTDIR=Scala/finch"
@@ -212,6 +208,7 @@ before_install:
 services:
 services:
   - postgresql
   - postgresql
   - redis-server
   - redis-server
+  - mongodb
 
 
 addons:
 addons:
   postgresql: "9.3"
   postgresql: "9.3"

+ 0 - 2
config/create-postgres-database.sql

@@ -2,5 +2,3 @@ CREATE USER benchmarkdbuser WITH PASSWORD 'benchmarkdbpass';
 
 
 DROP DATABASE IF EXISTS hello_world;
 DROP DATABASE IF EXISTS hello_world;
 CREATE DATABASE hello_world WITH ENCODING 'UTF8';
 CREATE DATABASE hello_world WITH ENCODING 'UTF8';
-
-GRANT ALL PRIVILEGES ON DATABASE hello_world to benchmarkdbuser;

+ 0 - 29
config/create-postgres-old.sql

@@ -1,29 +0,0 @@
-DROP TABLE IF EXISTS World;
-CREATE TABLE  "World" (
-  id integer NOT NULL,
-  randomNumber integer NOT NULL default 0,
-  PRIMARY KEY  (id)
-);
-
-INSERT INTO "World" (id, randomNumber)
-SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
-
-DROP TABLE IF EXISTS Fortune;
-CREATE TABLE  "Fortune" (
-  id integer NOT NULL,
-  message varchar(2048) NOT NULL,
-  PRIMARY KEY  (id)
-);
-
-INSERT INTO fortune (id, message) VALUES (1, 'fortune: No such file or directory');
-INSERT INTO fortune (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
-INSERT INTO fortune (id, message) VALUES (3, 'After enough decimal places, nobody gives a damn.');
-INSERT INTO fortune (id, message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
-INSERT INTO fortune (id, message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.');
-INSERT INTO fortune (id, message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
-INSERT INTO fortune (id, message) VALUES (7, 'Any program that runs right is obsolete.');
-INSERT INTO fortune (id, message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth');
-INSERT INTO fortune (id, message) VALUES (9, 'Feature: A bug with seniority.');
-INSERT INTO fortune (id, message) VALUES (10, 'Computers make very fast, very accurate mistakes.');
-INSERT INTO fortune (id, message) VALUES (11, '<script>alert("This should not be displayed in a browser alert box.");</script>');
-INSERT INTO fortune (id, message) VALUES (12, 'フレームワークのベンチマーク');

+ 0 - 29
config/create-postgres-upper-quote.sql

@@ -1,29 +0,0 @@
-DROP TABLE IF EXISTS "World";
-CREATE TABLE  "World" (
-  id integer NOT NULL,
-  randomNumber integer NOT NULL default 0,
-  PRIMARY KEY  (id)
-);
-
-INSERT INTO "World" (id, randomNumber)
-SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
-
-DROP TABLE IF EXISTS "Fortune";
-CREATE TABLE "Fortune" (
-  id integer NOT NULL,
-  message varchar(2048) NOT NULL,
-  PRIMARY KEY  (id)
-);
-
-INSERT INTO "Fortune" (id, message) VALUES (1, 'fortune: No such file or directory');
-INSERT INTO "Fortune" (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
-INSERT INTO "Fortune" (id, message) VALUES (3, 'After enough decimal places, nobody gives a damn.');
-INSERT INTO "Fortune" (id, message) VALUES (4, 'A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
-INSERT INTO "Fortune" (id, message) VALUES (5, 'A computer program does what you tell it to do, not what you want it to do.');
-INSERT INTO "Fortune" (id, message) VALUES (6, 'Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
-INSERT INTO "Fortune" (id, message) VALUES (7, 'Any program that runs right is obsolete.');
-INSERT INTO "Fortune" (id, message) VALUES (8, 'A list is only as strong as its weakest link. — Donald Knuth');
-INSERT INTO "Fortune" (id, message) VALUES (9, 'Feature: A bug with seniority.');
-INSERT INTO "Fortune" (id, message) VALUES (10, 'Computers make very fast, very accurate mistakes.');
-INSERT INTO "Fortune" (id, message) VALUES (11, '<script>alert("This should not be displayed in a browser alert box.");</script>');
-INSERT INTO "Fortune" (id, message) VALUES (12, 'フレームワークのベンチマーク');

+ 4 - 0
config/create-postgres.sql

@@ -5,6 +5,7 @@ CREATE TABLE  World (
   randomNumber integer NOT NULL default 0,
   randomNumber integer NOT NULL default 0,
   PRIMARY KEY  (id)
   PRIMARY KEY  (id)
 );
 );
+GRANT SELECT, UPDATE ON World to benchmarkdbuser;
 
 
 INSERT INTO World (id, randomnumber)
 INSERT INTO World (id, randomnumber)
 SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
 SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
@@ -15,6 +16,7 @@ CREATE TABLE Fortune (
   message varchar(2048) NOT NULL,
   message varchar(2048) NOT NULL,
   PRIMARY KEY  (id)
   PRIMARY KEY  (id)
 );
 );
+GRANT SELECT ON Fortune to benchmarkdbuser;
 
 
 INSERT INTO Fortune (id, message) VALUES (1, 'fortune: No such file or directory');
 INSERT INTO Fortune (id, message) VALUES (1, 'fortune: No such file or directory');
 INSERT INTO Fortune (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
 INSERT INTO Fortune (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
@@ -36,6 +38,7 @@ CREATE TABLE  "World" (
   randomNumber integer NOT NULL default 0,
   randomNumber integer NOT NULL default 0,
   PRIMARY KEY  (id)
   PRIMARY KEY  (id)
 );
 );
+GRANT SELECT, UPDATE ON "World" to benchmarkdbuser;
 
 
 INSERT INTO "World" (id, randomnumber)
 INSERT INTO "World" (id, randomnumber)
 SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
 SELECT x.id, random() * 10000 + 1 FROM generate_series(1,10000) as x(id);
@@ -46,6 +49,7 @@ CREATE TABLE "Fortune" (
   message varchar(2048) NOT NULL,
   message varchar(2048) NOT NULL,
   PRIMARY KEY  (id)
   PRIMARY KEY  (id)
 );
 );
+GRANT SELECT ON "Fortune" to benchmarkdbuser;
 
 
 INSERT INTO "Fortune" (id, message) VALUES (1, 'fortune: No such file or directory');
 INSERT INTO "Fortune" (id, message) VALUES (1, 'fortune: No such file or directory');
 INSERT INTO "Fortune" (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');
 INSERT INTO "Fortune" (id, message) VALUES (2, 'A computer scientist is someone who fixes things that aren''t broken.');

+ 2 - 53
config/create.sql

@@ -3,59 +3,6 @@ DROP DATABASE IF EXISTS hello_world;
 CREATE DATABASE hello_world;
 CREATE DATABASE hello_world;
 USE hello_world;
 USE hello_world;
 
 
-DROP TABLE IF EXISTS World;
-CREATE TABLE  World (
-  id int(10) unsigned NOT NULL auto_increment,
-  randomNumber int NOT NULL default 0,
-  PRIMARY KEY  (id)
-)
-ENGINE=INNODB;
-GRANT ALL ON hello_world.world TO 'benchmarkdbuser'@'%' IDENTIFIED BY 'benchmarkdbpass';
-GRANT ALL ON hello_world.world TO 'benchmarkdbuser'@'localhost' IDENTIFIED BY 'benchmarkdbpass';
-
-DROP PROCEDURE IF EXISTS load_data;
-
-DELIMITER #
-CREATE PROCEDURE load_data()
-BEGIN
-
-declare v_max int unsigned default 10000;
-declare v_counter int unsigned default 0;
-
-  TRUNCATE TABLE World;
-  START TRANSACTION;
-  while v_counter < v_max do
-    INSERT INTO World (randomNumber) VALUES ( floor(0 + (rand() * 10000)) );
-    SET v_counter=v_counter+1;
-  end while;
-  commit;
-END #
-
-DELIMITER ;
-
-CALL load_data();
-
-DROP TABLE IF EXISTS Fortune;
-CREATE TABLE  Fortune (
-  id int(10) unsigned NOT NULL auto_increment,
-  message varchar(2048) CHARACTER SET 'utf8' NOT NULL,
-  PRIMARY KEY  (id)
-)
-ENGINE=INNODB;
-
-INSERT INTO Fortune (message) VALUES ('fortune: No such file or directory');
-INSERT INTO Fortune (message) VALUES ('A computer scientist is someone who fixes things that aren''t broken.');
-INSERT INTO Fortune (message) VALUES ('After enough decimal places, nobody gives a damn.');
-INSERT INTO Fortune (message) VALUES ('A bad random number generator: 1, 1, 1, 1, 1, 4.33e+67, 1, 1, 1');
-INSERT INTO Fortune (message) VALUES ('A computer program does what you tell it to do, not what you want it to do.');
-INSERT INTO Fortune (message) VALUES ('Emacs is a nice operating system, but I prefer UNIX. — Tom Christaensen');
-INSERT INTO Fortune (message) VALUES ('Any program that runs right is obsolete.');
-INSERT INTO Fortune (message) VALUES ('A list is only as strong as its weakest link. — Donald Knuth');
-INSERT INTO Fortune (message) VALUES ('Feature: A bug with seniority.');
-INSERT INTO Fortune (message) VALUES ('Computers make very fast, very accurate mistakes.');
-INSERT INTO Fortune (message) VALUES ('<script>alert("This should not be displayed in a browser alert box.");</script>');
-INSERT INTO Fortune (message) VALUES ('フレームワークのベンチマーク');
-
 DROP TABLE IF EXISTS world;
 DROP TABLE IF EXISTS world;
 CREATE TABLE  world (
 CREATE TABLE  world (
   id int(10) unsigned NOT NULL auto_increment,
   id int(10) unsigned NOT NULL auto_increment,
@@ -63,6 +10,8 @@ CREATE TABLE  world (
   PRIMARY KEY  (id)
   PRIMARY KEY  (id)
 )
 )
 ENGINE=INNODB;
 ENGINE=INNODB;
+GRANT SELECT, UPDATE ON hello_world.world TO 'benchmarkdbuser'@'%' IDENTIFIED BY 'benchmarkdbpass';
+GRANT SELECT, UPDATE ON hello_world.world TO 'benchmarkdbuser'@'localhost' IDENTIFIED BY 'benchmarkdbpass';
 
 
 DROP PROCEDURE IF EXISTS load_data;
 DROP PROCEDURE IF EXISTS load_data;
 
 

+ 1 - 1
config/travis_setup.sh

@@ -83,7 +83,7 @@ echo "Populating Postgres database"
 psql --version
 psql --version
 sudo useradd benchmarkdbuser -p benchmarkdbpass
 sudo useradd benchmarkdbuser -p benchmarkdbpass
 sudo -u postgres psql template1 < config/create-postgres-database.sql
 sudo -u postgres psql template1 < config/create-postgres-database.sql
-sudo -u benchmarkdbuser psql hello_world < config/create-postgres.sql
+sudo -u postgres psql hello_world < config/create-postgres.sql
 sudo sed -i "s|#listen_addresses = 'localhost'|listen_addresses = '*'|g" /etc/postgresql/9.3/main/postgresql.conf
 sudo sed -i "s|#listen_addresses = 'localhost'|listen_addresses = '*'|g" /etc/postgresql/9.3/main/postgresql.conf
 sudo sed -i 's|max_connections = 255|max_connections = 500|g' /etc/postgresql/9.3/main/postgresql.conf
 sudo sed -i 's|max_connections = 255|max_connections = 500|g' /etc/postgresql/9.3/main/postgresql.conf
 sudo service postgresql stop
 sudo service postgresql stop

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

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

+ 9 - 9
deployment/vagrant-aws/README.md

@@ -17,8 +17,7 @@ production setup costs about TODO.
 
 
 ## Prerequisites
 ## Prerequisites
 
 
-* **A recent version of Vagrant**, like 1.6.3 (NOTE: `apt-get` is 
-too old, download the newest `deb` directly). See 
+* **A recent version of Vagrant**, like 1.6.3. See 
 [here](https://www.vagrantup.com/downloads.html) for downloads
 [here](https://www.vagrantup.com/downloads.html) for downloads
 
 
 * **Vagrant AWS Plugin** from [here](https://github.com/mitchellh/vagrant-aws)
 * **Vagrant AWS Plugin** from [here](https://github.com/mitchellh/vagrant-aws)
@@ -29,12 +28,13 @@ too old, download the newest `deb` directly). See
 
 
 ## Using Vagrant to Run Amazon-powered Virtual Machine
 ## Using Vagrant to Run Amazon-powered Virtual Machine
 
 
-The high level steps are 
-1) clone this project 
-2) set environment variables allowing us to log into your amazon account
-3) Run amazon setup script to create network
-4) Run `vagrant up --provider=aws` to launch into amazon
-5) Run `vagrant ssh` to log into the application server
+The high level steps are:
+
+* clone this project 
+* set environment variables allowing us to log into your amazon account
+* Run amazon setup script to create network
+* Run `vagrant up --provider=aws` to launch into amazon
+* Run `vagrant ssh` to log into the application server
 
 
 By default, your local git clone of this project will not by synced with the 
 By default, your local git clone of this project will not by synced with the 
 remote amazon machines.
 remote amazon machines.
@@ -189,7 +189,7 @@ simultaneous benchmarks, so the better approach is to just increase all
 the IP addresses by 3 and run the additional benchmarks in the same VPC. 
 the IP addresses by 3 and run the additional benchmarks in the same VPC. 
 
 
 
 
-**I'm getting an AuthFailure but my Credientials are Correct!**:
+**I'm getting an AuthFailure but my Credentials are Correct!**:
 
 
 This normally means the AMI has been moved from public to private. Ubuntu's 
 This normally means the AMI has been moved from public to private. Ubuntu's 
 Cloud image team occasionally does this. Navigate [here](http://cloud-images.ubuntu.com/trusty/current/) and find a more current AMI. 
 Cloud image team occasionally does this. Navigate [here](http://cloud-images.ubuntu.com/trusty/current/) and find a more current AMI. 

+ 6 - 2
deployment/vagrant-aws/setup_aws.py

@@ -101,8 +101,12 @@ def run_aws(command, prefix=True, load=True):
   log.debug("Request : %s", command)
   log.debug("Request : %s", command)
   result = subprocess.check_output(command, shell=True)
   result = subprocess.check_output(command, shell=True)
   log.debug("Response: %s", result)
   log.debug("Response: %s", result)
-  if load:
-    return json.loads(result)
+  if load and result != '':
+    try:
+      return json.loads(result)
+    except ValueError:
+      log.error("Could not parse result '%s' as JSON for command '%s'", result, command)
+      raise
   else:
   else:
     return result
     return result
 
 

+ 13 - 9
frameworks/C++/cpoll_cppsp/benchmark_config.json

@@ -7,18 +7,19 @@
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 16969,
       "port": 16969,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Micro",
+      "classification": "Platform",
       "database": "None",
       "database": "None",
       "framework": "cpoll-cppsp",
       "framework": "cpoll-cppsp",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "CPoll",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-cppsp",
       "display_name": "cpoll-cppsp",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "cpoll_cppsp"
     },
     },
     "raw": {
     "raw": {
       "setup_file": "setup",
       "setup_file": "setup",
@@ -32,14 +33,15 @@
       "database": "MySQL",
       "database": "MySQL",
       "framework": "cpoll-cppsp",
       "framework": "cpoll-cppsp",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "CPoll",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-cppsp-raw",
       "display_name": "cpoll-cppsp-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "cpoll_cppsp"
     },
     },
     "postgres-raw": {
     "postgres-raw": {
       "setup_file": "setup",
       "setup_file": "setup",
@@ -51,14 +53,15 @@
       "database": "Postgres",
       "database": "Postgres",
       "framework": "cpoll-cppsp",
       "framework": "cpoll-cppsp",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "CPoll",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-cppsp-raw",
       "display_name": "cpoll-cppsp-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "cpoll_cppsp"
     },
     },
     "postgres-raw-threadpool": {
     "postgres-raw-threadpool": {
       "setup_file": "setup",
       "setup_file": "setup",
@@ -70,14 +73,15 @@
       "database": "Postgres",
       "database": "Postgres",
       "framework": "cpoll-cppsp",
       "framework": "cpoll-cppsp",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "CPoll",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-pool",
       "display_name": "cpoll-pool",
       "notes": "Threadpool",
       "notes": "Threadpool",
-      "versus": ""
+      "versus": "cpoll_cppsp"
     }
     }
   }]
   }]
 }
 }

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

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

+ 2 - 1
frameworks/C++/cutelyst/config/config_socket.ini

@@ -3,9 +3,10 @@ master
 ; Increase listen queue used for nginx connecting to uWSGI. This matches
 ; Increase listen queue used for nginx connecting to uWSGI. This matches
 ; net.ipv4.tcp_max_syn_backlog and net.core.somaxconn.
 ; net.ipv4.tcp_max_syn_backlog and net.core.somaxconn.
 ; for performance
 ; for performance
+listen = 65535
 disable-logging
 disable-logging
 ; use UNIX sockets instead of TCP loopback for performance
 ; use UNIX sockets instead of TCP loopback for performance
-socket = /tmp/uwsgi.sock
+socket = /var/tmp/uwsgi.sock
 ; allow nginx to access the UNIX socket
 ; allow nginx to access the UNIX socket
 chmod-socket = 666
 chmod-socket = 666
 ; Avoid thundering herd problem http://uwsgi-docs.readthedocs.org/en/latest/articles/SerializingAccept.html .
 ; Avoid thundering herd problem http://uwsgi-docs.readthedocs.org/en/latest/articles/SerializingAccept.html .

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

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

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

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

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

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

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

@@ -9,17 +9,15 @@ sed -i 's|include .*/conf/uwsgi_params;|include '"${NGINX_HOME}"'/conf/uwsgi_par
 cd $IROOT
 cd $IROOT
 mkdir cutelyst-benchmarks || true
 mkdir cutelyst-benchmarks || true
 cd cutelyst-benchmarks
 cd cutelyst-benchmarks
-rm -rf *
 
 
 QT_VERSION_MM=56
 QT_VERSION_MM=56
 export CMAKE_PREFIX_PATH=/opt/qt${QT_VERSION_MM}:${IROOT}
 export CMAKE_PREFIX_PATH=/opt/qt${QT_VERSION_MM}:${IROOT}
 
 
 cmake $TROOT -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$IROOT
 cmake $TROOT -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$IROOT
 
 
-make clean
 make -j $MAX_THREADS
 make -j $MAX_THREADS
 
 
 nginx -c $TROOT/nginx.conf
 nginx -c $TROOT/nginx.conf
 
 
 export LD_LIBRARY_PATH=/opt/qt${QT_VERSION_MM}/lib:${IROOT}/lib/x86_64-linux-gnu/
 export LD_LIBRARY_PATH=/opt/qt${QT_VERSION_MM}/lib:${IROOT}/lib/x86_64-linux-gnu/
-uwsgi --ini ${TROOT}/config/config_socket.ini --cutelyst-app ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $(( $MAX_THREADS * 2 )) &
+uwsgi --ini ${TROOT}/config/config_socket.ini --cutelyst-app ${IROOT}/cutelyst-benchmarks/src/libcutelyst_benchmarks.so -p $MAX_THREADS &

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

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

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

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

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

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

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

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

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

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

+ 188 - 4
frameworks/C++/ffead-cpp/benchmark_config.json

@@ -11,19 +11,203 @@
       "update_url": "/te-benchmark/updates?queries=",
       "update_url": "/te-benchmark/updates?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "micro",
       "database": "MongoDB",
       "database": "MongoDB",
       "framework": "ffead-cpp",
       "framework": "ffead-cpp",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
+      "orm": "Full",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "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",
       "orm": "Full",
       "platform": "ffead-cpp",
       "platform": "ffead-cpp",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "ffead-cpp",
+      "display_name": "ffead-cpp-postgresql",
       "notes": "",
       "notes": "",
       "versus": ""
       "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
 echo $LD_LIBRARY_PATH
 rm -f $FFEAD_CPP_PATH/*.cntrl
 rm -f $FFEAD_CPP_PATH/*.cntrl
 rm -f $FFEAD_CPP_PATH/tmp/*.sess
 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
 $TROOT/ffead-cpp-2.0/CHS $FFEAD_CPP_PATH > ffead.log 2>&1
 
 

+ 5 - 5
frameworks/C++/poco/benchmark_config.json

@@ -8,17 +8,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
       "database": "none",
       "database": "none",
-      "framework": "POCO",
+      "framework": "None",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "POCO",
-      "webserver": "poco",
+      "platform": "None",
+      "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "POCO",
       "display_name": "POCO",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "poco"
     }
     }
   }]
   }]
 }
 }
-

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

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

+ 9 - 6
frameworks/C++/silicon/benchmark_config.json

@@ -15,14 +15,15 @@
       "database": "MySQL",
       "database": "MySQL",
       "framework": "silicon",
       "framework": "silicon",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Full",
       "orm": "Full",
-      "platform": "Silicon",
+      "platform": "None",
       "webserver": "microhttpd",
       "webserver": "microhttpd",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "silicon-tpc-mysql",
       "display_name": "silicon-tpc-mysql",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "silicon"
     },
     },
     "epoll-mysql": {
     "epoll-mysql": {
       "setup_file": "setup_mhd_epoll_mysql",
       "setup_file": "setup_mhd_epoll_mysql",
@@ -38,14 +39,15 @@
       "database": "MySQL",
       "database": "MySQL",
       "framework": "silicon",
       "framework": "silicon",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Full",
       "orm": "Full",
-      "platform": "Silicon",
+      "platform": "None",
       "webserver": "microhttpd",
       "webserver": "microhttpd",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "silicon-epoll-mysql",
       "display_name": "silicon-epoll-mysql",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "silicon"
     },
     },
     "lwan-mysql": {
     "lwan-mysql": {
       "setup_file": "setup_lwan_mysql",
       "setup_file": "setup_lwan_mysql",
@@ -61,14 +63,15 @@
       "database": "MySQL",
       "database": "MySQL",
       "framework": "silicon",
       "framework": "silicon",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Full",
       "orm": "Full",
-      "platform": "Silicon",
+      "platform": "None",
       "webserver": "Lwan",
       "webserver": "Lwan",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "silicon-lwan-mysql",
       "display_name": "silicon-lwan-mysql",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "silicon"
     }
     }
     
     
   }]
   }]

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

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

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

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

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

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

+ 8 - 6
frameworks/C++/treefrog/benchmark_config.json

@@ -16,7 +16,7 @@
       "framework": "treefrog",
       "framework": "treefrog",
       "language": "C++",
       "language": "C++",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "Treefrog",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
@@ -38,14 +38,15 @@
       "database": "MySQL",
       "database": "MySQL",
       "framework": "treefrog",
       "framework": "treefrog",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "Treefrog",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "treefrog-hybrid",
       "display_name": "treefrog-hybrid",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "treefrog"
     },
     },
     "postgres": {
     "postgres": {
       "setup_file": "setup-postgres",
       "setup_file": "setup-postgres",
@@ -61,8 +62,9 @@
       "database": "Postgres",
       "database": "Postgres",
       "framework": "treefrog",
       "framework": "treefrog",
       "language": "C++",
       "language": "C++",
+      "flavor": "None",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "Treefrog",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
@@ -85,13 +87,13 @@
       "framework": "treefrog",
       "framework": "treefrog",
       "language": "C++",
       "language": "C++",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "Treefrog",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "treefrog-mongo",
       "display_name": "treefrog-mongo",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "treefrog"
     }
     }
   }]
   }]
 }
 }

+ 6 - 80
frameworks/C++/ulib/benchmark_config.json

@@ -11,7 +11,7 @@
       "framework": "ULib",
       "framework": "ULib",
       "language": "C++",
       "language": "C++",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "webserver": "ULib",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
@@ -19,7 +19,7 @@
       "notes": "",
       "notes": "",
       "versus": ""
       "versus": ""
     },
     },
-    "json_normal": {
+    "json": {
       "setup_file": "setup_json",
       "setup_file": "setup_json",
       "json_url": "/json",
       "json_url": "/json",
       "port": 8080,
       "port": 8080,
@@ -29,61 +29,7 @@
       "framework": "ULib",
       "framework": "ULib",
       "language": "C++",
       "language": "C++",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "ULib",
-      "webserver": "ULib",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ULib",
-      "notes": "",
-      "versus": ""
-    },
-    "json_medium": {
-      "setup_file": "setup_json_medium",
-      "json_url": "/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "ULib",
-      "language": "C++",
-      "orm": "Micro",
-      "platform": "ULib",
-      "webserver": "ULib",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ULib",
-      "notes": "",
-      "versus": ""
-    },
-    "json_large": {
-      "setup_file": "setup_json_large",
-      "json_url": "/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "ULib",
-      "language": "C++",
-      "orm": "Micro",
-      "platform": "ULib",
-      "webserver": "ULib",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ULib",
-      "notes": "",
-      "versus": ""
-    },
-    "json_extra": {
-      "setup_file": "setup_json_extra",
-      "json_url": "/json",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "ULib",
-      "language": "C++",
-      "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "webserver": "ULib",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
@@ -104,7 +50,7 @@
       "framework": "ULib",
       "framework": "ULib",
       "language": "C++",
       "language": "C++",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "webserver": "ULib",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
@@ -125,7 +71,7 @@
       "framework": "ULib",
       "framework": "ULib",
       "language": "C++",
       "language": "C++",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "webserver": "ULib",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
@@ -133,26 +79,6 @@
       "notes": "",
       "notes": "",
       "versus": ""
       "versus": ""
     },
     },
-    "sqlite": {
-      "setup_file": "setup_sqlite",
-      "db_url": "/db",
-      "query_url": "/query?queries=",
-      "fortune_url": "/fortune",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "SQLite",
-      "framework": "ULib",
-      "language": "C++",
-      "orm": "Micro",
-      "platform": "ULib",
-      "webserver": "ULib",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "ULib-sqlite",
-      "notes": "",
-      "versus": ""
-    },
     "mongodb": {
     "mongodb": {
       "setup_file": "setup_mongodb",
       "setup_file": "setup_mongodb",
       "db_url": "/mdb",
       "db_url": "/mdb",
@@ -166,7 +92,7 @@
       "framework": "ULib",
       "framework": "ULib",
       "language": "C++",
       "language": "C++",
       "orm": "Micro",
       "orm": "Micro",
-      "platform": "ULib",
+      "platform": "None",
       "webserver": "ULib",
       "webserver": "ULib",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",

+ 4 - 2
frameworks/C++/ulib/setup_json.sh

@@ -2,8 +2,10 @@
 
 
 fw_depends ulib
 fw_depends ulib
 
 
-# Travis is really broken!!
-if [ "$TRAVIS" == "true" ]; then
+# Travis is broken
+if [ "$TRAVIS" != "true" ]; then
+MAX_THREADS=$(( 3 * $MAX_THREADS / 2 ))
+else
 MAX_THREADS=$(( 2 * $MAX_THREADS ))
 MAX_THREADS=$(( 2 * $MAX_THREADS ))
 fi
 fi
 
 

+ 8 - 4
frameworks/C++/wt/benchmark_config.json

@@ -15,13 +15,15 @@
      	"database": "MySQL",
      	"database": "MySQL",
      	"framework": "wt",
      	"framework": "wt",
      	"language": "C++",
      	"language": "C++",
+          "flavor": "None",
      	"orm": "Full",
      	"orm": "Full",
-     	"platform": "Wt",
+     	"platform": "None",
      	"webserver": "None",
      	"webserver": "None",
      	"os": "Linux",
      	"os": "Linux",
      	"database_os": "Linux",
      	"database_os": "Linux",
      	"display_name": "wt",
      	"display_name": "wt",
-     	"notes": ""
+     	"notes": "",
+          "versus": "wt"
      },
      },
      "postgres": {
      "postgres": {
      	"setup_file": "setup_postgres",
      	"setup_file": "setup_postgres",
@@ -35,13 +37,15 @@
      	"database": "Postgres",
      	"database": "Postgres",
      	"framework": "wt",
      	"framework": "wt",
      	"language": "C++",
      	"language": "C++",
+          "flavor": "None",
      	"orm": "Full",
      	"orm": "Full",
-     	"platform": "Wt",
+     	"platform": "None",
      	"webserver": "None",
      	"webserver": "None",
      	"os": "Linux",
      	"os": "Linux",
      	"database_os": "Linux",
      	"database_os": "Linux",
      	"display_name": "wt-postgres",
      	"display_name": "wt-postgres",
-     	"notes": ""
+     	"notes": "",
+          "versus": "wt"
      }
      }
   }]
   }]
 }
 }

+ 5 - 4
frameworks/C/duda/benchmark_config.json

@@ -8,17 +8,18 @@
       "port": 2001,
       "port": 2001,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
-      "database": "none",
-      "framework": "Duda",
+      "database": "None",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Duda I/O",
+      "platform": "duda",
       "webserver": "Monkey",
       "webserver": "Monkey",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "Duda I/O",
       "display_name": "Duda I/O",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "duda"
     }
     }
   }]
   }]
 }
 }

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

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

+ 5 - 4
frameworks/C/h2o/benchmark_config.json

@@ -13,14 +13,15 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
       "database": "Postgres",
       "database": "Postgres",
-      "framework": "H2O",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "H2O",
-      "webserver": "H2O",
+      "platform": "None",
+      "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "h2o",
+      "display_name": "H2O",
       "notes": ""
       "notes": ""
     }
     }
   }]
   }]

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

@@ -25,7 +25,7 @@ run_curl()
 
 
 run_h2o_app()
 run_h2o_app()
 {
 {
-	"$1/h2o_app" -a2 -f "$2/template/fortunes.mustache" -m2 "$3" "$4" \
+	"$1/h2o_app" -a1 -f "$2/template/fortunes.mustache" -m5 "$3" "$4" \
 		-d "host=$DBHOST dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass" &
 		-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;
 			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);
 		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)
 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);
 		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;
 		db_conn->param = param;
 		do_execute_query(db_conn);
 		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;
 		param->l.next = NULL;
 		*ctx->db_state.queries.tail = &param->l;
 		*ctx->db_state.queries.tail = &param->l;
 		ctx->db_state.queries.tail = &param->l.next;
 		ctx->db_state.queries.tail = &param->l.next;
 		ctx->db_state.query_num++;
 		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);
 			start_database_connect(ctx, NULL);
 	}
 	}
 	else
 	else

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

@@ -24,7 +24,6 @@
 #include <string.h>
 #include <string.h>
 #include <unistd.h>
 #include <unistd.h>
 #include <sys/epoll.h>
 #include <sys/epoll.h>
-#include <sys/syscall.h>
 
 
 #include "error.h"
 #include "error.h"
 #include "event_loop.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.cb = on_close_connection;
 				sock->on_close.data = &ctx->event_loop.conn_num;
 				sock->on_close.data = &ctx->event_loop.conn_num;
 				h2o_accept(&ctx->event_loop.h2o_accept_ctx, sock);
 				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);
 	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)
 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;
 		ctx->global_data->shutdown = true;
 		h2o_socket_read_stop(ctx->event_loop.h2o_socket);
 		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)
 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)
 	while (!ctx->global_data->shutdown || ctx->event_loop.conn_num)
 		h2o_evloop_run(ctx->event_loop.h2o_ctx.loop);
 		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->h2o_socket);
 	h2o_socket_close(event_loop->epoll_socket);
 	h2o_socket_close(event_loop->epoll_socket);
 	h2o_context_dispose(&event_loop->h2o_ctx);
 	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,
 void initialize_event_loop(bool is_main_thread,
                            global_data_t *global_data,
                            global_data_t *global_data,
+                           h2o_multithread_receiver_t *h2o_receiver,
                            event_loop_t *loop)
                            event_loop_t *loop)
 {
 {
 	memset(loop, 0, sizeof(*loop));
 	memset(loop, 0, sizeof(*loop));
@@ -165,7 +162,7 @@ void initialize_event_loop(bool is_main_thread,
 	loop->h2o_socket->data = loop;
 	loop->h2o_socket->data = loop;
 	h2o_socket_read_start(loop->h2o_socket, accept_connection);
 	h2o_socket_read_start(loop->h2o_socket, accept_connection);
 	h2o_multithread_register_receiver(loop->h2o_ctx.queue,
 	h2o_multithread_register_receiver(loop->h2o_ctx.queue,
-	                                  &loop->h2o_receiver,
+	                                  h2o_receiver,
 	                                  process_messages);
 	                                  process_messages);
 	// libh2o's event loop does not support write polling unless it
 	// libh2o's event loop does not support write polling unless it
 	// controls sending the data as well, so do read polling on the
 	// 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;
 	int epoll_fd;
 	h2o_accept_ctx_t h2o_accept_ctx;
 	h2o_accept_ctx_t h2o_accept_ctx;
 	h2o_context_t h2o_ctx;
 	h2o_context_t h2o_ctx;
-	h2o_multithread_receiver_t h2o_receiver;
 } event_loop_t;
 } event_loop_t;
 
 
 void event_loop(thread_context_t *ctx);
 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,
 void initialize_event_loop(bool is_main_thread,
                            global_data_t *global_data,
                            global_data_t *global_data,
+                           h2o_multithread_receiver_t *h2o_receiver,
                            event_loop_t *loop);
                            event_loop_t *loop);
 int start_write_polling(int fd,
 int start_write_polling(int fd,
                         void (**on_write_ready)(void *),
                         void (**on_write_ready)(void *),

+ 25 - 40
frameworks/C/h2o/src/fortune.c

@@ -49,6 +49,7 @@ typedef struct {
 	iovec_list_t *iovec_list_iter;
 	iovec_list_t *iovec_list_iter;
 	h2o_req_t *req;
 	h2o_req_t *req;
 	list_t *result;
 	list_t *result;
+	size_t content_length;
 	size_t num_result;
 	size_t num_result;
 	db_query_param_t param;
 	db_query_param_t param;
 	h2o_generator_t generator;
 	h2o_generator_t generator;
@@ -56,9 +57,9 @@ typedef struct {
 
 
 static uintmax_t add_iovec(mustache_api_t *api,
 static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
                            void *userdata,
-                           char *buffer,
+                           const char *buffer,
                            uintmax_t buffer_size);
                            uintmax_t buffer_size);
-static void cleanup_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
+static void cleanup_fortunes(void *data);
 static int compare_fortunes(const list_t *x, const list_t *y);
 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 void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
 static list_t *get_sorted_sublist(list_t *head);
 static list_t *get_sorted_sublist(list_t *head);
@@ -76,7 +77,7 @@ static list_t *sort_fortunes(list_t *head);
 
 
 static uintmax_t add_iovec(mustache_api_t *api,
 static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
                            void *userdata,
-                           char *buffer,
+                           const char *buffer,
                            uintmax_t buffer_size)
                            uintmax_t buffer_size)
 {
 {
 	IGNORE_FUNCTION_PARAMETER(api);
 	IGNORE_FUNCTION_PARAMETER(api);
@@ -101,20 +102,17 @@ static uintmax_t add_iovec(mustache_api_t *api,
 	}
 	}
 
 
 	if (ret) {
 	if (ret) {
-		iovec_list->iov[iovec_list->iovcnt].base = buffer;
+		iovec_list->iov[iovec_list->iovcnt].base = (char *) buffer;
 		iovec_list->iov[iovec_list->iovcnt++].len = buffer_size;
 		iovec_list->iov[iovec_list->iovcnt++].len = buffer_size;
+		fortune_ctx->content_length += buffer_size;
 	}
 	}
 
 
 	return ret;
 	return ret;
 }
 }
 
 
-static void cleanup_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req)
+static void cleanup_fortunes(void *data)
 {
 {
-	IGNORE_FUNCTION_PARAMETER(req);
-
-	fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t,
-	                                                           generator,
-	                                                           self);
+	fortune_ctx_t * const fortune_ctx = data;
 	const list_t *iter = fortune_ctx->result;
 	const list_t *iter = fortune_ctx->result;
 
 
 	if (iter)
 	if (iter)
@@ -146,19 +144,12 @@ static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req)
 	fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t,
 	fortune_ctx_t * const fortune_ctx = H2O_STRUCT_FROM_MEMBER(fortune_ctx_t,
 	                                                           generator,
 	                                                           generator,
 	                                                           self);
 	                                                           self);
+	iovec_list_t * const iovec_list = H2O_STRUCT_FROM_MEMBER(iovec_list_t,
+	                                                         l,
+	                                                         fortune_ctx->iovec_list);
 
 
-	if (fortune_ctx->iovec_list) {
-		iovec_list_t * const iovec_list = H2O_STRUCT_FROM_MEMBER(iovec_list_t,
-		                                                         l,
-		                                                         fortune_ctx->iovec_list);
-
-		fortune_ctx->iovec_list = iovec_list->l.next;
-		h2o_send(fortune_ctx->req, iovec_list->iov, iovec_list->iovcnt, false);
-	}
-	else {
-		h2o_send(req, NULL, 0, true);
-		cleanup_fortunes(self, req);
-	}
+	fortune_ctx->iovec_list = iovec_list->l.next;
+	h2o_send(req, iovec_list->iov, iovec_list->iovcnt, !fortune_ctx->iovec_list);
 }
 }
 
 
 static list_t *get_sorted_sublist(list_t *head)
 static list_t *get_sorted_sublist(list_t *head)
@@ -168,12 +159,7 @@ static list_t *get_sorted_sublist(list_t *head)
 	if (head) {
 	if (head) {
 		head = head->next;
 		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;
 			tail = head;
 			head = head->next;
 			head = head->next;
 		}
 		}
@@ -218,7 +204,6 @@ static void on_fortune_error(db_query_param_t *param, const char *error_string)
 	                                                           param,
 	                                                           param,
 	                                                           param);
 	                                                           param);
 
 
-	cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 	send_error(BAD_GATEWAY, error_string, fortune_ctx->req);
 	send_error(BAD_GATEWAY, error_string, fortune_ctx->req);
 }
 }
 
 
@@ -254,7 +239,6 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 					fortune->data = result;
 					fortune->data = result;
 			}
 			}
 			else {
 			else {
-				cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 				send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
 				send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
 				ret = DONE;
 				ret = DONE;
 
 
@@ -265,8 +249,10 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 			}
 			}
 		}
 		}
 	}
 	}
-	else if (result)
+	else if (result) {
 		PQclear(result);
 		PQclear(result);
+		send_error(BAD_GATEWAY, PQresultErrorMessage(result), fortune_ctx->req);
+	}
 	else {
 	else {
 		mustache_api_t api = {.sectget = on_fortune_section,
 		mustache_api_t api = {.sectget = on_fortune_section,
 		                      .varget = on_fortune_variable,
 		                      .varget = on_fortune_variable,
@@ -286,18 +272,15 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 
 
 		if (mustache_render(&api, fortune_ctx, ctx->global_data->fortunes_template)) {
 		if (mustache_render(&api, fortune_ctx, ctx->global_data->fortunes_template)) {
 			fortune_ctx->iovec_list = iovec_list->l.next;
 			fortune_ctx->iovec_list = iovec_list->l.next;
-			set_default_response_param(HTML, fortune_ctx->req);
+			set_default_response_param(HTML, fortune_ctx->content_length, fortune_ctx->req);
 			h2o_start_response(fortune_ctx->req, &fortune_ctx->generator);
 			h2o_start_response(fortune_ctx->req, &fortune_ctx->generator);
 			h2o_send(fortune_ctx->req,
 			h2o_send(fortune_ctx->req,
 			         iovec_list->iov,
 			         iovec_list->iov,
 			         iovec_list->iovcnt,
 			         iovec_list->iovcnt,
-			         false);
+			         !fortune_ctx->iovec_list);
 		}
 		}
-		else {
-			cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
+		else
 			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
 			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
-			ret = DONE;
-		}
 	}
 	}
 
 
 	return ret;
 	return ret;
@@ -335,7 +318,6 @@ static void on_fortune_timeout(db_query_param_t *param)
 	                                                           param,
 	                                                           param,
 	                                                           param);
 	                                                           param);
 
 
-	cleanup_fortunes(&fortune_ctx->generator, fortune_ctx->req);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, fortune_ctx->req);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, fortune_ctx->req);
 }
 }
 
 
@@ -388,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,
 	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
 	                                                      event_loop.h2o_ctx,
 	                                                      event_loop.h2o_ctx,
 	                                                      req->conn->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) {
 	if (fortune_ctx) {
 		fortune_t * const fortune = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune));
 		fortune_t * const fortune = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune));
@@ -401,7 +385,6 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 			fortune->message.len = sizeof(NEW_FORTUNE_MESSAGE) - 1;
 			fortune->message.len = sizeof(NEW_FORTUNE_MESSAGE) - 1;
 			memset(fortune_ctx, 0, sizeof(*fortune_ctx));
 			memset(fortune_ctx, 0, sizeof(*fortune_ctx));
 			fortune_ctx->generator.proceed = complete_fortunes;
 			fortune_ctx->generator.proceed = complete_fortunes;
-			fortune_ctx->generator.stop = cleanup_fortunes;
 			fortune_ctx->num_result = 1;
 			fortune_ctx->num_result = 1;
 			fortune_ctx->param.command = FORTUNE_TABLE_NAME;
 			fortune_ctx->param.command = FORTUNE_TABLE_NAME;
 			fortune_ctx->param.on_error = on_fortune_error;
 			fortune_ctx->param.on_error = on_fortune_error;
@@ -414,6 +397,8 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 			if (execute_query(ctx, &fortune_ctx->param))
 			if (execute_query(ctx, &fortune_ctx->param))
 				send_service_unavailable_error(DB_REQ_ERROR, req);
 				send_service_unavailable_error(DB_REQ_ERROR, req);
 		}
 		}
+		else
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 	}
 	}
 	else
 	else
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);

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

@@ -24,6 +24,7 @@
 #include <netdb.h>
 #include <netdb.h>
 #include <signal.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
@@ -44,6 +45,7 @@
 #include "tls.h"
 #include "tls.h"
 #include "utility.h"
 #include "utility.h"
 
 
+#define DEFAULT_CACHE_LINE_SIZE 128
 #define DEFAULT_TCP_FASTOPEN_QUEUE_LEN 4096
 #define DEFAULT_TCP_FASTOPEN_QUEUE_LEN 4096
 #define USAGE_MESSAGE \
 #define USAGE_MESSAGE \
 	"Usage:\n%s [-a <max connections accepted simultaneously>] [-b <bind address>] " \
 	"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)
 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)
 	if (global_data->file_logger)
 		global_data->file_logger->dispose(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;
 	sigset_t signals;
 
 
 	memset(global_data, 0, sizeof(*global_data));
 	memset(global_data, 0, sizeof(*global_data));
-	global_data->config = config;
 	global_data->memory_alignment = get_maximum_cache_line_size();
 	global_data->memory_alignment = get_maximum_cache_line_size();
-	assert(global_data->memory_alignment <= DEFAULT_CACHE_LINE_SIZE);
 	CHECK_ERRNO(sigemptyset, &signals);
 	CHECK_ERRNO(sigemptyset, &signals);
 #ifdef NDEBUG
 #ifdef NDEBUG
 	CHECK_ERRNO(sigaddset, &signals, SIGINT);
 	CHECK_ERRNO(sigaddset, &signals, SIGINT);
@@ -194,7 +198,7 @@ static int initialize_global_data(const config_t *config, global_data_t *global_
 		goto error;
 		goto error;
 
 
 	if (config->cert && config->key)
 	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"));
 	const h2o_iovec_t host = h2o_iovec_init(H2O_STRLIT("default"));
 	h2o_hostconf_t * const hostconf = h2o_config_register_host(&global_data->h2o_config,
 	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->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;
 		return EXIT_SUCCESS;
+	}
 
 
 error:
 error:
 	free_global_data(global_data);
 	free_global_data(global_data);
@@ -353,10 +361,17 @@ int main(int argc, char *argv[])
 		global_data_t global_data;
 		global_data_t global_data;
 
 
 		if (initialize_global_data(&config, &global_data) == EXIT_SUCCESS) {
 		if (initialize_global_data(&config, &global_data) == EXIT_SUCCESS) {
+			thread_context_t ctx;
+
 			setup_process();
 			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);
 			free_global_data(&global_data);
 			rc = EXIT_SUCCESS;
 			rc = EXIT_SUCCESS;
 		}
 		}

+ 38 - 53
frameworks/C/h2o/src/request_handler.c

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

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

@@ -22,6 +22,7 @@
 #define REQUEST_H_
 #define REQUEST_H_
 
 
 #include <h2o.h>
 #include <h2o.h>
+#include <stdbool.h>
 #include <yajl/yajl_gen.h>
 #include <yajl/yajl_gen.h>
 
 
 typedef enum {
 typedef enum {
@@ -44,8 +45,10 @@ const char *get_query_param(const char *query,
                             size_t param_len);
                             size_t param_len);
 void register_request_handlers(h2o_hostconf_t *hostconf, h2o_access_log_filehandle_t *log_handle);
 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);
 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 send_service_unavailable_error(const char *body, h2o_req_t *req);
-void set_default_response_param(content_type_t content_type, h2o_req_t *req);
+void set_default_response_param(content_type_t content_type,
+                                size_t content_length,
+                                h2o_req_t *req);
 
 
 #endif // REQUEST_H_
 #endif // REQUEST_H_

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

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

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

@@ -22,9 +22,12 @@
 #include <errno.h>
 #include <errno.h>
 #include <h2o.h>
 #include <h2o.h>
 #include <pthread.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdlib.h>
+#include <string.h>
 #include <h2o/serverutil.h>
 #include <h2o/serverutil.h>
 #include <sys/epoll.h>
 #include <sys/epoll.h>
+#include <sys/syscall.h>
 
 
 #include "database.h"
 #include "database.h"
 #include "error.h"
 #include "error.h"
@@ -35,71 +38,86 @@ static void *run_thread(void *arg);
 
 
 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);
 	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) {
 	if (ret) {
 		memset(ret, 0, sz);
 		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;
 			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;
 	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();
 	const size_t num_cpus = h2o_numproc();
 
 
 	// The first thread context is used by the main thread.
 	// 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
 	// If the number of threads is not equal to the number of processors, then let the scheduler
 	// decide how to balance the load.
 	// 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);
 		const size_t cpusetsize = CPU_ALLOC_SIZE(num_cpus);
 		cpu_set_t * const cpuset = CPU_ALLOC(num_cpus);
 		cpu_set_t * const cpuset = CPU_ALLOC(num_cpus);
 
 
 		if (!cpuset)
 		if (!cpuset)
 			abort();
 			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_ZERO_S(cpusetsize, cpuset);
 			CPU_SET_S(i, 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);
 		CPU_FREE(cpuset);

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

@@ -24,33 +24,41 @@
 #include <assert.h>
 #include <assert.h>
 #include <h2o.h>
 #include <h2o.h>
 #include <pthread.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <sys/types.h>
 #include <sys/types.h>
 
 
 #include "database.h"
 #include "database.h"
 #include "event_loop.h"
 #include "event_loop.h"
 #include "utility.h"
 #include "utility.h"
 
 
-#define DEFAULT_CACHE_LINE_SIZE 64
-
 typedef struct thread_context_t thread_context_t;
 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 {
 struct thread_context_t {
+	const config_t *config;
 	global_data_t *global_data;
 	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;
 	unsigned random_seed;
 	pid_t tid;
 	pid_t tid;
 	db_state_t db_state;
 	db_state_t db_state;
 	event_loop_t event_loop;
 	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_
 #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);
 	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_library_init();
 	SSL_load_error_strings();
 	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());
 	global_data->ssl_ctx = SSL_CTX_new(TLSv1_2_server_method());
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_certificate_file,
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_certificate_file,
 	                    global_data->ssl_ctx,
 	                    global_data->ssl_ctx,
-	                    global_data->config->cert,
+	                    config->cert,
 	                    SSL_FILETYPE_PEM);
 	                    SSL_FILETYPE_PEM);
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_PrivateKey_file,
 	CHECK_OPENSSL_ERROR(SSL_CTX_use_PrivateKey_file,
 	                    global_data->ssl_ctx,
 	                    global_data->ssl_ctx,
-	                    global_data->config->key,
+	                    config->key,
 	                    SSL_FILETYPE_PEM);
 	                    SSL_FILETYPE_PEM);
 }
 }

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

@@ -24,6 +24,6 @@
 #include "utility.h"
 #include "utility.h"
 
 
 void cleanup_openssl(global_data_t *global_data);
 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_
 #endif // TLS_H_

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

@@ -36,7 +36,7 @@
 #define TOSTRING(x) # x
 #define TOSTRING(x) # x
 #define YAJL_STRLIT(s) (const unsigned char *) (s), sizeof(s) - 1
 #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 {
 typedef struct {
 	const char *bind_address;
 	const char *bind_address;
@@ -54,12 +54,11 @@ typedef struct {
 } config_t;
 } config_t;
 
 
 typedef struct {
 typedef struct {
-	const config_t *config;
-	thread_context_t *ctx;
 	h2o_logger_t *file_logger;
 	h2o_logger_t *file_logger;
 	mustache_template_t *fortunes_template;
 	mustache_template_t *fortunes_template;
 	h2o_socket_t *signals;
 	h2o_socket_t *signals;
 	SSL_CTX *ssl_ctx;
 	SSL_CTX *ssl_ctx;
+	global_thread_data_t *global_thread_data;
 	size_t memory_alignment;
 	size_t memory_alignment;
 	int listener_sd;
 	int listener_sd;
 	int signal_fd;
 	int signal_fd;
@@ -67,7 +66,11 @@ typedef struct {
 	h2o_globalconf_t h2o_config;
 	h2o_globalconf_t h2o_config;
 } global_data_t;
 } 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);
 yajl_gen get_json_generator(h2o_mem_pool_t *pool);
+
 uint32_t get_random_number(uint32_t max_rand, unsigned int *seed);
 uint32_t get_random_number(uint32_t max_rand, unsigned int *seed);
 
 
 #endif // UTILITY_H_
 #endif // UTILITY_H_

+ 68 - 85
frameworks/C/h2o/src/world.c

@@ -53,8 +53,6 @@ typedef struct {
 
 
 typedef struct {
 typedef struct {
 	db_query_param_t param;
 	db_query_param_t param;
-	yajl_gen gen;
-	h2o_generator_t h2o_generator;
 	const char *id_pointer;
 	const char *id_pointer;
 	h2o_req_t *req;
 	h2o_req_t *req;
 	uint32_t id;
 	uint32_t id;
@@ -64,18 +62,18 @@ typedef struct {
 
 
 typedef struct {
 typedef struct {
 	single_query_ctx_t single;
 	single_query_ctx_t single;
+	yajl_gen gen;
 	size_t num_query;
 	size_t num_query;
 	size_t num_result;
 	size_t num_result;
 	update_state_t update_state;
 	update_state_t update_state;
 	query_result_t res[];
 	query_result_t res[];
 } multiple_query_ctx_t;
 } multiple_query_ctx_t;
 
 
-static void complete_request(struct st_h2o_generator_t *self, h2o_req_t *req);
-static void cleanup_request(struct st_h2o_generator_t *self, h2o_req_t *req);
+static void cleanup_request(void *data);
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req);
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req);
-static int initialize_single_query_context(h2o_req_t *req,
-                                           on_result_t on_result,
-                                           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_error(db_query_param_t *param, const char *error_string);
 static void on_database_timeout(db_query_param_t *param);
 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);
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result);
@@ -85,25 +83,15 @@ static int on_update_write_ready(db_query_param_t *param, PGconn *db_conn);
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen);
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen);
 static void serialize_items(const query_result_t *res,
 static void serialize_items(const query_result_t *res,
                             size_t num_result,
                             size_t num_result,
-                            h2o_generator_t *h2o_generator,
                             yajl_gen gen,
                             yajl_gen gen,
                             h2o_req_t *req);
                             h2o_req_t *req);
 
 
-static void cleanup_request(struct st_h2o_generator_t *self, h2o_req_t *req)
+static void cleanup_request(void *data)
 {
 {
-	IGNORE_FUNCTION_PARAMETER(req);
-
-	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
-	                                                              h2o_generator,
-	                                                              self);
+	const multiple_query_ctx_t * const query_ctx = data;
 
 
-	yajl_gen_free(query_ctx->gen);
-}
-
-static void complete_request(struct st_h2o_generator_t *self, h2o_req_t *req)
-{
-	h2o_send(req, NULL, 0, true);
-	cleanup_request(self, req);
+	if (query_ctx->gen)
+		yajl_gen_free(query_ctx->gen);
 }
 }
 
 
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
@@ -129,16 +117,13 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 		num_query = MAX_QUERIES;
 		num_query = MAX_QUERIES;
 
 
 	const size_t sz = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
 	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.
 		// MAX_ID is a relatively small number, so allocate on the stack.
 		DEFINE_BITSET(bitset, MAX_ID);
 		DEFINE_BITSET(bitset, MAX_ID);
 
 
+		initialize_single_query_context(req, on_multiple_query_result, &query_ctx->single);
 		memset(&query_ctx->single + 1,
 		memset(&query_ctx->single + 1,
 		       0,
 		       0,
 		       offsetof(multiple_query_ctx_t, res) - sizeof(query_ctx->single));
 		       offsetof(multiple_query_ctx_t, res) - sizeof(query_ctx->single));
@@ -159,45 +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);
 		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);
 			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;
 	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));
 	memset(query_ctx, 0, sizeof(*query_ctx));
-	query_ctx->gen = get_json_generator(&req->pool);
-
-	if (query_ctx->gen) {
-		query_ctx->h2o_generator.proceed = complete_request;
-		query_ctx->h2o_generator.stop = cleanup_request;
-		query_ctx->id_format = 1;
-		query_ctx->id_len = sizeof(query_ctx->id);
-		query_ctx->id_pointer = (const char *) &query_ctx->id;
-		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)
 static void on_database_error(db_query_param_t *param, const char *error_string)
@@ -206,7 +183,6 @@ static void on_database_error(db_query_param_t *param, const char *error_string)
 	                                                              param,
 	                                                              param,
 	                                                              param);
 	                                                              param);
 
 
-	yajl_gen_free(query_ctx->gen);
 	send_error(BAD_GATEWAY, error_string, query_ctx->req);
 	send_error(BAD_GATEWAY, error_string, query_ctx->req);
 }
 }
 
 
@@ -216,7 +192,6 @@ static void on_database_timeout(db_query_param_t *param)
 	                                                              param,
 	                                                              param,
 	                                                              param);
 	                                                              param);
 
 
-	yajl_gen_free(query_ctx->gen);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
 	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
 }
 }
 
 
@@ -251,8 +226,7 @@ static result_return_t on_multiple_query_result(db_query_param_t *param, PGresul
 		else if (query_ctx->update_state == NO_UPDATE) {
 		else if (query_ctx->update_state == NO_UPDATE) {
 			serialize_items(query_ctx->res,
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
 			                query_ctx->num_result,
-			                &query_ctx->single.h2o_generator,
-			                query_ctx->single.gen,
+			                query_ctx->gen,
 			                query_ctx->single.req);
 			                query_ctx->single.req);
 			return DONE;
 			return DONE;
 		}
 		}
@@ -277,7 +251,6 @@ static result_return_t on_multiple_query_result(db_query_param_t *param, PGresul
 		PQclear(result);
 		PQclear(result);
 	}
 	}
 
 
-	yajl_gen_free(query_ctx->single.gen);
 	return DONE;
 	return DONE;
 }
 }
 
 
@@ -299,9 +272,17 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		random_number = ntohl(random_number);
 		random_number = ntohl(random_number);
 		PQclear(result);
 		PQclear(result);
 
 
-		if (!serialize_item(ntohl(query_ctx->id), random_number, query_ctx->gen) &&
-		    !send_json_response(query_ctx->gen, &query_ctx->h2o_generator, query_ctx->req))
-			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);
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, query_ctx->req);
 	}
 	}
@@ -310,7 +291,6 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		PQclear(result);
 		PQclear(result);
 	}
 	}
 
 
-	yajl_gen_free(query_ctx->gen);
 	return DONE;
 	return DONE;
 }
 }
 
 
@@ -342,8 +322,7 @@ static result_return_t on_update_result(db_query_param_t *param, PGresult *resul
 
 
 			serialize_items(query_ctx->res,
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
 			                query_ctx->num_result,
-			                &query_ctx->single.h2o_generator,
-			                query_ctx->single.gen,
+			                query_ctx->gen,
 			                query_ctx->single.req);
 			                query_ctx->single.req);
 			ret = DONE;
 			ret = DONE;
 			break;
 			break;
@@ -440,21 +419,28 @@ error_yajl:
 
 
 static void serialize_items(const query_result_t *res,
 static void serialize_items(const query_result_t *res,
                             size_t num_result,
                             size_t num_result,
-                            h2o_generator_t *h2o_generator,
                             yajl_gen gen,
                             yajl_gen gen,
                             h2o_req_t *req)
                             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, false, req))
+			return;
+	}
 
 
-	if (send_json_response(gen, h2o_generator, req))
 error_yajl:
 error_yajl:
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+	send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 }
 }
 
 
 int multiple_queries(struct st_h2o_handler_t *self, h2o_req_t *req)
 int multiple_queries(struct st_h2o_handler_t *self, h2o_req_t *req)
@@ -473,18 +459,15 @@ int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
 	                                                      req->conn->ctx);
 	                                                      req->conn->ctx);
 	single_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*query_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);
 		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);
 			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
 	}
 	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 
 
 	return 0;
 	return 0;
 }
 }

+ 4 - 3
frameworks/C/haywire/benchmark_config.json

@@ -6,17 +6,18 @@
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8000,
       "port": 8000,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Micro",
+      "classification": "Platform",
       "database": "None",
       "database": "None",
-      "framework": "haywire",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "haywire",
       "platform": "haywire",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "haywire"
     }
     }
   }]
   }]
 }
 }

+ 3 - 2
frameworks/C/libreactor/benchmark_config.json

@@ -9,15 +9,16 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
       "database": "None",
       "database": "None",
-      "framework": "libreactor",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "libreactor",
       "platform": "libreactor",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "libreactor"
     }
     }
   }]
   }]
 }
 }

+ 12 - 9
frameworks/C/lwan/benchmark_config.json

@@ -9,16 +9,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
       "database": "None",
       "database": "None",
-      "framework": "lwan",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "Lwan",
       "platform": "Lwan",
-      "webserver": "Lwan",
+      "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "Lwan",
       "display_name": "Lwan",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "lwan"
     },
     },
     "sqlite": {
     "sqlite": {
       "setup_file": "setup",
       "setup_file": "setup",
@@ -29,16 +30,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
       "database": "SQLite",
       "database": "SQLite",
-      "framework": "lwan",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "Lwan",
       "platform": "Lwan",
-      "webserver": "Lwan",
+      "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "Lwan",
       "display_name": "Lwan",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "lwan"
     },
     },
     "mysql": {
     "mysql": {
       "setup_file": "setup-mysql",
       "setup_file": "setup-mysql",
@@ -49,16 +51,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
       "database": "MySQL",
       "database": "MySQL",
-      "framework": "lwan",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "Lwan",
       "platform": "Lwan",
-      "webserver": "Lwan",
+      "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "Lwan",
       "display_name": "Lwan",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "lwan"
     }
     }
   }]
   }]
 }
 }

+ 8 - 6
frameworks/C/onion/benchmark_config.json

@@ -8,16 +8,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
       "database": "None",
       "database": "None",
-      "framework": "onion",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Onion",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "onion",
       "display_name": "onion",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "onion"
     },
     },
     "raw": {
     "raw": {
       "setup_file": "setup",
       "setup_file": "setup",
@@ -28,16 +29,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Platform",
       "classification": "Platform",
       "database": "MySQL",
       "database": "MySQL",
-      "framework": "onion",
+      "framework": "None",
       "language": "C",
       "language": "C",
+      "flavor": "None",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Onion",
+      "platform": "None",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "onion",
       "display_name": "onion",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "onion"
     }
     }
   }]
   }]
 }
 }

+ 25 - 21
frameworks/CSharp/HttpListener/benchmark_config.json

@@ -6,19 +6,20 @@
       "json_url": "/json",
       "json_url": "/json",
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "classification": "Platform",
       "database": "None",
       "database": "None",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "http-listener",
       "display_name": "http-listener",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "HttpListener"
     },
     },
     "mysql-raw": {
     "mysql-raw": {
       "setup_file": "setup",
       "setup_file": "setup",
@@ -27,14 +28,15 @@
       "fortune_url": "/fortunes?provider=mysql",
       "fortune_url": "/fortunes?provider=mysql",
       "update_url": "/updates?provider=mysql&queries=",
       "update_url": "/updates?provider=mysql&queries=",
       "port": 8080,
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "classification": "Platform",
       "database": "MySQL",
       "database": "MySQL",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "http-listener",
       "display_name": "http-listener",
@@ -48,14 +50,14 @@
       "fortune_url": "/fortunes?provider=postgresql",
       "fortune_url": "/fortunes?provider=postgresql",
       "update_url": "/updates?provider=postgresql&queries=",
       "update_url": "/updates?provider=postgresql&queries=",
       "port": 8080,
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "classification": "Platform",
       "database": "Postgres",
       "database": "Postgres",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "http-listener",
       "display_name": "http-listener",
@@ -69,14 +71,15 @@
       "fortune_url": "/mongodbfortunes",
       "fortune_url": "/mongodbfortunes",
       "update_url": "/mongodbupdates?queries=",
       "update_url": "/mongodbupdates?queries=",
       "port": 8080,
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "classification": "Platform",
       "database": "MongoDB",
       "database": "MongoDB",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "http-listener",
       "display_name": "http-listener",
@@ -90,14 +93,15 @@
       "fortune_url": "/fortunes?provider=sqlserver",
       "fortune_url": "/fortunes?provider=sqlserver",
       "update_url": "/updates?provider=sqlserver&queries=",
       "update_url": "/updates?provider=sqlserver&queries=",
       "port": 8080,
       "port": 8080,
-      "approach": "Realistic",
+      "approach": "Stripped",
       "classification": "Platform",
       "classification": "Platform",
       "database": "SQLServer",
       "database": "SQLServer",
-      "framework": "http-listener",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
-      "webserver": "HTTP.sys",
+      "platform": "None",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Windows",
       "database_os": "Windows",
       "display_name": "http-listener",
       "display_name": "http-listener",

+ 25 - 20
frameworks/CSharp/aspnet-stripped/benchmark_config.json

@@ -9,16 +9,17 @@
       "approach": "Stripped",
       "approach": "Stripped",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "None",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-stripped",
+      "display_name": "asp.net-stripped",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-stripped"
     },
     },
     "mysql-raw": {
     "mysql-raw": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -30,16 +31,17 @@
       "approach": "Stripped",
       "approach": "Stripped",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "MySQL",
       "database": "MySQL",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-stripped-raw",
+      "display_name": "asp.net-stripped-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-stripped"
     },
     },
     "postgresql-raw": {
     "postgresql-raw": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -51,16 +53,17 @@
       "approach": "Stripped",
       "approach": "Stripped",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "Postgres",
       "database": "Postgres",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-stripped-raw",
+      "display_name": "asp.net-stripped-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "asnet-stripped"
     },
     },
     "mongodb-raw": {
     "mongodb-raw": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -72,16 +75,17 @@
       "approach": "Stripped",
       "approach": "Stripped",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "MongoDB",
       "database": "MongoDB",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-stripped-raw",
+      "display_name": "asp.net-stripped-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-stripped"
     },
     },
     "sqlserver-raw": {
     "sqlserver-raw": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -93,16 +97,17 @@
       "approach": "Stripped",
       "approach": "Stripped",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "SQLServer",
       "database": "SQLServer",
-      "framework": "aspnet",
+      "framework": "ASP.NET",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Windows",
       "database_os": "Windows",
-      "display_name": "aspnet-stripped-raw",
+      "display_name": "asp.net-stripped-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-stripped"
     }
     }
   }]
   }]
 }
 }

+ 98 - 80
frameworks/CSharp/aspnet/benchmark_config.json

@@ -9,52 +9,55 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "None",
       "database": "None",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet",
+      "display_name": "asp.net",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "jsonnet": {
     "jsonnet": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
       "json_url": "/json/jsonnet",
       "json_url": "/json/jsonnet",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "None",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "asp.net",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-jsonnet",
+      "display_name": "jsonnet",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "servicestack": {
     "servicestack": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
       "json_url": "/json/servicestack",
       "json_url": "/json/servicestack",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "None",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "asp.net",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-svcstk",
+      "display_name": "servicestack",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "mysql-raw": {
     "mysql-raw": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -66,16 +69,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "MySQL",
       "database": "MySQL",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-raw",
+      "display_name": "asp.net-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "postgresql-raw": {
     "postgresql-raw": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -87,16 +91,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "Postgres",
       "database": "Postgres",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-raw",
+      "display_name": "asp.net-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "mongodb-raw": {
     "mongodb-raw": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -108,16 +113,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "MongoDB",
       "database": "MongoDB",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-raw",
+      "display_name": "asp.net-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "sqlserver-raw": {
     "sqlserver-raw": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -129,16 +135,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "SQLServer",
       "database": "SQLServer",
-      "framework": "aspnet-mvc",
+      "framework": "asp.net-mvc",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Windows",
       "database_os": "Windows",
-      "display_name": "aspnet-mvc-raw",
+      "display_name": "aspnet-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "mysql-entityframework": {
     "mysql-entityframework": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -150,16 +157,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "MySQL",
       "database": "MySQL",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Full",
       "orm": "Full",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc",
+      "display_name": "EntityFramework",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "postgresql-entityframework": {
     "postgresql-entityframework": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -171,16 +179,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "Postgres",
       "database": "Postgres",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Full",
       "orm": "Full",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc",
+      "display_name": "EntityFramework",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "sqlserver-entityframework": {
     "sqlserver-entityframework": {
       "setup_file": "setup_iis",
       "setup_file": "setup_iis",
@@ -192,16 +201,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "SQLServer",
       "database": "SQLServer",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
       "language": "C#",
+      "flavor": "Microsoft",
       "orm": "Full",
       "orm": "Full",
-      "platform": "NET",
+      "platform": ".NET",
       "webserver": "IIS",
       "webserver": "IIS",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Windows",
       "database_os": "Windows",
-      "display_name": "aspnet-mvc",
+      "display_name": "EntityFramework",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "mono": {
     "mono": {
       "setup_file": "setup_nginx",
       "setup_file": "setup_nginx",
@@ -209,54 +219,57 @@
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Platform",
       "database": "None",
       "database": "None",
-      "framework": "aspnet-mvc",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "webserver": "nginx",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono",
+      "display_name": "ASP.NET",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-mono"
     },
     },
     "mono-jsonnet": {
     "mono-jsonnet": {
       "setup_file": "setup_nginx",
       "setup_file": "setup_nginx",
       "json_url": "/json/jsonnet",
       "json_url": "/json/jsonnet",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "None",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "JSONNet",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "webserver": "nginx",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-jsonnet-mono",
+      "display_name": "JSONNet",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-mono"
     },
     },
     "mono-servicestack": {
     "mono-servicestack": {
       "setup_file": "setup_nginx",
       "setup_file": "setup_nginx",
       "json_url": "/json/servicestack",
       "json_url": "/json/servicestack",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Platform",
+      "classification": "Fullstack",
       "database": "None",
       "database": "None",
-      "framework": "aspnet",
+      "framework": "ServiceStack",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "webserver": "nginx",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-svcstk-mono",
+      "display_name": "ServiceStack",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet-mono"
     },
     },
     "mono-mysql-raw": {
     "mono-mysql-raw": {
       "setup_file": "setup_nginx",
       "setup_file": "setup_nginx",
@@ -268,18 +281,19 @@
       "update_url": "/ado/mysql/update?queries=",
       "update_url": "/ado/mysql/update?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Platform",
       "database": "MySQL",
       "database": "MySQL",
-      "framework": "aspnet-mvc",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "webserver": "nginx",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono-raw",
+      "display_name": "ASP.NET-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "mono-postgresql-raw": {
     "mono-postgresql-raw": {
       "setup_file": "setup_nginx",
       "setup_file": "setup_nginx",
@@ -289,18 +303,19 @@
       "update_url": "/ado/postgresql/update?queries=",
       "update_url": "/ado/postgresql/update?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Platform",
       "database": "Postgres",
       "database": "Postgres",
-      "framework": "aspnet-mvc",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "webserver": "nginx",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono-raw",
+      "display_name": "ASP.NET-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "mono-mongodb-raw": {
     "mono-mongodb-raw": {
       "setup_file": "setup_nginx",
       "setup_file": "setup_nginx",
@@ -310,18 +325,19 @@
       "update_url": "/mongodb/update?queries=",
       "update_url": "/mongodb/update?queries=",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Platform",
       "database": "MongoDB",
       "database": "MongoDB",
-      "framework": "aspnet-mvc",
+      "framework": "None",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "webserver": "nginx",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono-raw",
+      "display_name": "aspnet-raw",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "mono-mysql-entityframework": {
     "mono-mysql-entityframework": {
       "setup_file": "setup_nginx",
       "setup_file": "setup_nginx",
@@ -333,16 +349,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "MySQL",
       "database": "MySQL",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Full",
       "orm": "Full",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "webserver": "nginx",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono",
+      "display_name": "EntityFramework",
       "notes": "Entity framework",
       "notes": "Entity framework",
-      "versus": ""
+      "versus": "aspnet"
     },
     },
     "mono-postgresql-entityframework": {
     "mono-postgresql-entityframework": {
       "setup_file": "setup_nginx",
       "setup_file": "setup_nginx",
@@ -354,16 +371,17 @@
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Fullstack",
       "classification": "Fullstack",
       "database": "Postgres",
       "database": "Postgres",
-      "framework": "aspnet-mvc",
+      "framework": "EntityFramework",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Full",
       "orm": "Full",
-      "platform": "Mono",
+      "platform": "ASP.NET",
       "webserver": "nginx",
       "webserver": "nginx",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
-      "display_name": "aspnet-mvc-mono",
+      "display_name": "EntityFramework",
       "notes": "Entity framework",
       "notes": "Entity framework",
-      "versus": ""
+      "versus": "aspnet"
     }
     }
   }]
   }]
 }
 }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 14 - 5
frameworks/CSharp/aspnetcore/Benchmarks/Middleware/MiddlewareHelpers.cs

@@ -2,6 +2,8 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
 using System.Text.Encodings.Web;
 using System.Text.Encodings.Web;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Benchmarks.Data;
 using Benchmarks.Data;
@@ -33,15 +35,22 @@ namespace Benchmarks.Middleware
             httpContext.Response.StatusCode = StatusCodes.Status200OK;
             httpContext.Response.StatusCode = StatusCodes.Status200OK;
             httpContext.Response.ContentType = "text/html; charset=UTF-8";
             httpContext.Response.ContentType = "text/html; charset=UTF-8";
 
 
-            await httpContext.Response.WriteAsync("<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
-
+            var sb = new StringBuilder();
+            sb.Append("<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>");
             foreach (var item in model)
             foreach (var item in model)
             {
             {
-                await httpContext.Response.WriteAsync(
-                    $"<tr><td>{htmlEncoder.Encode(item.Id.ToString())}</td><td>{htmlEncoder.Encode(item.Message)}</td></tr>");
+                sb.Append("<tr><td>");
+                sb.Append(item.Id.ToString(CultureInfo.InvariantCulture));
+                sb.Append("</td><td>");
+                sb.Append(htmlEncoder.Encode(item.Message));
+                sb.Append("</td></tr>");
             }
             }
 
 
-            await httpContext.Response.WriteAsync("</table></body></html>");
+            sb.Append("</table></body></html>");
+            var response = sb.ToString();
+            // fortunes includes multibyte characters so response.Length is incorrect
+            httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(response);
+            await httpContext.Response.WriteAsync(response);
         }
         }
     }
     }
 }
 }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 188 - 13
frameworks/CSharp/aspnetcore/benchmark_config.json

@@ -2,27 +2,112 @@
   "framework": "aspnetcore",
   "framework": "aspnetcore",
   "tests": [{
   "tests": [{
     "default": {
     "default": {
-      "setup_file": "setup",
-      "json_url": "/json",
+      "setup_file": "setup-plaintext",
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Micro",
       "database": "None",
       "database": "None",
       "framework": "aspnetcore",
       "framework": "aspnetcore",
       "language": "C#",
       "language": "C#",
+      "flavor": "Mono",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "NET",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-linux",
       "display_name": "aspnetcore-linux",
       "notes": "",
       "notes": "",
       "versus": ""
       "versus": ""
     },
     },
+    "middleware-json": {
+      "setup_file": "setup-json",
+      "json_url": "/json",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-raw": {
+      "setup_file": "setup-raw",
+      "db_url": "/db/raw",
+      "query_url": "/queries/raw?queries=",
+      "update_url": "/updates/raw?queries=",
+      "fortune_url": "/fortunes/raw",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-middleware-raw",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-ef": {
+      "setup_file": "setup-ef",
+      "db_url": "/db/ef",
+      "query_url": "/queries/ef?queries=",
+      "update_url": "/updates/ef?queries=",
+      "fortune_url": "/fortunes/ef",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Full",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-middleware-ef",
+      "notes": "",
+      "versus": ""
+    },
+    "middleware-dapper": {
+      "setup_file": "setup-dapper",
+      "db_url": "/db/dapper",
+      "query_url": "/queries/dapper?queries=",
+      "update_url": "/updates/dapper?queries=",
+      "fortune_url": "/fortunes/dapper",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Micro",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-middleware-dapper",
+      "notes": "",
+      "versus": ""
+    },
     "mvc-linux": {
     "mvc-linux": {
-      "setup_file": "setup-mvc",
-      "json_url": "/mvc/json",
+      "setup_file": "setup-plaintext",
       "plaintext_url": "/mvc/plaintext",
       "plaintext_url": "/mvc/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
@@ -32,26 +117,113 @@
       "language": "C#",
       "language": "C#",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "NET",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-linux",
       "display_name": "aspnetcore-mvc-linux",
       "notes": "",
       "notes": "",
       "versus": ""
       "versus": ""
     },
     },
+    "mvc-linux-json": {
+      "setup_file": "setup-json",
+      "json_url": "/mvc/json",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "None",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-linux",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-raw": {
+      "setup_file": "setup-raw",
+      "db_url": "/mvc/db/raw",
+      "query_url": "/mvc/queries/raw?queries=",
+      "update_url": "/mvc/updates/raw?queries=",
+      "fortune_url": "/mvc/fortunes/raw",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Raw",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-raw",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-ef": {
+      "setup_file": "setup-ef",
+      "db_url": "/mvc/db/ef",
+      "query_url": "/mvc/queries/ef?queries=",
+      "update_url": "/mvc/updates/ef?queries=",
+      "fortune_url": "/mvc/fortunes/ef",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Full",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-ef",
+      "notes": "",
+      "versus": ""
+    },
+    "mvc-dapper": {
+      "setup_file": "setup-dapper",
+      "db_url": "/mvc/db/dapper",
+      "query_url": "/mvc/queries/dapper?queries=",
+      "update_url": "/mvc/updates/dapper?queries=",
+      "fortune_url": "/mvc/fortunes/dapper",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "aspnetcore",
+      "language": "C#",
+      "orm": "Micro",
+      "platform": "NET",
+      "flavor": "CoreCLR",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "aspnetcore-mvc-dapper",
+      "notes": "",
+      "versus": ""
+    },
     "win": {
     "win": {
       "setup_file": "setup-windows",
       "setup_file": "setup-windows",
       "json_url": "/json",
       "json_url": "/json",
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Micro",
       "database": "None",
       "database": "None",
       "framework": "aspnetcore",
       "framework": "aspnetcore",
       "language": "C#",
       "language": "C#",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "NET",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-win",
       "display_name": "aspnetcore-win",
@@ -70,7 +242,8 @@
       "language": "C#",
       "language": "C#",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "NET",
       "platform": "NET",
-      "webserver": "Kestrel",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-win",
       "display_name": "aspnetcore-mvc-win",
@@ -83,13 +256,14 @@
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
-      "classification": "Fullstack",
+      "classification": "Micro",
       "database": "None",
       "database": "None",
       "framework": "aspnetcore",
       "framework": "aspnetcore",
       "language": "C#",
       "language": "C#",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "NET",
       "platform": "NET",
-      "webserver": "WebListener",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-weblistener",
       "display_name": "aspnetcore-weblistener",
@@ -108,7 +282,8 @@
       "language": "C#",
       "language": "C#",
       "orm": "Raw",
       "orm": "Raw",
       "platform": "NET",
       "platform": "NET",
-      "webserver": "WebListener",
+      "flavor": "CoreCLR",
+      "webserver": "None",
       "os": "Windows",
       "os": "Windows",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "aspnetcore-mvc-weblistener",
       "display_name": "aspnetcore-mvc-weblistener",

部分文件因为文件数量过多而无法显示