瀏覽代碼

Merge remote-tracking branch 'upstream/master'

Dith3r 8 年之前
父節點
當前提交
439dc52ece
共有 100 個文件被更改,包括 1260 次插入2410 次删除
  1. 3 9
      .github/PULL_REQUEST_TEMPLATE.md
  2. 24 38
      .travis.yml
  3. 0 2
      benchmark.cfg.example
  4. 0 172
      config/cassandra/cassandra.init
  5. 0 6
      config/cassandra/cassandra.init.env
  6. 0 704
      config/cassandra/cassandra.yaml
  7. 0 1
      config/cassandra/cleanup-keyspace.cql
  8. 0 9
      config/cassandra/create-keyspace.cql
  9. 0 6
      config/cassandra/db-data-gen.py
  10. 0 44
      config/cassandra/log4j-server.properties
  11. 0 24
      config/elasticsearch/elasticsearch
  12. 0 389
      config/elasticsearch/elasticsearch.yml
  13. 0 21
      config/elasticsearch/es-create-index.sh
  14. 0 5
      config/elasticsearch/es-db-data-gen.py
  15. 23 93
      config/mongodb.conf
  16. 2 2
      config/php-fpm.conf
  17. 0 4
      config/travis_mysql_setup.sh
  18. 0 151
      config/travis_setup.sh
  19. 1 2
      deployment/vagrant-aws/README.md
  20. 6 33
      deployment/vagrant-common/bootstrap.sh
  21. 4 6
      deployment/vagrant-common/core.rb
  22. 3 3
      frameworks/C++/cpoll_cppsp/benchmark_config.json
  23. 1 1
      frameworks/C++/cpoll_cppsp/setup.sh
  24. 5 0
      frameworks/C++/cpoll_cppsp/setup_mysql.sh
  25. 5 0
      frameworks/C++/cpoll_cppsp/setup_postgresql.sh
  26. 1 1
      frameworks/C++/cutelyst/setup.sh
  27. 2 3
      frameworks/C++/cutelyst/setup_thread.sh
  28. 1 1
      frameworks/C++/cutelyst/setup_uwsgi_nginx.sh
  29. 1 0
      frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp
  30. 5 5
      frameworks/C++/cutelyst/src/databaseupdatestest.cpp
  31. 21 31
      frameworks/C++/cutelyst/src/fortunetest.cpp
  32. 2 4
      frameworks/C++/cutelyst/src/fortunetest.h
  33. 2 2
      frameworks/C++/cutelyst/src/jsontest.cpp
  34. 5 5
      frameworks/C++/cutelyst/src/multipledatabasequeriestest.cpp
  35. 2 2
      frameworks/C++/cutelyst/src/root.cpp
  36. 1 1
      frameworks/C++/cutelyst/src/singledatabasequerytest.cpp
  37. 186 2
      frameworks/C++/ffead-cpp/benchmark_config.json
  38. 15 0
      frameworks/C++/ffead-cpp/setup-apache2-mysql.sh
  39. 15 0
      frameworks/C++/ffead-cpp/setup-apache2-postgresql.sh
  40. 15 0
      frameworks/C++/ffead-cpp/setup-apache2.sh
  41. 16 0
      frameworks/C++/ffead-cpp/setup-mysql.sh
  42. 16 0
      frameworks/C++/ffead-cpp/setup-nginx-mysql.sh
  43. 15 0
      frameworks/C++/ffead-cpp/setup-nginx-postgresql.sh
  44. 15 0
      frameworks/C++/ffead-cpp/setup-nginx.sh
  45. 16 0
      frameworks/C++/ffead-cpp/setup-postgresql.sh
  46. 4 0
      frameworks/C++/ffead-cpp/setup.sh
  47. 2 2
      frameworks/C++/silicon/setup_lwan_mysql.sh
  48. 1 1
      frameworks/C++/silicon/setup_mhd_epoll_mysql.sh
  49. 1 1
      frameworks/C++/silicon/setup_mhd_tpc_mysql.sh
  50. 1 1
      frameworks/C++/treefrog/setup-mongodb.sh
  51. 1 1
      frameworks/C++/treefrog/setup-postgres.sh
  52. 1 1
      frameworks/C++/treefrog/setup-thread.sh
  53. 1 1
      frameworks/C++/treefrog/setup.sh
  54. 1 1
      frameworks/C++/ulib/setup_mongodb.sh
  55. 1 1
      frameworks/C++/ulib/setup_mysql.sh
  56. 1 1
      frameworks/C++/ulib/setup_postgres.sh
  57. 1 1
      frameworks/C++/wt/setup.sh
  58. 1 1
      frameworks/C++/wt/setup_postgres.sh
  59. 3 3
      frameworks/C/h2o/CMakeLists.txt
  60. 13 2
      frameworks/C/h2o/setup.sh
  61. 8 7
      frameworks/C/h2o/src/database.c
  62. 8 5
      frameworks/C/h2o/src/database.h
  63. 11 14
      frameworks/C/h2o/src/event_loop.c
  64. 2 2
      frameworks/C/h2o/src/event_loop.h
  65. 39 29
      frameworks/C/h2o/src/fortune.c
  66. 1 1
      frameworks/C/h2o/src/fortune.h
  67. 25 10
      frameworks/C/h2o/src/main.c
  68. 18 17
      frameworks/C/h2o/src/request_handler.c
  69. 2 1
      frameworks/C/h2o/src/request_handler.h
  70. 49 31
      frameworks/C/h2o/src/thread.c
  71. 20 12
      frameworks/C/h2o/src/thread.h
  72. 3 3
      frameworks/C/h2o/src/tls.c
  73. 1 1
      frameworks/C/h2o/src/tls.h
  74. 6 3
      frameworks/C/h2o/src/utility.h
  75. 437 251
      frameworks/C/h2o/src/world.c
  76. 1 1
      frameworks/C/lwan/setup-mysql.sh
  77. 1 1
      frameworks/C/onion/setup.sh
  78. 5 5
      frameworks/CSharp/aspnet/benchmark_config.json
  79. 5 0
      frameworks/CSharp/aspnet/setup_mongodb.sh
  80. 5 0
      frameworks/CSharp/aspnet/setup_mysql.sh
  81. 5 0
      frameworks/CSharp/aspnet/setup_postgresql.sh
  82. 2 0
      frameworks/CSharp/aspnetcore/setup-dapper.sh
  83. 2 0
      frameworks/CSharp/aspnetcore/setup-ef.sh
  84. 2 0
      frameworks/CSharp/aspnetcore/setup-raw.sh
  85. 1 1
      frameworks/CSharp/nancy/setup_nginx.sh
  86. 2 2
      frameworks/CSharp/revenj/README.md
  87. 13 11
      frameworks/CSharp/revenj/Revenj.Bench.sln
  88. 33 16
      frameworks/CSharp/revenj/Revenj.Bench/Context.cs
  89. 19 6
      frameworks/CSharp/revenj/Revenj.Bench/FortuneTemplate.cs
  90. 9 9
      frameworks/CSharp/revenj/Revenj.Bench/Fortunes.cs
  91. 1 1
      frameworks/CSharp/revenj/Revenj.Bench/Fortunes.tt
  92. 36 119
      frameworks/CSharp/revenj/Revenj.Bench/RestService.cs
  93. 13 30
      frameworks/CSharp/revenj/Revenj.Bench/Revenj.Bench.csproj
  94. 3 10
      frameworks/CSharp/revenj/Revenj.Http.exe.config
  95. 1 1
      frameworks/CSharp/revenj/benchmark_config.json
  96. 2 2
      frameworks/CSharp/revenj/setup.ps1
  97. 4 5
      frameworks/CSharp/revenj/setup.sh
  98. 1 1
      frameworks/CSharp/servicestack/setup_nginx.sh
  99. 1 1
      frameworks/CSharp/servicestack/setup_xsp.sh
  100. 1 1
      frameworks/Clojure/compojure/setup.sh

+ 3 - 9
.github/PULL_REQUEST_TEMPLATE.md

@@ -1,19 +1,13 @@
 <!--
 ....................................
 
-MAKE SURE YOU ARE OPENING A PULL 
+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.
+master = currently open to all pull
+requests for Round 14.
 
 ....................................
 -->

+ 24 - 38
.travis.yml

@@ -1,20 +1,15 @@
+# Travis CI
+#
+#
+
 sudo: required
 dist: trusty
-language: python
+language: generic
 python:
   - "2.7"
 
 env:
   matrix:
-    #Group tests by directory to logically break up travis-CI build. Otherwise
-    #we end up starting ~200+ different workers. Seems that ~100 is the limit
-    #before their website starts to lag heavily
-    #Here's the bash if you need to update this. Be sure to maintain the
-    #lines that are currently commented out (these cannot run in Travis)
-    #  cd frameworks
-    #  find . -type d -depth 2 | sed 's|./|    - "TESTDIR=|' | sed 's/$/"/g'
-    #
-    #
     - "TESTDIR=C/libreactor"
     - "TESTDIR=C/lwan"
     - "TESTDIR=C/duda"
@@ -42,7 +37,7 @@ env:
     - "TESTDIR=Clojure/luminus"
     - "TESTDIR=Clojure/pedestal"
     - "TESTDIR=Clojure/aleph"
-    - "TESTDIR=Crystal/crystal-raw"
+    - "TESTDIR=Crystal/crystal"
     - "TESTDIR=Crystal/kemal"
     - "TESTDIR=D/vibed"
     - "TESTDIR=Dart/dart-raw"
@@ -65,6 +60,7 @@ env:
     - "TESTDIR=Go/revel"
     - "TESTDIR=Go/webgo"
     - "TESTDIR=Groovy/grails"
+    - "TESTDIR=Groovy/hot"
     - "TESTDIR=Haskell/snap"
     - "TESTDIR=Haskell/wai"
     - "TESTDIR=Haskell/yesod"
@@ -116,6 +112,7 @@ env:
     - "TESTDIR=JavaScript/sailsjs"
     - "TESTDIR=Kotlin/hexagon"
     - "TESTDIR=Lua/lapis"
+    - "TESTDIR=Lua/octopus"
     - "TESTDIR=Lua/openresty"
     - "TESTDIR=Nim/jester"
     - "TESTDIR=Nim/nawak"
@@ -164,6 +161,7 @@ env:
     - "TESTDIR=Python/turbogears"
     - "TESTDIR=Python/uwsgi"
     - "TESTDIR=Python/web2py"
+    - "TESTDIR=Python/weppy"
     - "TESTDIR=Python/wheezyweb"
     - "TESTDIR=Python/wsgi"
     - "TESTDIR=Racket/racket-ws"
@@ -194,36 +192,24 @@ env:
     - "TESTDIR=Scala/finch"
     - "TESTDIR=Ur/urweb"
 
-before_install:
-  - pip install colorama==0.3.1
-  # Version 2.3 has a nice Counter() and other features
-  # but it requires —-allow-external and -—allow-unverified
-  - pip install progressbar==2.2
-  - pip install requests
-  - echo "127.0.0.1 " `hostname` | sudo tee /etc/hosts
-  - echo "127.0.0.1 localhost" | sudo tee /etc/hosts
-
-services:
-  - postgresql
-  - redis-server
-  - mongodb
-
-addons:
-  postgresql: "9.3"
-  apt:
-    packages:
-      - mysql-server
-      - redis-server
-
 before_script:
-  - sudo sysctl -w net.core.somaxconn=65535
-  - sudo ./config/travis_mysql_setup.sh
-  - mysql -uroot < config/create.sql
-  - sudo ./config/create-redis.sh
+  # travis_clean.sh takes care of some services that are baked into the travis
+  # build. Using language: generic gets us an ubuntu build with fewer services,
+  # but includes database installs, ruby and rvm installs, and others that interfere
+  # with running the suite in a clean ubuntu install.
+  - source ./toolset/travis/travis_clean.sh
+
+  # travis_setup.sh runs all the same commands you would run if you were setting up
+  # a development environment via:
+  # http://frameworkbenchmarks.readthedocs.io/en/latest/Development/Installation-Guide/
+  - source ./toolset/travis/travis_setup.sh
 
 script:
-  # Pick one test in this directory and verify
-  - time ./toolset/run-ci.py verify "$TESTDIR"
+  # run-ci.py runs the diffing to see if travis needs to test this framework. Ideally/eventually,
+  # we'd like to try and do the diffing before travis_clean & setup.
+  # This will run the tests exactly as you would in your own vm:
+  # ./toolset/run-tests.py --mode verify --test (all the valid tests for this framework)
+  - ./toolset/run-ci.py verify "$TESTDIR"
 
 cache:
   directories:

+ 0 - 2
benchmark.cfg.example

@@ -3,7 +3,6 @@
 client_host=127.0.0.1
 client_identity_file=None
 client_user=techempower
-runner_user=testrunner
 database_host=127.0.0.1
 database_identity_file=None
 database_os=linux
@@ -21,7 +20,6 @@ query_levels=[1, 5,10,15,20]
 threads=8
 mode=benchmark
 os=linux
-password_prompt=False
 server_host=127.0.0.1
 sleep=60
 test=None

+ 0 - 172
config/cassandra/cassandra.init

@@ -1,172 +0,0 @@
-#! /bin/sh
-### BEGIN INIT INFO
-# Provides:          cassandra
-# Required-Start:    $remote_fs $network $named $time
-# Required-Stop:     $remote_fs $network $named $time
-# Should-Start:      ntp mdadm
-# Should-Stop:       ntp mdadm
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: distributed storage system for structured data
-# Description:       Cassandra is a distributed (peer-to-peer) system for
-#                    the management and storage of structured data.
-### END INIT INFO
-
-# Author: Eric Evans <[email protected]>
-
-DESC="Cassandra"
-NAME=cassandra
-PIDFILE=/var/run/$NAME/$NAME.pid
-SCRIPTNAME=/etc/init.d/$NAME
-CASSANDRA_CONF=/etc/cassandra
-WAIT_FOR_START=10
-CASSANDRA_HOME=/usr/share/cassandra
-CASSANDRA_LIB=$CASSANDRA_HOME
-CASSANDRA_PROG=/usr/sbin/cassandra
-FD_LIMIT=100000
-
-# Read configuration variable file if it is present
-[ -r /etc/default/$NAME ] && . /etc/default/$NAME
-
-valid_chome=`find $CASSANDRA_LIB/apache-cassandra*.jar 2> /dev/null`
-[ -n "$valid_chome" ] || exit 0
-[ -e $CASSANDRA_CONF/cassandra.yaml ] || exit 0
-[ -e $CASSANDRA_CONF/cassandra-env.sh ] || exit 0
-
-# Read Cassandra environment file.
-. $CASSANDRA_CONF/cassandra-env.sh
-
-if [ -z "$JVM_OPTS" ]; then
-    echo "Initialization failed; \$JVM_OPTS not set!" >&2
-    exit 3
-fi
-
-export JVM_OPTS
-
-# Export JAVA_HOME, if set.
-[ -n "$JAVA_HOME" ] && export JAVA_HOME
-
-# Load the VERBOSE setting and other rcS variables
-. /lib/init/vars.sh
-
-# Define LSB log_* functions.
-# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
-. /lib/lsb/init-functions
-
-#
-# Function that returns 0 if process is running, or nonzero if not.
-#
-# The nonzero value is 3 if the process is simply not running, and 1 if the
-# process is not running but the pidfile exists (to match the exit codes for
-# the "status" command; see LSB core spec 3.1, section 20.2)
-#
-CMD_PATT="cassandra.+CassandraDaemon"
-is_running()
-{
-    if [ -f $PIDFILE ]; then
-        pid=`cat $PIDFILE`
-        grep -Eq "$CMD_PATT" "/proc/$pid/cmdline" 2>/dev/null && return 0
-        return 1
-    fi
-    return 3
-}
-
-#
-# Function that starts the daemon/service
-#
-do_start()
-{
-    # Return
-    #   0 if daemon has been started
-    #   1 if daemon was already running
-    #   2 if daemon could not be started
-
-    ulimit -l unlimited
-    ulimit -n "$FD_LIMIT"
-
-    cassandra_home=`getent passwd cassandra | awk -F ':' '{ print $6; }'`
-    heap_dump_f="$cassandra_home/java_`date +%s`.hprof"
-    error_log_f="$cassandra_home/hs_err_`date +%s`.log"
-
-    [ -e `dirname "$PIDFILE"` ] || \
-        install -d -ocassandra -gcassandra -m755 `dirname $PIDFILE`
-
-
-
-    start-stop-daemon -S -c cassandra -a $CASSANDRA_PROG -q -p "$PIDFILE" -t >/dev/null || return 1
-
-    start-stop-daemon -S -c cassandra -a $CASSANDRA_PROG -b -p "$PIDFILE" -- \
-        -p "$PIDFILE" -H "$heap_dump_f" -E "$error_log_f" >/dev/null || return 2
-
-}
-
-#
-# Function that stops the daemon/service
-#
-do_stop()
-{
-    # Return
-    #   0 if daemon has been stopped
-    #   1 if daemon was already stopped
-    #   2 if daemon could not be stopped
-    #   other if a failure occurred
-    start-stop-daemon -K -p "$PIDFILE" -R TERM/30/KILL/5 >/dev/null
-    RET=$?
-    rm -f "$PIDFILE"
-    return $RET
-}
-
-case "$1" in
-  start)
-	[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
-	do_start
-	case "$?" in
-		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
-		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
-	esac
-	;;
-  stop)
-	[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
-	do_stop
-	case "$?" in
-		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
-		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
-	esac
-	;;
-  restart|force-reload)
-	log_daemon_msg "Restarting $DESC" "$NAME"
-	do_stop
-	case "$?" in
-	  0|1)
-		do_start
-		case "$?" in
-			0) log_end_msg 0 ;;
-			1) log_end_msg 1 ;; # Old process is still running
-			*) log_end_msg 1 ;; # Failed to start
-		esac
-		;;
-	  *)
-	  	# Failed to stop
-		log_end_msg 1
-		;;
-	esac
-	;;
-  status)
-    is_running
-    stat=$?
-    case "$stat" in
-      0) log_success_msg "$DESC is running" ;;
-      1) log_failure_msg "could not access pidfile for $DESC" ;;
-      *) log_success_msg "$DESC is not running" ;;
-    esac
-    exit "$stat"
-    ;;
-  *)
-	echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" >&2
-	exit 3
-	;;
-esac
-
-:
-
-# vi:ai sw=4 ts=4 tw=0 et

+ 0 - 6
config/cassandra/cassandra.init.env

@@ -1,6 +0,0 @@
-
-CASSANDRA_HOME=/opt/cassandra
-CASSANDRA_LIB=$CASSANDRA_HOME/lib
-CASSANDRA_CONF=$CASSANDRA_HOME/conf
-CASSANDRA_PROG=$CASSANDRA_HOME/bin/cassandra
-

+ 0 - 704
config/cassandra/cassandra.yaml

@@ -1,704 +0,0 @@
-# Cassandra storage config YAML 
-
-# NOTE:
-#   See http://wiki.apache.org/cassandra/StorageConfiguration for
-#   full explanations of configuration directives
-# /NOTE
-
-# The name of the cluster. This is mainly used to prevent machines in
-# one logical cluster from joining another.
-cluster_name: 'TFB Cluster'
-
-# This defines the number of tokens randomly assigned to this node on the ring
-# The more tokens, relative to other nodes, the larger the proportion of data
-# that this node will store. You probably want all nodes to have the same number
-# of tokens assuming they have equal hardware capability.
-#
-# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility,
-# and will use the initial_token as described below.
-#
-# Specifying initial_token will override this setting on the node's initial start,
-# on subsequent starts, this setting will apply even if initial token is set.
-#
-# If you already have a cluster with 1 token per node, and wish to migrate to 
-# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations
-num_tokens: 256
-
-# initial_token allows you to specify tokens manually.  While you can use # it with
-# vnodes (num_tokens > 1, above) -- in which case you should provide a 
-# comma-separated list -- it's primarily used when adding nodes # to legacy clusters 
-# that do not have vnodes enabled.
-# initial_token:
-
-# May either be "true" or "false" to enable globally, or contain a list
-# of data centers to enable per-datacenter.
-# hinted_handoff_enabled: DC1,DC2
-# See http://wiki.apache.org/cassandra/HintedHandoff
-hinted_handoff_enabled: true
-# this defines the maximum amount of time a dead host will have hints
-# generated.  After it has been dead this long, new hints for it will not be
-# created until it has been seen alive and gone down again.
-max_hint_window_in_ms: 10800000 # 3 hours
-# Maximum throttle in KBs per second, per delivery thread.  This will be
-# reduced proportionally to the number of nodes in the cluster.  (If there
-# are two nodes in the cluster, each delivery thread will use the maximum
-# rate; if there are three, each will throttle to half of the maximum,
-# since we expect two nodes to be delivering hints simultaneously.)
-hinted_handoff_throttle_in_kb: 1024
-# Number of threads with which to deliver hints;
-# Consider increasing this number when you have multi-dc deployments, since
-# cross-dc handoff tends to be slower
-max_hints_delivery_threads: 2
-
-# Maximum throttle in KBs per second, total. This will be
-# reduced proportionally to the number of nodes in the cluster.
-batchlog_replay_throttle_in_kb: 1024
-
-# Authentication backend, implementing IAuthenticator; used to identify users
-# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
-# PasswordAuthenticator}.
-#
-# - AllowAllAuthenticator performs no checks - set it to disable authentication.
-# - PasswordAuthenticator relies on username/password pairs to authenticate
-#   users. It keeps usernames and hashed passwords in system_auth.credentials table.
-#   Please increase system_auth keyspace replication factor if you use this authenticator.
-authenticator: AllowAllAuthenticator
-
-# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
-# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
-# CassandraAuthorizer}.
-#
-# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
-# - CassandraAuthorizer stores permissions in system_auth.permissions table. Please
-#   increase system_auth keyspace replication factor if you use this authorizer.
-authorizer: AllowAllAuthorizer
-
-# Validity period for permissions cache (fetching permissions can be an
-# expensive operation depending on the authorizer, CassandraAuthorizer is
-# one example). Defaults to 2000, set to 0 to disable.
-# Will be disabled automatically for AllowAllAuthorizer.
-permissions_validity_in_ms: 2000
-
-# Refresh interval for permissions cache (if enabled).
-# After this interval, cache entries become eligible for refresh. Upon next
-# access, an async reload is scheduled and the old value returned until it
-# completes. If permissions_validity_in_ms is non-zero, then this must be
-# also.
-# Defaults to the same value as permissions_validity_in_ms.
-# permissions_update_interval_in_ms: 1000
-
-# The partitioner is responsible for distributing groups of rows (by
-# partition key) across nodes in the cluster.  You should leave this
-# alone for new clusters.  The partitioner can NOT be changed without
-# reloading all data, so when upgrading you should set this to the
-# same partitioner you were already using.
-#
-# Besides Murmur3Partitioner, partitioners included for backwards
-# compatibility include RandomPartitioner, ByteOrderedPartitioner, and
-# OrderPreservingPartitioner.
-#
-partitioner: org.apache.cassandra.dht.Murmur3Partitioner
-
-# Directories where Cassandra should store data on disk.  Cassandra
-# will spread data evenly across them, subject to the granularity of
-# the configured compaction strategy.
-data_file_directories:
-    - /ssd/cassandra/data
-
-# commit log
-commitlog_directory: /ssd/cassandra/commitlog
-
-# policy for data disk failures:
-# stop_paranoid: shut down gossip and Thrift even for single-sstable errors.
-# stop: shut down gossip and Thrift, leaving the node effectively dead, but
-#       can still be inspected via JMX.
-# best_effort: stop using the failed disk and respond to requests based on
-#              remaining available sstables.  This means you WILL see obsolete
-#              data at CL.ONE!
-# ignore: ignore fatal errors and let requests fail, as in pre-1.2 Cassandra
-disk_failure_policy: stop
-
-# policy for commit disk failures:
-# stop: shut down gossip and Thrift, leaving the node effectively dead, but
-#       can still be inspected via JMX.
-# stop_commit: shutdown the commit log, letting writes collect but 
-#              continuing to service reads, as in pre-2.0.5 Cassandra
-# ignore: ignore fatal errors and let the batches fail
-commit_failure_policy: stop
-
-# Maximum size of the key cache in memory.
-#
-# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
-# minimum, sometimes more. The key cache is fairly tiny for the amount of
-# time it saves, so it's worthwhile to use it at large numbers.
-# The row cache saves even more time, but must contain the entire row,
-# so it is extremely space-intensive. It's best to only use the
-# row cache if you have hot rows or static rows.
-#
-# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
-#
-# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
-key_cache_size_in_mb:
-
-# Duration in seconds after which Cassandra should
-# save the key cache. Caches are saved to saved_caches_directory as
-# specified in this configuration file.
-#
-# Saved caches greatly improve cold-start speeds, and is relatively cheap in
-# terms of I/O for the key cache. Row cache saving is much more expensive and
-# has limited use.
-#
-# Default is 14400 or 4 hours.
-key_cache_save_period: 14400
-
-# Number of keys from the key cache to save
-# Disabled by default, meaning all keys are going to be saved
-# key_cache_keys_to_save: 100
-
-# Maximum size of the row cache in memory.
-# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
-#
-# Default value is 0, to disable row caching.
-row_cache_size_in_mb: 0
-
-# Duration in seconds after which Cassandra should
-# safe the row cache. Caches are saved to saved_caches_directory as specified
-# in this configuration file.
-#
-# Saved caches greatly improve cold-start speeds, and is relatively cheap in
-# terms of I/O for the key cache. Row cache saving is much more expensive and
-# has limited use.
-#
-# Default is 0 to disable saving the row cache.
-row_cache_save_period: 0
-
-# Number of keys from the row cache to save
-# Disabled by default, meaning all keys are going to be saved
-# row_cache_keys_to_save: 100
-
-# The off-heap memory allocator.  Affects storage engine metadata as
-# well as caches.  Experiments show that JEMAlloc saves some memory
-# than the native GCC allocator (i.e., JEMalloc is more
-# fragmentation-resistant).
-# 
-# Supported values are: NativeAllocator, JEMallocAllocator
-#
-# If you intend to use JEMallocAllocator you have to install JEMalloc as library and
-# modify cassandra-env.sh as directed in the file.
-#
-# Defaults to NativeAllocator
-# memory_allocator: NativeAllocator
-
-# saved caches
-saved_caches_directory: /ssd/cassandra/saved_caches
-
-# commitlog_sync may be either "periodic" or "batch." 
-# When in batch mode, Cassandra won't ack writes until the commit log
-# has been fsynced to disk.  It will wait up to
-# commitlog_sync_batch_window_in_ms milliseconds for other writes, before
-# performing the sync.
-#
-# commitlog_sync: batch
-# commitlog_sync_batch_window_in_ms: 50
-#
-# the other option is "periodic" where writes may be acked immediately
-# and the CommitLog is simply synced every commitlog_sync_period_in_ms
-# milliseconds.  By default this allows 1024*(CPU cores) pending
-# entries on the commitlog queue.  If you are writing very large blobs,
-# you should reduce that; 16*cores works reasonably well for 1MB blobs.
-# It should be at least as large as the concurrent_writes setting.
-commitlog_sync: periodic
-commitlog_sync_period_in_ms: 10000
-# commitlog_periodic_queue_size:
-
-# The size of the individual commitlog file segments.  A commitlog
-# segment may be archived, deleted, or recycled once all the data
-# in it (potentially from each columnfamily in the system) has been
-# flushed to sstables.  
-#
-# The default size is 32, which is almost always fine, but if you are
-# archiving commitlog segments (see commitlog_archiving.properties),
-# then you probably want a finer granularity of archiving; 8 or 16 MB
-# is reasonable.
-commitlog_segment_size_in_mb: 32
-
-# any class that implements the SeedProvider interface and has a
-# constructor that takes a Map<String, String> of parameters will do.
-seed_provider:
-    # Addresses of hosts that are deemed contact points. 
-    # Cassandra nodes use this list of hosts to find each other and learn
-    # the topology of the ring.  You must change this if you are running
-    # multiple nodes!
-    - class_name: org.apache.cassandra.locator.SimpleSeedProvider
-      parameters:
-          # seeds is actually a comma-delimited list of addresses.
-          # Ex: "<ip1>,<ip2>,<ip3>"
-          - seeds: "127.0.0.1"
-
-# For workloads with more data than can fit in memory, Cassandra's
-# bottleneck will be reads that need to fetch data from
-# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
-# order to allow the operations to enqueue low enough in the stack
-# that the OS and drives can reorder them.
-#
-# On the other hand, since writes are almost never IO bound, the ideal
-# number of "concurrent_writes" is dependent on the number of cores in
-# your system; (8 * number_of_cores) is a good rule of thumb.
-concurrent_reads: 32
-concurrent_writes: 32
-
-# Total memory to use for sstable-reading buffers.  Defaults to
-# the smaller of 1/4 of heap or 512MB.
-# file_cache_size_in_mb: 512
-
-# Total memory to use for memtables.  Cassandra will flush the largest
-# memtable when this much memory is used.
-# If omitted, Cassandra will set it to 1/4 of the heap.
-# memtable_total_space_in_mb: 2048
-
-# Total space to use for commitlogs.  Since commitlog segments are
-# mmapped, and hence use up address space, the default size is 32
-# on 32-bit JVMs, and 1024 on 64-bit JVMs.
-#
-# If space gets above this value (it will round up to the next nearest
-# segment multiple), Cassandra will flush every dirty CF in the oldest
-# segment and remove it.  So a small total commitlog space will tend
-# to cause more flush activity on less-active columnfamilies.
-# commitlog_total_space_in_mb: 4096
-
-# This sets the amount of memtable flush writer threads.  These will
-# be blocked by disk io, and each one will hold a memtable in memory
-# while blocked. If you have a large heap and many data directories,
-# you can increase this value for better flush performance.
-# By default this will be set to the amount of data directories defined.
-#memtable_flush_writers: 1
-
-# the number of full memtables to allow pending flush, that is,
-# waiting for a writer thread.  At a minimum, this should be set to
-# the maximum number of secondary indexes created on a single CF.
-memtable_flush_queue_size: 4
-
-# Whether to, when doing sequential writing, fsync() at intervals in
-# order to force the operating system to flush the dirty
-# buffers. Enable this to avoid sudden dirty buffer flushing from
-# impacting read latencies. Almost always a good idea on SSDs; not
-# necessarily on platters.
-trickle_fsync: false
-trickle_fsync_interval_in_kb: 10240
-
-# TCP port, for commands and data
-storage_port: 7000
-
-# SSL port, for encrypted communication.  Unused unless enabled in
-# encryption_options
-ssl_storage_port: 7001
-
-# Address to bind to and tell other Cassandra nodes to connect to. You
-# _must_ change this if you want multiple nodes to be able to
-# communicate!
-# 
-# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
-# will always do the Right Thing _if_ the node is properly configured
-# (hostname, name resolution, etc), and the Right Thing is to use the
-# address associated with the hostname (it might not be).
-#
-# Setting this to 0.0.0.0 is always wrong.
-listen_address: 127.0.0.1
-
-# Address to broadcast to other Cassandra nodes
-# Leaving this blank will set it to the same value as listen_address
-# broadcast_address: 1.2.3.4
-
-# Internode authentication backend, implementing IInternodeAuthenticator;
-# used to allow/disallow connections from peer nodes.
-# internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
-
-# Whether to start the native transport server.
-# Please note that the address on which the native transport is bound is the
-# same as the rpc_address. The port however is different and specified below.
-start_native_transport: true
-# port for the CQL native transport to listen for clients on
-native_transport_port: 9042
-# The maximum threads for handling requests when the native transport is used.
-# This is similar to rpc_max_threads though the default differs slightly (and
-# there is no native_transport_min_threads, idle threads will always be stopped
-# after 30 seconds).
-# native_transport_max_threads: 128
-#
-# The maximum size of allowed frame. Frame (requests) larger than this will
-# be rejected as invalid. The default is 256MB.
-# native_transport_max_frame_size_in_mb: 256
-
-# Whether to start the thrift rpc server.
-start_rpc: true
-
-# The address to bind the Thrift RPC service and native transport
-# server -- clients connect here.
-#
-# Leaving this blank has the same effect it does for ListenAddress,
-# (i.e. it will be based on the configured hostname of the node).
-#
-# Note that unlike ListenAddress above, it is allowed to specify 0.0.0.0
-# here if you want to listen on all interfaces, but that will break clients 
-# that rely on node auto-discovery.
-rpc_address: 127.0.0.1
-# port for Thrift to listen for clients on
-rpc_port: 9160
-
-# enable or disable keepalive on rpc/native connections
-rpc_keepalive: true
-
-# Cassandra provides two out-of-the-box options for the RPC Server:
-#
-# sync  -> One thread per thrift connection. For a very large number of clients, memory
-#          will be your limiting factor. On a 64 bit JVM, 180KB is the minimum stack size
-#          per thread, and that will correspond to your use of virtual memory (but physical memory
-#          may be limited depending on use of stack space).
-#
-# hsha  -> Stands for "half synchronous, half asynchronous." All thrift clients are handled
-#          asynchronously using a small number of threads that does not vary with the amount
-#          of thrift clients (and thus scales well to many clients). The rpc requests are still
-#          synchronous (one thread per active request). If hsha is selected then it is essential
-#          that rpc_max_threads is changed from the default value of unlimited.
-#
-# The default is sync because on Windows hsha is about 30% slower.  On Linux,
-# sync/hsha performance is about the same, with hsha of course using less memory.
-#
-# Alternatively,  can provide your own RPC server by providing the fully-qualified class name
-# of an o.a.c.t.TServerFactory that can create an instance of it.
-rpc_server_type: sync
-
-# Uncomment rpc_min|max_thread to set request pool size limits.
-#
-# Regardless of your choice of RPC server (see above), the number of maximum requests in the
-# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync
-# RPC server, it also dictates the number of clients that can be connected at all).
-#
-# The default is unlimited and thus provides no protection against clients overwhelming the server. You are
-# encouraged to set a maximum that makes sense for you in production, but do keep in mind that
-# rpc_max_threads represents the maximum number of client requests this server may execute concurrently.
-#
-# rpc_min_threads: 16
-# rpc_max_threads: 2048
-
-# uncomment to set socket buffer sizes on rpc connections
-# rpc_send_buff_size_in_bytes:
-# rpc_recv_buff_size_in_bytes:
-
-# Uncomment to set socket buffer size for internode communication
-# Note that when setting this, the buffer size is limited by net.core.wmem_max
-# and when not setting it it is defined by net.ipv4.tcp_wmem
-# See:
-# /proc/sys/net/core/wmem_max
-# /proc/sys/net/core/rmem_max
-# /proc/sys/net/ipv4/tcp_wmem
-# /proc/sys/net/ipv4/tcp_wmem
-# and: man tcp
-# internode_send_buff_size_in_bytes:
-# internode_recv_buff_size_in_bytes:
-
-# Frame size for thrift (maximum message length).
-thrift_framed_transport_size_in_mb: 15
-
-# Set to true to have Cassandra create a hard link to each sstable
-# flushed or streamed locally in a backups/ subdirectory of the
-# keyspace data.  Removing these links is the operator's
-# responsibility.
-incremental_backups: false
-
-# Whether or not to take a snapshot before each compaction.  Be
-# careful using this option, since Cassandra won't clean up the
-# snapshots for you.  Mostly useful if you're paranoid when there
-# is a data format change.
-snapshot_before_compaction: false
-
-# Whether or not a snapshot is taken of the data before keyspace truncation
-# or dropping of column families. The STRONGLY advised default of true 
-# should be used to provide data safety. If you set this flag to false, you will
-# lose data on truncation or drop.
-auto_snapshot: true
-
-# When executing a scan, within or across a partition, we need to keep the
-# tombstones seen in memory so we can return them to the coordinator, which
-# will use them to make sure other replicas also know about the deleted rows.
-# With workloads that generate a lot of tombstones, this can cause performance
-# problems and even exaust the server heap.
-# (http://www.datastax.com/dev/blog/cassandra-anti-patterns-queues-and-queue-like-datasets)
-# Adjust the thresholds here if you understand the dangers and want to
-# scan more tombstones anyway.  These thresholds may also be adjusted at runtime
-# using the StorageService mbean.
-tombstone_warn_threshold: 1000
-tombstone_failure_threshold: 100000
-
-# Granularity of the collation index of rows within a partition.
-# Increase if your rows are large, or if you have a very large
-# number of rows per partition.  The competing goals are these:
-#   1) a smaller granularity means more index entries are generated
-#      and looking up rows withing the partition by collation column
-#      is faster
-#   2) but, Cassandra will keep the collation index in memory for hot
-#      rows (as part of the key cache), so a larger granularity means
-#      you can cache more hot rows
-column_index_size_in_kb: 64
-
-
-# Log WARN on any batch size exceeding this value. 5kb per batch by default.
-# Caution should be taken on increasing the size of this threshold as it can lead to node instability.
-batch_size_warn_threshold_in_kb: 5
-
-# Size limit for rows being compacted in memory.  Larger rows will spill
-# over to disk and use a slower two-pass compaction process.  A message
-# will be logged specifying the row key.
-in_memory_compaction_limit_in_mb: 64
-
-# Number of simultaneous compactions to allow, NOT including
-# validation "compactions" for anti-entropy repair.  Simultaneous
-# compactions can help preserve read performance in a mixed read/write
-# workload, by mitigating the tendency of small sstables to accumulate
-# during a single long running compactions. The default is usually
-# fine and if you experience problems with compaction running too
-# slowly or too fast, you should look at
-# compaction_throughput_mb_per_sec first.
-#
-# concurrent_compactors defaults to the number of cores.
-# Uncomment to make compaction mono-threaded, the pre-0.8 default.
-#concurrent_compactors: 1
-
-# Multi-threaded compaction. When enabled, each compaction will use
-# up to one thread per core, plus one thread per sstable being merged.
-# This is usually only useful for SSD-based hardware: otherwise, 
-# your concern is usually to get compaction to do LESS i/o (see:
-# compaction_throughput_mb_per_sec), not more.
-multithreaded_compaction: false
-
-# Throttles compaction to the given total throughput across the entire
-# system. The faster you insert data, the faster you need to compact in
-# order to keep the sstable count down, but in general, setting this to
-# 16 to 32 times the rate you are inserting data is more than sufficient.
-# Setting this to 0 disables throttling. Note that this account for all types
-# of compaction, including validation compaction.
-compaction_throughput_mb_per_sec: 16
-
-# Track cached row keys during compaction, and re-cache their new
-# positions in the compacted sstable.  Disable if you use really large
-# key caches.
-compaction_preheat_key_cache: true
-
-# Throttles all outbound streaming file transfers on this node to the
-# given total throughput in Mbps. This is necessary because Cassandra does
-# mostly sequential IO when streaming data during bootstrap or repair, which
-# can lead to saturating the network connection and degrading rpc performance.
-# When unset, the default is 200 Mbps or 25 MB/s.
-# stream_throughput_outbound_megabits_per_sec: 200
-
-# Throttles all streaming file transfer between the datacenters,
-# this setting allows users to throttle inter dc stream throughput in addition
-# to throttling all network stream traffic as configured with
-# stream_throughput_outbound_megabits_per_sec
-# inter_dc_stream_throughput_outbound_megabits_per_sec:
-
-# How long the coordinator should wait for read operations to complete
-read_request_timeout_in_ms: 5000
-# How long the coordinator should wait for seq or index scans to complete
-range_request_timeout_in_ms: 10000
-# How long the coordinator should wait for writes to complete
-write_request_timeout_in_ms: 2000
-# How long a coordinator should continue to retry a CAS operation
-# that contends with other proposals for the same row
-cas_contention_timeout_in_ms: 1000
-# How long the coordinator should wait for truncates to complete
-# (This can be much longer, because unless auto_snapshot is disabled
-# we need to flush first so we can snapshot before removing the data.)
-truncate_request_timeout_in_ms: 60000
-# The default timeout for other, miscellaneous operations
-request_timeout_in_ms: 10000
-
-# Enable operation timeout information exchange between nodes to accurately
-# measure request timeouts.  If disabled, replicas will assume that requests
-# were forwarded to them instantly by the coordinator, which means that
-# under overload conditions we will waste that much extra time processing 
-# already-timed-out requests.
-#
-# Warning: before enabling this property make sure to ntp is installed
-# and the times are synchronized between the nodes.
-cross_node_timeout: false
-
-# Enable socket timeout for streaming operation.
-# When a timeout occurs during streaming, streaming is retried from the start
-# of the current file. This _can_ involve re-streaming an important amount of
-# data, so you should avoid setting the value too low.
-# Default value is 0, which never timeout streams.
-# streaming_socket_timeout_in_ms: 0
-
-# phi value that must be reached for a host to be marked down.
-# most users should never need to adjust this.
-# phi_convict_threshold: 8
-
-# endpoint_snitch -- Set this to a class that implements
-# IEndpointSnitch.  The snitch has two functions:
-# - it teaches Cassandra enough about your network topology to route
-#   requests efficiently
-# - it allows Cassandra to spread replicas around your cluster to avoid
-#   correlated failures. It does this by grouping machines into
-#   "datacenters" and "racks."  Cassandra will do its best not to have
-#   more than one replica on the same "rack" (which may not actually
-#   be a physical location)
-#
-# IF YOU CHANGE THE SNITCH AFTER DATA IS INSERTED INTO THE CLUSTER,
-# YOU MUST RUN A FULL REPAIR, SINCE THE SNITCH AFFECTS WHERE REPLICAS
-# ARE PLACED.
-#
-# Out of the box, Cassandra provides
-#  - SimpleSnitch:
-#    Treats Strategy order as proximity. This can improve cache
-#    locality when disabling read repair.  Only appropriate for
-#    single-datacenter deployments.
-#  - GossipingPropertyFileSnitch
-#    This should be your go-to snitch for production use.  The rack
-#    and datacenter for the local node are defined in
-#    cassandra-rackdc.properties and propagated to other nodes via
-#    gossip.  If cassandra-topology.properties exists, it is used as a
-#    fallback, allowing migration from the PropertyFileSnitch.
-#  - PropertyFileSnitch:
-#    Proximity is determined by rack and data center, which are
-#    explicitly configured in cassandra-topology.properties.
-#  - Ec2Snitch:
-#    Appropriate for EC2 deployments in a single Region. Loads Region
-#    and Availability Zone information from the EC2 API. The Region is
-#    treated as the datacenter, and the Availability Zone as the rack.
-#    Only private IPs are used, so this will not work across multiple
-#    Regions.
-#  - Ec2MultiRegionSnitch:
-#    Uses public IPs as broadcast_address to allow cross-region
-#    connectivity.  (Thus, you should set seed addresses to the public
-#    IP as well.) You will need to open the storage_port or
-#    ssl_storage_port on the public IP firewall.  (For intra-Region
-#    traffic, Cassandra will switch to the private IP after
-#    establishing a connection.)
-#  - RackInferringSnitch:
-#    Proximity is determined by rack and data center, which are
-#    assumed to correspond to the 3rd and 2nd octet of each node's IP
-#    address, respectively.  Unless this happens to match your
-#    deployment conventions, this is best used as an example of
-#    writing a custom Snitch class and is provided in that spirit.
-#
-# You can use a custom Snitch by setting this to the full class name
-# of the snitch, which will be assumed to be on your classpath.
-endpoint_snitch: SimpleSnitch
-
-# controls how often to perform the more expensive part of host score
-# calculation
-dynamic_snitch_update_interval_in_ms: 100 
-# controls how often to reset all host scores, allowing a bad host to
-# possibly recover
-dynamic_snitch_reset_interval_in_ms: 600000
-# if set greater than zero and read_repair_chance is < 1.0, this will allow
-# 'pinning' of replicas to hosts in order to increase cache capacity.
-# The badness threshold will control how much worse the pinned host has to be
-# before the dynamic snitch will prefer other replicas over it.  This is
-# expressed as a double which represents a percentage.  Thus, a value of
-# 0.2 means Cassandra would continue to prefer the static snitch values
-# until the pinned host was 20% worse than the fastest.
-dynamic_snitch_badness_threshold: 0.1
-
-# request_scheduler -- Set this to a class that implements
-# RequestScheduler, which will schedule incoming client requests
-# according to the specific policy. This is useful for multi-tenancy
-# with a single Cassandra cluster.
-# NOTE: This is specifically for requests from the client and does
-# not affect inter node communication.
-# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place
-# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of
-# client requests to a node with a separate queue for each
-# request_scheduler_id. The scheduler is further customized by
-# request_scheduler_options as described below.
-request_scheduler: org.apache.cassandra.scheduler.NoScheduler
-
-# Scheduler Options vary based on the type of scheduler
-# NoScheduler - Has no options
-# RoundRobin
-#  - throttle_limit -- The throttle_limit is the number of in-flight
-#                      requests per client.  Requests beyond 
-#                      that limit are queued up until
-#                      running requests can complete.
-#                      The value of 80 here is twice the number of
-#                      concurrent_reads + concurrent_writes.
-#  - default_weight -- default_weight is optional and allows for
-#                      overriding the default which is 1.
-#  - weights -- Weights are optional and will default to 1 or the
-#               overridden default_weight. The weight translates into how
-#               many requests are handled during each turn of the
-#               RoundRobin, based on the scheduler id.
-#
-# request_scheduler_options:
-#    throttle_limit: 80
-#    default_weight: 5
-#    weights:
-#      Keyspace1: 1
-#      Keyspace2: 5
-
-# request_scheduler_id -- An identifier based on which to perform
-# the request scheduling. Currently the only valid option is keyspace.
-# request_scheduler_id: keyspace
-
-# Enable or disable inter-node encryption
-# Default settings are TLS v1, RSA 1024-bit keys (it is imperative that
-# users generate their own keys) TLS_RSA_WITH_AES_128_CBC_SHA as the cipher
-# suite for authentication, key exchange and encryption of the actual data transfers.
-# Use the DHE/ECDHE ciphers if running in FIPS 140 compliant mode.
-# NOTE: No custom encryption options are enabled at the moment
-# The available internode options are : all, none, dc, rack
-#
-# If set to dc cassandra will encrypt the traffic between the DCs
-# If set to rack cassandra will encrypt the traffic between the racks
-#
-# The passwords used in these options must match the passwords used when generating
-# the keystore and truststore.  For instructions on generating these files, see:
-# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore
-#
-server_encryption_options:
-    internode_encryption: none
-    keystore: conf/.keystore
-    keystore_password: cassandra
-    truststore: conf/.truststore
-    truststore_password: cassandra
-    # More advanced defaults below:
-    # protocol: TLS
-    # algorithm: SunX509
-    # store_type: JKS
-    # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
-    # require_client_auth: false
-
-# enable or disable client/server encryption.
-client_encryption_options:
-    enabled: false
-    keystore: conf/.keystore
-    keystore_password: cassandra
-    # require_client_auth: false
-    # Set trustore and truststore_password if require_client_auth is true
-    # truststore: conf/.truststore
-    # truststore_password: cassandra
-    # More advanced defaults below:
-    # protocol: TLS
-    # algorithm: SunX509
-    # store_type: JKS
-    # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
-
-# internode_compression controls whether traffic between nodes is
-# compressed.
-# can be:  all  - all traffic is compressed
-#          dc   - traffic between different datacenters is compressed
-#          none - nothing is compressed.
-internode_compression: all
-
-# Enable or disable tcp_nodelay for inter-dc communication.
-# Disabling it will result in larger (but fewer) network packets being sent,
-# reducing overhead from the TCP protocol itself, at the cost of increasing
-# latency if you block for cross-datacenter responses.
-inter_dc_tcp_nodelay: false
-
-# Enable or disable kernel page cache preheating from contents of the key cache after compaction.
-# When enabled it would preheat only first "page" (4KB) of each row to optimize
-# for sequential access. Note: This could be harmful for fat rows, see CASSANDRA-4937
-# for further details on that topic.
-preheat_kernel_page_cache: false

+ 0 - 1
config/cassandra/cleanup-keyspace.cql

@@ -1 +0,0 @@
-DROP KEYSPACE tfb;

+ 0 - 9
config/cassandra/create-keyspace.cql

@@ -1,9 +0,0 @@
-
-CREATE KEYSPACE tfb WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1 };
-USE tfb;
-
-CREATE TABLE  World (
-  id int,
-  randomNumber int,
-  PRIMARY KEY (id)
-);

+ 0 - 6
config/cassandra/db-data-gen.py

@@ -1,6 +0,0 @@
-from random import randint
-
-print "USE tfb;"
-
-for i in range(1, 10001):
-  print "INSERT INTO world (id, randomnumber) VALUES (%d, %d);" % (i, randint(1, 10000))

+ 0 - 44
config/cassandra/log4j-server.properties

@@ -1,44 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# for production, you should probably set pattern to %c instead of %l.  
-# (%l is slower.)
-
-# output messages into a rolling log file as well as stdout
-log4j.rootLogger=INFO,stdout,R
-
-# stdout
-log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%5p %d{HH:mm:ss,SSS} %m%n
-
-# rolling log file
-log4j.appender.R=org.apache.log4j.RollingFileAppender
-log4j.appender.R.maxFileSize=20MB
-log4j.appender.R.maxBackupIndex=50
-log4j.appender.R.layout=org.apache.log4j.PatternLayout
-log4j.appender.R.layout.ConversionPattern=%5p [%t] %d{ISO8601} %F (line %L) %m%n
-# Edit the next line to point to your logs directory
-log4j.appender.R.File=/ssd/log/cassandra/system.log
-
-# Application logging options
-#log4j.logger.org.apache.cassandra=DEBUG
-#log4j.logger.org.apache.cassandra.db=DEBUG
-#log4j.logger.org.apache.cassandra.service.StorageProxy=DEBUG
-
-# Adding this to avoid thrift logging disconnect errors.
-log4j.logger.org.apache.thrift.server.TNonblockingServer=ERROR
-

+ 0 - 24
config/elasticsearch/elasticsearch

@@ -1,24 +0,0 @@
-#!/bin/bash
-
-start() {
-  /opt/elasticsearch/bin/elasticsearch -d -p /ssd/elasticsearch/es.pid
-}
-
-stop() {
-  kill -HUP `cat /ssd/elasticsearch/es.pid`
-}
-
-case "$1" in
-  start)
-    start
-    ;;
-  stop)
-    stop
-    ;;
-  restart)
-    stop
-    sleep 10
-    start
-    ;;
-esac
-

+ 0 - 389
config/elasticsearch/elasticsearch.yml

@@ -1,389 +0,0 @@
-##################### Elasticsearch Configuration Example #####################
-
-# This file contains an overview of various configuration settings,
-# targeted at operations staff. Application developers should
-# consult the guide at <http://elasticsearch.org/guide>.
-#
-# The installation procedure is covered at
-# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup.html>.
-#
-# Elasticsearch comes with reasonable defaults for most settings,
-# so you can try it out without bothering with configuration.
-#
-# Most of the time, these defaults are just fine for running a production
-# cluster. If you're fine-tuning your cluster, or wondering about the
-# effect of certain configuration option, please _do ask_ on the
-# mailing list or IRC channel [http://elasticsearch.org/community].
-
-# Any element in the configuration can be replaced with environment variables
-# by placing them in ${...} notation. For example:
-#
-#node.rack: ${RACK_ENV_VAR}
-
-# For information on supported formats and syntax for the config file, see
-# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html>
-
-
-################################### Cluster ###################################
-
-# Cluster name identifies your cluster for auto-discovery. If you're running
-# multiple clusters on the same network, make sure you're using unique names.
-#
-#cluster.name: elasticsearch
-
-
-#################################### Node #####################################
-
-# Node names are generated dynamically on startup, so you're relieved
-# from configuring them manually. You can tie this node to a specific name:
-#
-#node.name: "Franz Kafka"
-
-# Every node can be configured to allow or deny being eligible as the master,
-# and to allow or deny to store the data.
-#
-# Allow this node to be eligible as a master node (enabled by default):
-#
-#node.master: true
-#
-# Allow this node to store data (enabled by default):
-#
-#node.data: true
-
-# You can exploit these settings to design advanced cluster topologies.
-#
-# 1. You want this node to never become a master node, only to hold data.
-#    This will be the "workhorse" of your cluster.
-#
-#node.master: false
-#node.data: true
-#
-# 2. You want this node to only serve as a master: to not store any data and
-#    to have free resources. This will be the "coordinator" of your cluster.
-#
-#node.master: true
-#node.data: false
-#
-# 3. You want this node to be neither master nor data node, but
-#    to act as a "search load balancer" (fetching data from nodes,
-#    aggregating results, etc.)
-#
-#node.master: false
-#node.data: false
-
-# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
-# Node Info API [http://localhost:9200/_nodes] or GUI tools
-# such as <http://www.elasticsearch.org/overview/marvel/>,
-# <http://github.com/karmi/elasticsearch-paramedic>,
-# <http://github.com/lukas-vlcek/bigdesk> and
-# <http://mobz.github.com/elasticsearch-head> to inspect the cluster state.
-
-# A node can have generic attributes associated with it, which can later be used
-# for customized shard allocation filtering, or allocation awareness. An attribute
-# is a simple key value pair, similar to node.key: value, here is an example:
-#
-#node.rack: rack314
-
-# By default, multiple nodes are allowed to start from the same installation location
-# to disable it, set the following:
-#node.max_local_storage_nodes: 1
-
-
-#################################### Index ####################################
-
-# You can set a number of options (such as shard/replica options, mapping
-# or analyzer definitions, translog settings, ...) for indices globally,
-# in this file.
-#
-# Note, that it makes more sense to configure index settings specifically for
-# a certain index, either when creating it or by using the index templates API.
-#
-# See <http://elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules.html> and
-# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
-# for more information.
-
-# Set the number of shards (splits) of an index (5 by default):
-#
-#index.number_of_shards: 5
-
-# Set the number of replicas (additional copies) of an index (1 by default):
-#
-#index.number_of_replicas: 1
-
-# Note, that for development on a local machine, with small indices, it usually
-# makes sense to "disable" the distributed features:
-#
-#index.number_of_shards: 1
-#index.number_of_replicas: 0
-
-# These settings directly affect the performance of index and search operations
-# in your cluster. Assuming you have enough machines to hold shards and
-# replicas, the rule of thumb is:
-#
-# 1. Having more *shards* enhances the _indexing_ performance and allows to
-#    _distribute_ a big index across machines.
-# 2. Having more *replicas* enhances the _search_ performance and improves the
-#    cluster _availability_.
-#
-# The "number_of_shards" is a one-time setting for an index.
-#
-# The "number_of_replicas" can be increased or decreased anytime,
-# by using the Index Update Settings API.
-#
-# Elasticsearch takes care about load balancing, relocating, gathering the
-# results from nodes, etc. Experiment with different settings to fine-tune
-# your setup.
-
-# Use the Index Status API (<http://localhost:9200/A/_status>) to inspect
-# the index status.
-
-
-#################################### Paths ####################################
-
-# Path to directory containing configuration (this file and logging.yml):
-#
-#path.conf: /path/to/conf
-
-# Path to directory where to store index data allocated for this node.
-#
-#path.data: /path/to/data
-path.data: /ssd/elasticsearch/data
-
-#
-# Can optionally include more than one location, causing data to be striped across
-# the locations (a la RAID 0) on a file level, favouring locations with most free
-# space on creation. For example:
-#
-#path.data: /path/to/data1,/path/to/data2
-
-# Path to temporary files:
-#
-#path.work: /path/to/work
-path.work: /ssd/elasticsearch/work
-
-# Path to log files:
-#
-#path.logs: /path/to/logs
-path.logs: /ssd/log/elasticsearch
-
-# Path to where plugins are installed:
-#
-#path.plugins: /path/to/plugins
-
-
-#################################### Plugin ###################################
-
-# If a plugin listed here is not installed for current node, the node will not start.
-#
-#plugin.mandatory: mapper-attachments,lang-groovy
-
-
-################################### Memory ####################################
-
-# Elasticsearch performs poorly when JVM starts swapping: you should ensure that
-# it _never_ swaps.
-#
-# Set this property to true to lock the memory:
-#
-#bootstrap.mlockall: true
-
-# Make sure that the ES_MIN_MEM and ES_MAX_MEM environment variables are set
-# to the same value, and that the machine has enough memory to allocate
-# for Elasticsearch, leaving enough memory for the operating system itself.
-#
-# You should also make sure that the Elasticsearch process is allowed to lock
-# the memory, eg. by using `ulimit -l unlimited`.
-
-
-############################## Network And HTTP ###############################
-
-# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
-# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
-# communication. (the range means that if the port is busy, it will automatically
-# try the next port).
-
-# Set the bind address specifically (IPv4 or IPv6):
-#
-#network.bind_host: 192.168.0.1
-
-# Set the address other nodes will use to communicate with this node. If not
-# set, it is automatically derived. It must point to an actual IP address.
-#
-#network.publish_host: 192.168.0.1
-
-# Set both 'bind_host' and 'publish_host':
-#
-#network.host: 192.168.0.1
-
-# Set a custom port for the node to node communication (9300 by default):
-#
-#transport.tcp.port: 9300
-
-# Enable compression for all communication between nodes (disabled by default):
-#
-#transport.tcp.compress: true
-
-# Set a custom port to listen for HTTP traffic:
-#
-#http.port: 9200
-
-# Set a custom allowed content length:
-#
-#http.max_content_length: 100mb
-
-# Disable HTTP completely:
-#
-#http.enabled: false
-
-
-################################### Gateway ###################################
-
-# The gateway allows for persisting the cluster state between full cluster
-# restarts. Every change to the state (such as adding an index) will be stored
-# in the gateway, and when the cluster starts up for the first time,
-# it will read its state from the gateway.
-
-# There are several types of gateway implementations. For more information, see
-# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-gateway.html>.
-
-# The default gateway type is the "local" gateway (recommended):
-#
-#gateway.type: local
-
-# Settings below control how and when to start the initial recovery process on
-# a full cluster restart (to reuse as much local data as possible when using shared
-# gateway).
-
-# Allow recovery process after N nodes in a cluster are up:
-#
-#gateway.recover_after_nodes: 1
-
-# Set the timeout to initiate the recovery process, once the N nodes
-# from previous setting are up (accepts time value):
-#
-#gateway.recover_after_time: 5m
-
-# Set how many nodes are expected in this cluster. Once these N nodes
-# are up (and recover_after_nodes is met), begin recovery process immediately
-# (without waiting for recover_after_time to expire):
-#
-#gateway.expected_nodes: 2
-
-
-############################# Recovery Throttling #############################
-
-# These settings allow to control the process of shards allocation between
-# nodes during initial recovery, replica allocation, rebalancing,
-# or when adding and removing nodes.
-
-# Set the number of concurrent recoveries happening on a node:
-#
-# 1. During the initial recovery
-#
-#cluster.routing.allocation.node_initial_primaries_recoveries: 4
-#
-# 2. During adding/removing nodes, rebalancing, etc
-#
-#cluster.routing.allocation.node_concurrent_recoveries: 2
-
-# Set to throttle throughput when recovering (eg. 100mb, by default 20mb):
-#
-#indices.recovery.max_bytes_per_sec: 20mb
-
-# Set to limit the number of open concurrent streams when
-# recovering a shard from a peer:
-#
-#indices.recovery.concurrent_streams: 5
-
-
-################################## Discovery ##################################
-
-# Discovery infrastructure ensures nodes can be found within a cluster
-# and master node is elected. Multicast discovery is the default.
-
-# Set to ensure a node sees N other master eligible nodes to be considered
-# operational within the cluster. This should be set to a quorum/majority of 
-# the master-eligible nodes in the cluster.
-#
-#discovery.zen.minimum_master_nodes: 1
-
-# Set the time to wait for ping responses from other nodes when discovering.
-# Set this option to a higher value on a slow or congested network
-# to minimize discovery failures:
-#
-#discovery.zen.ping.timeout: 3s
-
-# For more information, see
-# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html>
-
-# Unicast discovery allows to explicitly control which nodes will be used
-# to discover the cluster. It can be used when multicast is not present,
-# or to restrict the cluster communication-wise.
-#
-# 1. Disable multicast discovery (enabled by default):
-#
-#discovery.zen.ping.multicast.enabled: false
-#
-# 2. Configure an initial list of master nodes in the cluster
-#    to perform discovery when new nodes (master or data) are started:
-#
-#discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
-
-# EC2 discovery allows to use AWS EC2 API in order to perform discovery.
-#
-# You have to install the cloud-aws plugin for enabling the EC2 discovery.
-#
-# For more information, see
-# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-ec2.html>
-#
-# See <http://elasticsearch.org/tutorials/elasticsearch-on-ec2/>
-# for a step-by-step tutorial.
-
-# GCE discovery allows to use Google Compute Engine API in order to perform discovery.
-#
-# You have to install the cloud-gce plugin for enabling the GCE discovery.
-#
-# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-gce>.
-
-# Azure discovery allows to use Azure API in order to perform discovery.
-#
-# You have to install the cloud-azure plugin for enabling the Azure discovery.
-#
-# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-azure>.
-
-################################## Slow Log ##################################
-
-# Shard level query and fetch threshold logging.
-
-#index.search.slowlog.threshold.query.warn: 10s
-#index.search.slowlog.threshold.query.info: 5s
-#index.search.slowlog.threshold.query.debug: 2s
-#index.search.slowlog.threshold.query.trace: 500ms
-
-#index.search.slowlog.threshold.fetch.warn: 1s
-#index.search.slowlog.threshold.fetch.info: 800ms
-#index.search.slowlog.threshold.fetch.debug: 500ms
-#index.search.slowlog.threshold.fetch.trace: 200ms
-
-#index.indexing.slowlog.threshold.index.warn: 10s
-#index.indexing.slowlog.threshold.index.info: 5s
-#index.indexing.slowlog.threshold.index.debug: 2s
-#index.indexing.slowlog.threshold.index.trace: 500ms
-
-################################## GC Logging ################################
-
-#monitor.jvm.gc.young.warn: 1000ms
-#monitor.jvm.gc.young.info: 700ms
-#monitor.jvm.gc.young.debug: 400ms
-
-#monitor.jvm.gc.old.warn: 10s
-#monitor.jvm.gc.old.info: 5s
-#monitor.jvm.gc.old.debug: 2s
-
-################################## Security ################################
-
-# Uncomment if you want to enable JSONP as a valid return transport on the
-# http server. With this enabled, it may pose a security risk, so disabling
-# it unless you need it is recommended (it is disabled by default).
-#
-#http.jsonp.enable: true

+ 0 - 21
config/elasticsearch/es-create-index.sh

@@ -1,21 +0,0 @@
-#!/bin/bash
-
-curl -XDELETE http://localhost:9200/tfb
-
-curl -XPUT 'http://localhost:9200/tfb' -d '
-{
-  "settings": {
-    "index": {
-      "number_of_shards": 1,
-      "number_of_replicas": 1
-    }
-  },
-  "mappings": {
-    "world": {
-      "properties": {
-        "randomNumber": { "type" : "integer", "index" : "not_analyzed" }
-      }
-    }
-  }
-}
-'

+ 0 - 5
config/elasticsearch/es-db-data-gen.py

@@ -1,5 +0,0 @@
-from random import randint
-
-for i in range(1, 10001):
-  print """{ "index" : { "_id" : "%s" } }
-{ "randomNumber" : %s }""" % (i, randint(1, 10000))

+ 23 - 93
config/mongodb.conf

@@ -1,93 +1,23 @@
-# mongodb.conf
-
-# Where to store the data.
-dbpath=/ssd/mongodb
-
-#where to log
-logpath=/ssd/log/mongodb/mongodb.log
-
-logappend=true
-
-bind_ip = 0.0.0.0
-#port = 27017
-
-# Enable journaling, http://www.mongodb.org/display/DOCS/Journaling
-journal=true
-
-# Enables periodic logging of CPU utilization and I/O wait
-#cpu = true
-
-# Turn on/off security.  Off is currently the default
-#noauth = true
-#auth = true
-
-# Verbose logging output.
-#verbose = true
-
-# Inspect all client data for validity on receipt (useful for
-# developing drivers)
-#objcheck = true
-
-# Enable db quota management
-#quota = true
-
-# Set oplogging level where n is
-#   0=off (default)
-#   1=W
-#   2=R
-#   3=both
-#   7=W+some reads
-#oplog = 0
-
-# Diagnostic/debugging option
-#nocursors = true
-
-# Ignore query hints
-#nohints = true
-
-# Disable the HTTP interface (Defaults to localhost:27018).
-#nohttpinterface = true
-
-# Turns off server-side scripting.  This will result in greatly limited
-# functionality
-#noscripting = true
-
-# Turns off table scans.  Any query that would do a table scan fails.
-#notablescan = true
-
-# Disable data file preallocation.
-#noprealloc = true
-
-# Specify .ns file size for new databases.
-# nssize = <size>
-
-# Accout token for Mongo monitoring server.
-#mms-token = <token>
-
-# Server name for Mongo monitoring server.
-#mms-name = <server-name>
-
-# Ping interval for Mongo monitoring server.
-#mms-interval = <seconds>
-
-# Replication Options
-
-# in replicated mongo databases, specify here whether this is a slave or master
-#slave = true
-#source = master.example.com
-# Slave only: specify a single database to replicate
-#only = master.example.com
-# or
-#master = true
-#source = slave.example.com
-
-# Address of a server to pair with.
-#pairwith = <server:port>
-# Address of arbiter server.
-#arbiter = <server:port>
-# Automatically resync if slave data is stale
-#autoresync
-# Custom size for replication operation log.
-#oplogSize = <MB>
-# Size limit for in-memory storage of op ids.
-#opIdMem = <bytes>
+# mongod.conf
+
+# for documentation of all options, see:
+#   http://docs.mongodb.org/manual/reference/configuration-options/
+
+# Where and how to store data.
+storage:
+  dbPath: "/ssd/mongodb"
+  journal:
+    enabled: true
+#  engine:
+#  mmapv1:
+#  wiredTiger:
+
+# where to write logging data.
+systemLog:
+  destination: file
+  logAppend: true
+  path: "/ssd/log/mongodb/mongod.log"
+
+# network interfaces
+net:
+  port: 27017

+ 2 - 2
config/php-fpm.conf

@@ -139,8 +139,8 @@ events.mechanism = epoll
 ;       will be used.
 ; Note: TFB does not run php-fpm as root, and therefore these directives are 
 ;       ignored. Commenting them out avoids spurious log messages
-; user = testrunner
-; group = testrunner
+; user = 
+; group = 
 
 ; The address on which to accept FastCGI requests.
 ; Valid syntaxes are:

+ 0 - 4
config/travis_mysql_setup.sh

@@ -1,4 +0,0 @@
-sed -i 's|\[mysqld\]|\[mysqld\]\
-lower_case_table_names = 1\
-character-set-server=utf8\
-collation-server=utf8_general_ci|g' /etc/mysql/my.cnf

+ 0 - 151
config/travis_setup.sh

@@ -1,151 +0,0 @@
-export DEBIAN_FRONTEND=noninteractive
-
-# Turn on command tracing
-set -x 
-
-# Setup Apt For MongoDB
-#   Due to TechEmpower/FrameworkBenchmarks#989 and travis-ci/travis-ci#2655, 
-#   we put this into a loop
-until timeout 15s sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10; do echo 'Waiting for apt-key' ; done
-echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
-
-# Setup apt for Apache Cassandra
-until timeout 15s sudo apt-key adv --keyserver pgp.mit.edu --recv 4BD736A82B5C1B00; do echo 'Waiting for apt-key' ; done
-sudo apt-add-repository  'deb http://www.apache.org/dist/cassandra/debian 20x main'
-
-# Run installation 
-# DO NOT COPY --force-yes TO ANY NON-TRAVIS-CI SCRIPTS! Seriously, it can cause some 
-# major damage and should only be used inside a VM or Linux Container
-sudo apt-get -q update
-sudo apt-get -q -y --force-yes install -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \
-  mongodb-org \
-  cassandra \
-  openssh-server \
-  mysql-server
-
-# Run as travis user (who already has passwordless sudo)
-ssh-keygen -f /home/travis/.ssh/id_rsa -N '' -t rsa
-cat /home/travis/.ssh/id_rsa.pub > /home/travis/.ssh/authorized_keys
-chmod 600 /home/travis/.ssh/authorized_keys
-
-# Set up the benchmark.cfg for travis user
-# NOTE: Please don't just copy the example config - it causes unexpected
-#       issues when those example variables change
-echo "[Defaults]"                                       > benchmark.cfg
-echo "client_identity_file=/home/travis/.ssh/id_rsa"   >> benchmark.cfg
-echo "database_identity_file=/home/travis/.ssh/id_rsa" >> benchmark.cfg
-echo "client_host=127.0.0.1"                           >> benchmark.cfg
-echo "database_host=127.0.0.1"                         >> benchmark.cfg
-echo "server_host=127.0.0.1"                           >> benchmark.cfg
-echo "client_user=travis"                              >> benchmark.cfg
-echo "database_user=travis"                            >> benchmark.cfg
-echo "runner_user=testrunner"                          >> benchmark.cfg
-
-# Create the new testrunner user
-sudo useradd testrunner
-# Give him a home dir
-sudo mkdir /home/testrunner
-# Make testrunner the owner of his home dir
-sudo chown testrunner:testrunner /home/testrunner
-# Add the testrunner user to every group that the travis user is in
-sudo sed -i 's|:travis|:travis,testrunner,benchmarkdbuser|g' /etc/group
-# Maybe unneeded - add the travis user to the testrunner group
-sudo sed -i 's|testrunner:x:\(.*\):|testrunner:x:\1:travis|g' /etc/group
-# Need to add testrunner to the sudoers group AND default him to a sudoers
-# because the travis user isn't in the sudo group - he's a sudoer.
-echo "testrunner ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
-# Set the default shell for testrunner to /bin/bash
-sudo sed -i 's|/home/testrunner:/bin/sh|/home/testrunner:/bin/bash|g' /etc/passwd
-
-mkdir installs
-sudo chown testrunner:testrunner installs
-
-# =============Setup Databases===========================
-# NOTE: Do not run `--install database` in travis-ci! 
-#       It changes DB configuration files and will break everything
-# =======================================================
-
-# Setup MySQL
-echo "Populating MySQL database"
-#sudo mysqladmin -u root password secret
-#sudo mv /etc/mysql/my.cnf /etc/mysql/my.cnf.orig
-#sudo mv config/my.cnf /etc/mysql/my.cnf
-sudo sed -i 's|#max_connections        = 100|max_connections        = 500|g' /etc/mysql/my.cnf
-sudo restart mysql
-#mysql -uroot -psecret < config/create.sql
-
-# Setup Postgres
-echo "Removing Postgres 9.1 from Travis-CI"
-sudo apt-get remove -qy postgresql postgresql-9.1 postgresql-client-9.1
-sudo apt-get install -qy postgresql-9.3 postgresql-client-9.3
-
-echo "Populating Postgres database"
-psql --version
-sudo useradd benchmarkdbuser -p benchmarkdbpass
-sudo -u postgres psql template1 < config/create-postgres-database.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|max_connections = 255|max_connections = 500|g' /etc/postgresql/9.3/main/postgresql.conf
-sudo service postgresql stop
-sudo service postgresql start 9.3
-
-# Setup Apache Cassandra
-echo "Populating Apache Cassandra database"
-for i in {1..15}; do
-nc -z localhost 9160 && break || sleep 1;
-echo "Waiting for Cassandra ($i/15}"
-done
-nc -z localhost 9160
-if [ $? -eq 0 ]; then
-cat config/cassandra/cleanup-keyspace.cql | sudo cqlsh
-python config/cassandra/db-data-gen.py > config/cassandra/tfb-data.cql
-sudo cqlsh -f config/cassandra/create-keyspace.cql
-sudo cqlsh -f config/cassandra/tfb-data.cql
-else
->&2 echo "Cassandra did not start, skipping"
-fi
-
-# Setup Elasticsearch
-curl -O https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.5.0.deb
-sudo dpkg -i --force-confnew elasticsearch-1.5.0.deb
-sudo update-rc.d elasticsearch defaults 95 10
-sudo service elasticsearch restart
-
-echo "Populating Elasticsearch database"
-for i in {1..15}; do
-nc -z localhost 9200 && break || sleep 1;
-echo "Waiting for Elasticsearch ($i/15}"
-done
-nc -z localhost 9200
-if [ $? -eq 0 ]; then
-curl localhost:9200
-sh config/elasticsearch/es-create-index.sh
-python config/elasticsearch/es-db-data-gen.py > config/elasticsearch/tfb-data.json
-curl -sS -D - -o /dev/null -XPOST localhost:9200/tfb/world/_bulk --data-binary @config/elasticsearch/tfb-data.json
-echo "Elasticsearch DB populated"
-else
->&2 echo "Elasticsearch did not start, skipping"
-fi
-
-# Setup MongoDB
-echo "Populating MongoDB database"
-for i in {1..15}; do
-nc -z localhost 27017 && break || sleep 1;
-echo "Waiting for MongoDB ($i/15}"
-done
-nc -z localhost 27017
-if [ $? -eq 0 ]; then
-mongo < config/create.js
-mongod --version
-else
->&2 echo "MongoDB did not start, skipping"
-fi
-
-# =============Modify Configurations===========================
-# It can be useful to enable debug features for verification 
-# inside Travis-CI
-# =======================================================
-
-sed -i 's|display_errors\] = off|display_errors\] = on|' config/php-fpm.conf
-
-#exit $?

+ 1 - 2
deployment/vagrant-aws/README.md

@@ -17,8 +17,7 @@ production setup costs about TODO.
 
 ## 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
 
 * **Vagrant AWS Plugin** from [here](https://github.com/mitchellh/vagrant-aws)

+ 6 - 33
deployment/vagrant-common/bootstrap.sh

@@ -59,7 +59,6 @@ if [ ! -e "~/.firstboot" ]; then
   echo "export TFB_DATABASE_HOST=$DATABA_IP" >> ~/.bash_profile
   echo "export TFB_CLIENT_USER=$USER" >> ~/.bash_profile
   echo "export TFB_DATABASE_USER=$USER" >> ~/.bash_profile
-  echo "export TFB_RUNNER_USER=testrunner" >> ~/.bash_profile
   echo "export FWROOT=$HOME/FrameworkBenchmarks" >> ~/.bash_profile 
   source ~/.bash_profile
 
@@ -75,12 +74,6 @@ if [ ! -e "~/.firstboot" ]; then
   echo $CLIENT_IP TFB-client   | sudo tee --append /etc/hosts
   echo $SERVER_IP TFB-server   | sudo tee --append /etc/hosts
 
-  # Add user to run tests
-  sudo adduser --disabled-password --gecos "" testrunner
-  # WARN: testrunner will NOT have sudo access by round 11
-  #       please begin migrating scripts to not rely on sudo.
-  sudo bash -c "echo 'testrunner ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-tfb-testrunner"
-
   # Update hostname to reflect our current role
   if [ "$ROLE" != "all" ]; then
     echo "Updating hostname"
@@ -111,8 +104,7 @@ if [ ! -e "~/.firstboot" ]; then
 
     # vboxfs does not support chown or chmod, which we need. 
     # We therefore bind-mount a normal linux directory so we can
-    # use these operations. This enables us to 
-    # use `chown -R testrunner:testrunner $FWROOT/installs` later
+    # use these operations.
     #echo "Mounting over your installs folder"
     #mkdir -p /tmp/TFB_installs
     #mkdir -p /FrameworkBenchmarks/installs
@@ -125,25 +117,11 @@ if [ ! -e "~/.firstboot" ]; then
     source ~/FrameworkBenchmarks/toolset/setup/linux/prerequisites.sh
   #fi
 
-  # Everyone gets SSH access to localhost
-  echo "Setting up SSH access to localhost"
-  ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
-  cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
-  sudo -u testrunner mkdir -p /home/testrunner/.ssh
-  sudo -u testrunner ssh-keygen -t rsa -N '' -f /home/testrunner/.ssh/id_rsa
-  sudo -u testrunner bash -c "cat /home/testrunner/.ssh/id_rsa.pub >> /home/testrunner/.ssh/authorized_keys"
-  sudo -u testrunner bash -c "cat /home/vagrant/.ssh/authorized_keys >> /home/testrunner/.ssh/authorized_keys"
-  chmod 600 ~/.ssh/authorized_keys
-  sudo -u testrunner chmod 600 /home/testrunner/.ssh/authorized_keys
-  
-  export RUNNER=testrunner
-  export ME=$(id -u -n)
-  sudo chown $RUNNER:$RUNNER /home/$RUNNER
-  sudo sed -i 's|:'"$ME"'|:'"$ME"','"$RUNNER"'|g' /etc/group
-  sudo sed -i 's|'"$ME"':x:\(.*\):|'"$ME"':x:\1:'"$RUNNER"'|g' /etc/group
-  sudo sed -i 's|'"$RUNNER"':x:\(.*\):|'"$RUNNER"':x:\1:'"$ME"'|g' /etc/group
-  echo "$RUNNER ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
-  sudo sed -i 's|/home/'"$RUNNER"':.*|/home/'"$RUNNER"':/bin/bash|g' /etc/passwd
+ # Everyone gets SSH access to localhost
+ echo "Setting up SSH access to localhost"
+ ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
+ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
+ chmod 600 ~/.ssh/authorized_keys
 
   # Enable remote SSH access if we are running production environment
   # Note : this is always copied from the local working copy using a
@@ -166,11 +144,6 @@ if [ ! -e "~/.firstboot" ]; then
     chmod 600 ~/.ssh/client ~/.ssh/database
   fi
 
-  # Setup 
-  echo "Installing $ROLE software"
-  cd $FWROOT
-  toolset/run-tests.py --verbose --install $ROLE --install-only --test ''
-
   # Setup a nice welcome message for our guest
   echo "Setting up welcome message"
   sudo rm -f /etc/update-motd.d/51-cloudguest

+ 4 - 6
deployment/vagrant-common/core.rb

@@ -106,12 +106,10 @@ def provider_virtualbox(config, role, ip_address='172.16.0.16')
     # The VirtualBox file system for shared folders (vboxfs)
     # does not support posix's chown/chmod - these can only 
     # be set at mount time, and they are uniform for the entire
-    # shared directory. We require chown, because we have the 
-    # testrunner user account, so this is a problem. To mitigate
-    # the effects, we set the folders and files to 777 permissions. 
-    # Even though we cannot chown them to testrunner, with 777 and 
-    # owner vagrant *most* of the software works ok. Occasional 
-    # issues are still possible. 
+    # shared directory. To mitigate the effects, we set the 
+    # folders and files to 777 permissions. 
+    # With 777 and wner vagrant *most* of the software works ok. 
+    # Occasional issues are still possible. 
     #
     # See mitchellh/vagrant#4997
     # See http://superuser.com/a/640028/136050

+ 3 - 3
frameworks/C++/cpoll_cppsp/benchmark_config.json

@@ -22,7 +22,7 @@
       "versus": "cpoll_cppsp"
     },
     "raw": {
-      "setup_file": "setup",
+      "setup_file": "setup_mysql",
       "db_url": "/db",
       "query_url": "/db?queries=",
       "fortune_url": "/fortune",
@@ -44,7 +44,7 @@
       "versus": "cpoll_cppsp"
     },
     "postgres-raw": {
-      "setup_file": "setup",
+      "setup_file": "setup_postgresql",
       "db_url": "/db_pg_async",
       "query_url": "/db_pg_async?queries=", 
       "port": 16969,
@@ -64,7 +64,7 @@
       "versus": "cpoll_cppsp"
     },
     "postgres-raw-threadpool": {
-      "setup_file": "setup",
+      "setup_file": "setup_postgresql",
       "db_url": "/db_pg_threadpool",
       "query_url": "/db_pg_threadpool?queries=", 
       "port": 16969,

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

@@ -2,7 +2,7 @@
 
 sed -i 's|#define BENCHMARK_DB_HOST ".*"|#define BENCHMARK_DB_HOST "'"$DBHOST"'"|g' www/connectioninfo.H
 
-fw_depends cppsp
+fw_depends postgresql-server-dev-9.3 cppsp
 
 make clean
 make

+ 5 - 0
frameworks/C++/cpoll_cppsp/setup_mysql.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+fw_depends mysql
+
+source ./setup.sh

+ 5 - 0
frameworks/C++/cpoll_cppsp/setup_postgresql.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+fw_depends postgresql
+
+source ./setup.sh

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

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends cutelyst
+fw_depends mysql postgresql cutelyst
 
 sed -i 's|DatabaseHostName=.*|DatabaseHostName='"$DBHOST"'|g' config/config.ini
 sed -i 's|SendDate=.*|SendDate=false|g' config/config.ini

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

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

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

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends cutelyst nginx
+fw_depends mysql postgresql cutelyst nginx
 
 sed -i 's|DatabaseHostName=.*|DatabaseHostName='"$DBHOST"'|g' config/config_socket.ini
 sed -i 's|SendDate=.*|SendDate=false|g' config/config_socket.ini

+ 1 - 0
frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp

@@ -23,6 +23,7 @@ static QMutex mutex;
 
 cutelyst_benchmarks::cutelyst_benchmarks(QObject *parent) : Application(parent)
 {
+    qsrand(QDateTime::currentMSecsSinceEpoch());
 }
 
 cutelyst_benchmarks::~cutelyst_benchmarks()

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

@@ -2,12 +2,12 @@
 
 #include <Cutelyst/Plugins/Utils/Sql>
 
-#include <QtSql/QSqlQuery>
+#include <QSqlQuery>
 
-#include <QtCore/QThread>
-#include <QtCore/QJsonDocument>
-#include <QtCore/QJsonObject>
-#include <QtCore/QJsonArray>
+#include <QThread>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
 
 DatabaseUpdatesTest::DatabaseUpdatesTest(QObject *parent) : Controller(parent)
 {

+ 21 - 31
frameworks/C++/cutelyst/src/fortunetest.cpp

@@ -2,14 +2,9 @@
 
 #include <Cutelyst/Plugins/Utils/Sql>
 
-#include <QStringBuilder>
+#include <QSqlQuery>
 
-#include <QtSql/QSqlQuery>
-
-#include <QtCore/QThread>
-#include <QtCore/QJsonDocument>
-#include <QtCore/QJsonObject>
-#include <QtCore/QJsonArray>
+#include <QThread>
 
 FortuneTest::FortuneTest(QObject *parent) : Controller(parent)
 {
@@ -18,30 +13,20 @@ FortuneTest::FortuneTest(QObject *parent) : Controller(parent)
 
 void FortuneTest::fortunes_raw_postgres(Context *c)
 {
-    QSqlQuery query = postgresQuery();
+    QSqlQuery query = CPreparedSqlQueryForDatabase(
+                QLatin1String("SELECT id, message FROM fortune"),
+                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
     auto fortunes = processQuery(c, query);
     renderRaw(c, fortunes);
 }
 
 void FortuneTest::fortunes_raw_mysql(Context *c)
 {
-    QSqlQuery query = mysqlQuery();
-    auto fortunes = processQuery(c, query);
-    renderRaw(c, fortunes);
-}
-
-QSqlQuery FortuneTest::postgresQuery()
-{
-    return CPreparedSqlQueryForDatabase(
-                QLatin1String("SELECT id, message FROM fortune"),
-                QSqlDatabase::database(QLatin1String("postgres-") + QThread::currentThread()->objectName()));
-}
-
-QSqlQuery FortuneTest::mysqlQuery()
-{
-    return CPreparedSqlQueryForDatabase(
+    QSqlQuery query = CPreparedSqlQueryForDatabase(
                 QLatin1String("SELECT id, message FROM fortune"),
                 QSqlDatabase::database(QLatin1String("mysql-") + QThread::currentThread()->objectName()));
+    auto fortunes = processQuery(c, query);
+    renderRaw(c, fortunes);
 }
 
 static bool caseSensitiveLessThan(const Fortune &a1, const Fortune &a2)
@@ -59,14 +44,12 @@ FortuneList FortuneTest::processQuery(Context *c, QSqlQuery &query)
     }
 
     while (query.next()) {
-        fortunes.append(qMakePair(query.value(0).toInt(), query.value(1).toString()));
+        fortunes.push_back({query.value(0).toInt(), query.value(1).toString()});
     }
-    fortunes.append(qMakePair(0, QStringLiteral("Additional fortune added at request time.")));
+    fortunes.push_back({0, QStringLiteral("Additional fortune added at request time.")});
 
     qSort(fortunes.begin(), fortunes.end(), caseSensitiveLessThan);
 
-    c->response()->setContentType(QStringLiteral("text/html; charset=UTF-8"));
-
     return fortunes;
 }
 
@@ -79,12 +62,19 @@ void FortuneTest::renderRaw(Context *c, const FortuneList &fortunes)
                               "<body>"
                               "<table>"
                               "<tr><th>id</th><th>message</th></tr>"));
-
-    Q_FOREACH (const Fortune &fortune, fortunes) {
-        out.append(QLatin1String("<tr><td>") % QString::number(fortune.first) % QLatin1String("</td><td>") % fortune.second.toHtmlEscaped() % QLatin1String("</td></tr>"));
+    out.reserve(4096);
+
+    for (const Fortune &fortune : fortunes) {
+        out.append(QStringLiteral("<tr><td>"))
+                .append(QString::number(fortune.first))
+                .append(QStringLiteral("</td><td>"))
+                .append(fortune.second.toHtmlEscaped())
+                .append(QStringLiteral("</td></tr>"));
     }
 
     out.append(QStringLiteral("</table></body></html>"));
 
-    c->response()->setBody(out);
+    auto response = c->response();
+    response->setBody(out);
+    response->setContentType(QStringLiteral("text/html; charset=UTF-8"));
 }

+ 2 - 4
frameworks/C++/cutelyst/src/fortunetest.h

@@ -5,8 +5,8 @@
 
 using namespace Cutelyst;
 
-typedef QPair<int, QString> Fortune;
-typedef QList<Fortune> FortuneList;
+typedef std::pair<int, QString> Fortune;
+typedef std::vector<Fortune> FortuneList;
 
 class QSqlQuery;
 class FortuneTest : public Controller
@@ -23,8 +23,6 @@ public:
     void fortunes_raw_mysql(Context *c);
 
 private:
-    inline QSqlQuery postgresQuery();
-    inline QSqlQuery mysqlQuery();
     inline FortuneList processQuery(Context *c, QSqlQuery &query);
     inline void renderRaw(Context *c, const FortuneList &fortunes);
 };

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

@@ -1,7 +1,7 @@
 #include "jsontest.h"
 
-#include <QtCore/QJsonDocument>
-#include <QtCore/QJsonObject>
+#include <QJsonDocument>
+#include <QJsonObject>
 
 JsonTest::JsonTest(QObject *parent) : Controller(parent)
 {

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

@@ -2,12 +2,12 @@
 
 #include <Cutelyst/Plugins/Utils/Sql>
 
-#include <QtSql/QSqlQuery>
+#include <QSqlQuery>
 
-#include <QtCore/QThread>
-#include <QtCore/QJsonDocument>
-#include <QtCore/QJsonObject>
-#include <QtCore/QJsonArray>
+#include <QThread>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
 
 MultipleDatabaseQueriesTest::MultipleDatabaseQueriesTest(QObject *parent) : Controller(parent)
 {

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

@@ -26,8 +26,8 @@ QString setupHeader(Context *c)
 
 void Root::End(Context *c)
 {
-    static QString lastDate = setupHeader(c);
-    static QElapsedTimer timer = timerSetup(c);
+    static thread_local QString lastDate = setupHeader(c);
+    static thread_local QElapsedTimer timer = timerSetup(c);
     if (timer.hasExpired(1000)) {
         lastDate = setupHeader(c);
         timer.restart();

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

@@ -10,7 +10,7 @@
 
 SingleDatabaseQueryTest::SingleDatabaseQueryTest(QObject *parent) : Controller(parent)
 {
-    qsrand(QDateTime::currentMSecsSinceEpoch());
+
 }
 
 void SingleDatabaseQueryTest::db_postgres(Context *c)

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

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

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

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+fw_depends mysql apache 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

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

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+fw_depends postgresql apache 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

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

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

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

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

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

@@ -1,6 +1,6 @@
 #! /bin/bash
-
-fw_depends silicon lwan
+ 
+fw_depends mysql silicon lwan
 
 rm -rf build
 mkdir build

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

@@ -1,6 +1,6 @@
 #! /bin/bash
 
-fw_depends silicon microhttpd
+fw_depends mysql silicon microhttpd
 
 rm -rf build
 mkdir build

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

@@ -1,6 +1,6 @@
 #! /bin/bash
 
-fw_depends silicon microhttpd
+fw_depends mysql silicon microhttpd
 
 rm -rf build
 mkdir build

+ 1 - 1
frameworks/C++/treefrog/setup-mongodb.sh

@@ -5,7 +5,7 @@ sed -i 's|HostName=.*|HostName='"$DBHOST"'|g' config/mongodb.ini
 sed -i 's|DriverType=.*|DriverType=QMYSQL|g' config/database.ini
 sed -i 's|MultiProcessingModule=.*|MultiProcessingModule=thread|g' config/application.ini
 
-fw_depends treefrog
+fw_depends mongodb treefrog
 
 # 1. Generate Makefile
 qmake -r CONFIG+=release

+ 1 - 1
frameworks/C++/treefrog/setup-postgres.sh

@@ -5,7 +5,7 @@ sed -i 's|HostName=.*|HostName='"$DBHOST"'|g' config/mongodb.ini
 sed -i 's|DriverType=.*|DriverType=QPSQL|g' config/database.ini
 sed -i 's|MultiProcessingModule=.*|MultiProcessingModule=thread|g' config/application.ini
 
-fw_depends treefrog
+fw_depends postgresql treefrog
 
 # 1. Generate Makefile
 qmake -r CONFIG+=release

+ 1 - 1
frameworks/C++/treefrog/setup-thread.sh

@@ -5,7 +5,7 @@ sed -i 's|HostName=.*|HostName='"$DBHOST"'|g' config/mongodb.ini
 sed -i 's|DriverType=.*|DriverType=QMYSQL|g' config/database.ini
 sed -i 's|MultiProcessingModule=.*|MultiProcessingModule=thread|g' config/application.ini
 
-fw_depends treefrog
+fw_depends mysql treefrog
 
 # 1. Generate Makefile
 qmake -r CONFIG+=release

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

@@ -5,7 +5,7 @@ sed -i 's|HostName=.*|HostName='"$DBHOST"'|g' config/mongodb.ini
 sed -i 's|DriverType=.*|DriverType=QMYSQL|g' config/database.ini
 sed -i 's|MultiProcessingModule=.*|MultiProcessingModule=hybrid|g' config/application.ini
 
-fw_depends treefrog
+fw_depends mysql treefrog
 
 # 1. Generate Makefile
 qmake -r CONFIG+=release

+ 1 - 1
frameworks/C++/ulib/setup_mongodb.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends ulib
+fw_depends mongodb ulib
 
 # Travis is broken
 if [ "$TRAVIS" != "true" ]; then

+ 1 - 1
frameworks/C++/ulib/setup_mysql.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends ulib
+fw_depends mysql ulib
 
 # Travis is broken
 if [ "$TRAVIS" != "true" ]; then

+ 1 - 1
frameworks/C++/ulib/setup_postgres.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends ulib
+fw_depends postgresql ulib
 
 MAX_THREADS=$(( 2 * $MAX_THREADS ))
 

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

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends apache wt
+fw_depends mysql apache wt
 
 sed -i 's|INSERT_DB_HOST_HERE|'"${DBHOST}"'|g' benchmark.cpp
 

+ 1 - 1
frameworks/C++/wt/setup_postgres.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends apache wt
+fw_depends postgresql apache wt
 
 sed -i 's|INSERT_DB_HOST_HERE|'"${DBHOST}"'|g' benchmark.cpp
 

+ 3 - 3
frameworks/C/h2o/CMakeLists.txt

@@ -8,9 +8,9 @@ find_path(MUSTACHE_C_INCLUDE mustache.h)
 find_path(YAJL_INCLUDE yajl/yajl_gen.h)
 set(COMMON_OPTIONS -flto -pthread)
 add_compile_options(-std=gnu11 -pedantic -Wall -Wextra ${COMMON_OPTIONS})
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fstack-protector-all -D_FORTIFY_SOURCE=2")
-set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast")
-set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -Ofast")
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_FORTIFY_SOURCE=2")
+set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
+set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O3")
 add_definitions(-DH2O_USE_LIBUV=0)
 include_directories(src ${H2O_INCLUDE} ${MUSTACHE_C_INCLUDE} ${YAJL_INCLUDE})
 file(GLOB SOURCES "src/*.c")

+ 13 - 2
frameworks/C/h2o/setup.sh

@@ -1,12 +1,22 @@
 #!/bin/bash
 
-fw_depends h2o mustache-c yajl
+fw_depends postgresql h2o mustache-c yajl
 
 H2O_APP_HOME="${IROOT}/h2o_app"
 BUILD_DIR="${H2O_APP_HOME}_build"
 H2O_APP_PROFILE_PORT="54321"
 H2O_APP_PROFILE_URL="http://127.0.0.1:$H2O_APP_PROFILE_PORT"
 
+# A hacky way to detect whether we are running in the physical hardware or the cloud environment.
+if [[ $(nproc) -gt 16 ]]; then
+	# In the physical hardware environment the application server has more CPU cores than the
+	# database server, so we need to reduce the maximum number of database connections per
+	# thread accordingly.
+	DB_CONN=2
+else
+	DB_CONN=8
+fi
+
 build_h2o_app()
 {
 	cmake -DCMAKE_INSTALL_PREFIX="$H2O_APP_HOME" -DCMAKE_BUILD_TYPE=Release \
@@ -25,7 +35,7 @@ run_curl()
 
 run_h2o_app()
 {
-	"$1/h2o_app" -a2 -f "$2/template/fortunes.mustache" -m8 "$3" "$4" \
+	"$1/h2o_app" -a1 -f "$2/template/fortunes.mustache" -m "$DB_CONN" "$3" "$4" \
 		-d "host=$DBHOST dbname=hello_world user=benchmarkdbuser password=benchmarkdbpass" &
 }
 
@@ -54,4 +64,5 @@ build_h2o_app "-fprofile-use"
 make -j "$(nproc)" install
 popd
 rm -rf "$BUILD_DIR"
+echo "Maximum database connections per thread: $DB_CONN"
 run_h2o_app "${H2O_APP_HOME}/bin" "${H2O_APP_HOME}/share/h2o_app"

+ 8 - 7
frameworks/C/h2o/src/database.c

@@ -66,8 +66,10 @@ static const struct {
 } prepared_statement[] = {
 	{FORTUNE_TABLE_NAME, "SELECT * FROM " FORTUNE_TABLE_NAME ";"},
 	{WORLD_TABLE_NAME,
-	 "SELECT * FROM " WORLD_TABLE_NAME " "
-	 "WHERE " WORLD_TABLE_NAME "." ID_FIELD_NAME " = $1::integer;"},
+	 "SELECT * FROM " WORLD_TABLE_NAME " WHERE " ID_FIELD_NAME " = $1::integer;"},
+	{UPDATE_QUERY_NAME,
+	 "UPDATE " WORLD_TABLE_NAME " SET randomNumber = $2::integer "
+	 "WHERE " ID_FIELD_NAME " = $1::integer;"},
 };
 
 static void do_execute_query(db_conn_t *db_conn)
@@ -353,8 +355,7 @@ static void start_database_connect(thread_context_t *ctx, db_conn_t *db_conn)
 			goto error;
 		}
 
-		const char * const conninfo =
-			ctx->global_data->config->db_host ? ctx->global_data->config->db_host : "";
+		const char * const conninfo = ctx->config->db_host ? ctx->config->db_host : "";
 
 		db_conn->conn = PQconnectStart(conninfo);
 
@@ -441,7 +442,7 @@ static void stop_database_write_polling(db_conn_t *db_conn)
 
 void connect_to_database(thread_context_t *ctx)
 {
-	for (size_t i = ctx->db_state.db_conn_num; i < ctx->global_data->config->max_db_conn_num; i++)
+	for (size_t i = ctx->db_state.db_conn_num; i < ctx->config->max_db_conn_num; i++)
 		start_database_connect(ctx, NULL);
 }
 
@@ -475,13 +476,13 @@ int execute_query(thread_context_t *ctx, db_query_param_t *param)
 		db_conn->param = param;
 		do_execute_query(db_conn);
 	}
-	else if (ctx->db_state.query_num < ctx->global_data->config->max_query_num) {
+	else if (ctx->db_state.query_num < ctx->config->max_query_num) {
 		param->l.next = NULL;
 		*ctx->db_state.queries.tail = &param->l;
 		ctx->db_state.queries.tail = &param->l.next;
 		ctx->db_state.query_num++;
 
-		if (ctx->db_state.db_conn_num < ctx->global_data->config->max_db_conn_num)
+		if (ctx->db_state.db_conn_num < ctx->config->max_db_conn_num)
 			start_database_connect(ctx, NULL);
 	}
 	else

+ 8 - 5
frameworks/C/h2o/src/database.h

@@ -22,12 +22,13 @@
 #define DATABASE_H_
 
 #include <h2o.h>
+#include <inttypes.h>
 #include <stdint.h>
 #include <postgresql/libpq-fe.h>
 
 #include "list.h"
+#include "utility.h"
 
-#define COPY_HEADER "PGCOPY\n\377\r\n\0\0\0\0\0\0\0\0\0"
 #define DB_REQ_ERROR "too many concurrent database requests\n"
 #define DB_TIMEOUT_ERROR "database timeout\n"
 #define FORTUNE_TABLE_NAME "Fortune"
@@ -36,13 +37,15 @@
 #define IS_SINGLE_ROW 2
 #define MAX_ID 10000
 #define MESSAGE_FIELD_NAME "message"
+#define UPDATE_QUERY_NAME "Update"
 #define WORLD_TABLE_NAME "World"
 
 #define UPDATE_QUERY \
-	"CREATE TEMP TABLE input(LIKE World INCLUDING ALL) ON COMMIT DROP;" \
-	"COPY input FROM STDIN WITH (FORMAT binary);" \
-	"UPDATE World SET randomNumber = input.randomNumber FROM input " \
-	"WHERE World." ID_FIELD_NAME " = input." ID_FIELD_NAME ";"
+	"EXECUTE \"" WORLD_TABLE_NAME "\"(%" PRIu32 ");" \
+	"EXECUTE \"" UPDATE_QUERY_NAME "\"(%" PRIu32 ", %" PRIu32 ");"
+
+#define MAX_UPDATE_QUERY_LEN \
+	(sizeof(UPDATE_QUERY) + 3 * (sizeof(MKSTR(MAX_ID)) - 1) - 3 * sizeof(PRIu32))
 
 typedef enum {
 	SUCCESS,

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

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

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

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

+ 39 - 29
frameworks/C/h2o/src/fortune.c

@@ -59,6 +59,7 @@ static uintmax_t add_iovec(mustache_api_t *api,
                            void *userdata,
                            const char *buffer,
                            uintmax_t buffer_size);
+static void cleanup_fortunes(void *data);
 static int compare_fortunes(const list_t *x, const list_t *y);
 static void complete_fortunes(struct st_h2o_generator_t *self, h2o_req_t *req);
 static list_t *get_sorted_sublist(list_t *head);
@@ -101,6 +102,7 @@ static uintmax_t add_iovec(mustache_api_t *api,
 	}
 
 	if (ret) {
+		memset(iovec_list->iov + iovec_list->iovcnt, 0, sizeof(*iovec_list->iov));
 		iovec_list->iov[iovec_list->iovcnt].base = (char *) buffer;
 		iovec_list->iov[iovec_list->iovcnt++].len = buffer_size;
 		fortune_ctx->content_length += buffer_size;
@@ -109,6 +111,22 @@ static uintmax_t add_iovec(mustache_api_t *api,
 	return ret;
 }
 
+static void cleanup_fortunes(void *data)
+{
+	fortune_ctx_t * const fortune_ctx = data;
+	const list_t *iter = fortune_ctx->result;
+
+	if (iter)
+		do {
+			const fortune_t * const fortune = H2O_STRUCT_FROM_MEMBER(fortune_t, l, iter);
+
+			if (fortune->data)
+				PQclear(fortune->data);
+
+			iter = iter->next;
+		} while (iter);
+}
+
 static int compare_fortunes(const list_t *x, const list_t *y)
 {
 	const fortune_t * const f1 = H2O_STRUCT_FROM_MEMBER(fortune_t, l, x);
@@ -142,12 +160,7 @@ static list_t *get_sorted_sublist(list_t *head)
 	if (head) {
 		head = head->next;
 
-		while (head && compare_fortunes(tail, head) < 0) {
-			tail = head;
-			head = head->next;
-		}
-
-		while (head && !compare_fortunes(tail, head)) {
+		while (head && compare_fortunes(tail, head) <= 0) {
 			tail = head;
 			head = head->next;
 		}
@@ -209,40 +222,33 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 		ret = SUCCESS;
 
 		for (size_t i = 0; i < num_rows; i++) {
-			const char * const message_data = PQgetvalue(result, i, 1);
-			h2o_iovec_t message = h2o_htmlescape(&fortune_ctx->req->pool,
-			                                     message_data,
-			                                     PQgetlength(result, i, 1));
-			const size_t id_len = PQgetlength(result, i, 0);
-			const size_t fortune_size = offsetof(fortune_t, data) + id_len +
-			                            (message_data == message.base ? message.len : 0);
 			fortune_t * const fortune = h2o_mem_alloc_pool(&fortune_ctx->req->pool,
-			                                               fortune_size);
+			                                               sizeof(*fortune));
 
 			if (fortune) {
-				memset(fortune, 0, offsetof(fortune_t, data));
-				memcpy(fortune->data, PQgetvalue(result, i, 0), id_len);
-				fortune->id.base = fortune->data;
-				fortune->id.len = id_len;
-
-				if (message_data == message.base) {
-					message.base = fortune->data + id_len;
-					memcpy(message.base, message_data, message.len);
-				}
-
-				fortune->message = message;
+				memset(fortune, 0, sizeof(*fortune));
+				fortune->id.base = PQgetvalue(result, i, 0);
+				fortune->id.len = PQgetlength(result, i, 0);
+				fortune->message = h2o_htmlescape(&fortune_ctx->req->pool,
+				                                  PQgetvalue(result, i, 1),
+				                                  PQgetlength(result, i, 1));
 				fortune->l.next = fortune_ctx->result;
 				fortune_ctx->result = &fortune->l;
 				fortune_ctx->num_result++;
+
+				if (!i)
+					fortune->data = result;
 			}
 			else {
 				send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, fortune_ctx->req);
 				ret = DONE;
+
+				if (!i)
+					PQclear(result);
+
 				break;
 			}
 		}
-
-		PQclear(result);
 	}
 	else if (result) {
 		PQclear(result);
@@ -258,7 +264,7 @@ static result_return_t on_fortune_result(db_query_param_t *param, PGresult *resu
 		const size_t iovcnt = MIN(MAX_IOVEC, fortune_ctx->num_result * 5 + 2);
 		const size_t sz = offsetof(iovec_list_t, iov) + iovcnt * sizeof(h2o_iovec_t);
 		char _Alignas(iovec_list_t) mem[sz];
-		iovec_list_t * const iovec_list = (iovec_list_t *) mem;
+		iovec_list_t * const restrict iovec_list = (iovec_list_t *) mem;
 
 		memset(iovec_list, 0, offsetof(iovec_list_t, iov));
 		iovec_list->max_iovcnt = iovcnt;
@@ -365,7 +371,9 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
 	                                                      event_loop.h2o_ctx,
 	                                                      req->conn->ctx);
-	fortune_ctx_t * const fortune_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune_ctx));
+	fortune_ctx_t * const fortune_ctx = h2o_mem_alloc_shared(&req->pool,
+	                                                         sizeof(*fortune_ctx),
+	                                                         cleanup_fortunes);
 
 	if (fortune_ctx) {
 		fortune_t * const fortune = h2o_mem_alloc_pool(&req->pool, sizeof(*fortune));
@@ -390,6 +398,8 @@ int fortunes(struct st_h2o_handler_t *self, h2o_req_t *req)
 			if (execute_query(ctx, &fortune_ctx->param))
 				send_service_unavailable_error(DB_REQ_ERROR, req);
 		}
+		else
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 	}
 	else
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);

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

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

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

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

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

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

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

@@ -22,6 +22,7 @@
 #define REQUEST_H_
 
 #include <h2o.h>
+#include <stdbool.h>
 #include <yajl/yajl_gen.h>
 
 typedef enum {
@@ -44,7 +45,7 @@ const char *get_query_param(const char *query,
                             size_t param_len);
 void register_request_handlers(h2o_hostconf_t *hostconf, h2o_access_log_filehandle_t *log_handle);
 void send_error(http_status_code_t status_code, const char *body, h2o_req_t *req);
-int send_json_response(yajl_gen gen, h2o_generator_t *h2o_generator, h2o_req_t *req);
+int send_json_response(yajl_gen gen, bool free_gen, h2o_req_t *req);
 void send_service_unavailable_error(const char *body, h2o_req_t *req);
 void set_default_response_param(content_type_t content_type,
                                 size_t content_length,

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

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

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

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

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

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

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

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

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

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

+ 437 - 251
frameworks/C/h2o/src/world.c

@@ -19,8 +19,10 @@
 
 #include <assert.h>
 #include <h2o.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <arpa/inet.h>
 #include <postgresql/libpq-fe.h>
 #include <yajl/yajl_gen.h>
@@ -38,13 +40,8 @@
 #define QUERIES_PARAMETER "queries="
 #define RANDOM_NUM_KEY "randomNumber"
 
-typedef enum {
-	NO_UPDATE = 0,
-	CREATE,
-	COPY_1,
-	COPY_2,
-	UPDATE
-} update_state_t;
+typedef struct multiple_query_ctx_t multiple_query_ctx_t;
+typedef struct update_ctx_t update_ctx_t;
 
 typedef struct {
 	uint32_t id;
@@ -52,44 +49,89 @@ typedef struct {
 } query_result_t;
 
 typedef struct {
+	multiple_query_ctx_t *ctx;
+	const char *id_pointer;
+	uint32_t id;
+	int id_format;
+	int id_len;
 	db_query_param_t param;
-	yajl_gen gen;
+} query_param_t;
+
+typedef struct {
 	const char *id_pointer;
 	h2o_req_t *req;
 	uint32_t id;
 	int id_format;
 	int id_len;
+	db_query_param_t param;
 } single_query_ctx_t;
 
+struct multiple_query_ctx_t {
+	yajl_gen gen; // also an error flag
+	h2o_req_t *req;
+	query_param_t *query_param;
+	size_t num_query;
+	size_t num_query_in_progress;
+	size_t num_result;
+	query_result_t res[];
+};
+
 typedef struct {
-	single_query_ctx_t single;
+	char *command;
+	update_ctx_t *ctx;
+	uint32_t random_number;
+	bool update;
+	db_query_param_t param;
+} update_param_t;
+
+struct update_ctx_t {
+	yajl_gen gen; // also an error flag
+	h2o_req_t *req;
+	update_param_t *update_param;
 	size_t num_query;
+	size_t num_query_in_progress;
 	size_t num_result;
-	update_state_t update_state;
 	query_result_t res[];
-} multiple_query_ctx_t;
-
-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 on_database_error(db_query_param_t *param, const char *error_string);
-static void on_database_timeout(db_query_param_t *param);
+};
+
+static void cleanup_multiple_query(void *data);
+static void cleanup_update(void *data);
+static size_t get_query_number(h2o_req_t *req);
+static void initialize_ids(size_t num_query, query_result_t *res, unsigned int *seed);
+static void on_multiple_query_error(db_query_param_t *param, const char *error_string);
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result);
+static void on_multiple_query_timeout(db_query_param_t *param);
+static void on_single_query_error(db_query_param_t *param, const char *error_string);
 static result_return_t on_single_query_result(db_query_param_t *param, PGresult *result);
+static void on_single_query_timeout(db_query_param_t *param);
+static void on_update_error(db_query_param_t *param, const char *error_string);
 static result_return_t on_update_result(db_query_param_t *param, PGresult *result);
-static int on_update_write_ready(db_query_param_t *param, PGconn *db_conn);
+static void on_update_timeout(db_query_param_t *param);
+static void process_result(PGresult *result, query_result_t *out);
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen);
 static void serialize_items(const query_result_t *res,
                             size_t num_result,
                             yajl_gen gen,
                             h2o_req_t *req);
 
-static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
+static void cleanup_multiple_query(void *data)
+{
+	const multiple_query_ctx_t * const query_ctx = data;
+
+	if (query_ctx->gen)
+		yajl_gen_free(query_ctx->gen);
+}
+
+static void cleanup_update(void *data)
+{
+	const update_ctx_t * const update_ctx = data;
+
+	if (update_ctx->gen)
+		yajl_gen_free(update_ctx->gen);
+}
+
+static size_t get_query_number(h2o_req_t *req)
 {
-	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
-	                                                      event_loop.h2o_ctx,
-	                                                      req->conn->ctx);
 	int num_query = 0;
 
 	if (req->query_at < SIZE_MAX) {
@@ -107,156 +149,118 @@ static int do_multiple_queries(update_state_t update_state, h2o_req_t *req)
 	else if (num_query > MAX_QUERIES)
 		num_query = MAX_QUERIES;
 
-	const size_t sz = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
-	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sz);
-
-	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 {
-		// MAX_ID is a relatively small number, so allocate on the stack.
-		DEFINE_BITSET(bitset, MAX_ID);
-
-		memset(&query_ctx->single + 1,
-		       0,
-		       offsetof(multiple_query_ctx_t, res) - sizeof(query_ctx->single));
-		query_ctx->num_query = num_query;
-		query_ctx->update_state = update_state;
+	return num_query;
+}
 
-		size_t max_rand = MAX_ID - query_ctx->num_query + 1;
+static void initialize_ids(size_t num_query, query_result_t *res, unsigned int *seed)
+{
+	// MAX_ID is a relatively small number, so allocate on the stack.
+	DEFINE_BITSET(bitset, MAX_ID);
 
-		for (size_t i = 0; i < query_ctx->num_query; i++) {
-			query_ctx->res[i].id = get_random_number(max_rand, &ctx->random_seed);
+	size_t max_rand = MAX_ID - num_query + 1;
 
-			if (BITSET_ISSET(query_ctx->res[i].id, bitset))
-				query_ctx->res[i].id = max_rand - 1;
+	for (size_t i = 0; i < num_query; i++) {
+		res[i].id = get_random_number(max_rand, seed);
 
-			BITSET_SET(query_ctx->res[i].id++, bitset);
-			max_rand++;
-		}
+		if (BITSET_ISSET(res[i].id, bitset))
+			res[i].id = max_rand - 1;
 
-		query_ctx->single.id = htonl(query_ctx->res->id);
-
-		if (execute_query(ctx, &query_ctx->single.param)) {
-			yajl_gen_free(query_ctx->single.gen);
-			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
+		BITSET_SET(res[i].id++, bitset);
+		max_rand++;
 	}
-
-	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 on_multiple_query_error(db_query_param_t *param, const char *error_string)
 {
-	int ret = EXIT_FAILURE;
-
-	memset(query_ctx, 0, sizeof(*query_ctx));
-	query_ctx->gen = get_json_generator(&req->pool);
+	const query_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(query_param_t,
+	                                                                 param,
+	                                                                 param);
+	multiple_query_ctx_t * const query_ctx = query_param->ctx;
 
 	if (query_ctx->gen) {
-		query_ctx->id_format = 1;
-		query_ctx->id_len = sizeof(query_ctx->id);
-		query_ctx->id_pointer = (const char *) &query_ctx->id;
-		query_ctx->param.command = WORLD_TABLE_NAME;
-		query_ctx->param.nParams = 1;
-		query_ctx->param.on_error = on_database_error;
-		query_ctx->param.on_result = on_result;
-		query_ctx->param.on_timeout = on_database_timeout;
-		query_ctx->param.paramFormats = &query_ctx->id_format;
-		query_ctx->param.paramLengths = &query_ctx->id_len;
-		query_ctx->param.paramValues = &query_ctx->id_pointer;
-		query_ctx->param.flags = IS_PREPARED;
-		query_ctx->param.resultFormat = 1;
-		query_ctx->req = req;
-		ret = EXIT_SUCCESS;
+		yajl_gen_free(query_ctx->gen);
+		query_ctx->gen = NULL;
+		send_error(BAD_GATEWAY, error_string, query_ctx->req);
 	}
 
-	return ret;
-}
-
-static void on_database_error(db_query_param_t *param, const char *error_string)
-{
-	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
-	                                                              param,
-	                                                              param);
-
-	yajl_gen_free(query_ctx->gen);
-	send_error(BAD_GATEWAY, error_string, query_ctx->req);
-}
-
-static void on_database_timeout(db_query_param_t *param)
-{
-	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
-	                                                              param,
-	                                                              param);
-
-	yajl_gen_free(query_ctx->gen);
-	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
+	query_ctx->num_query_in_progress--;
+	h2o_mem_release_shared(query_ctx);
 }
 
 static result_return_t on_multiple_query_result(db_query_param_t *param, PGresult *result)
 {
-	multiple_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(multiple_query_ctx_t,
-	                                                                single.param,
-	                                                                param);
+	query_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(query_param_t,
+	                                                           param,
+	                                                           param);
+	multiple_query_ctx_t * const query_ctx = query_param->ctx;
 
-	if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+	if (query_ctx->gen && PQresultStatus(result) == PGRES_TUPLES_OK) {
 		thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
 		                                                      event_loop.h2o_ctx,
-		                                                      query_ctx->single.req->conn->ctx);
-		uint32_t * const random_number = &query_ctx->res[query_ctx->num_result++].random_number;
+		                                                      query_ctx->req->conn->ctx);
 
-		assert(PQnfields(result) == 2);
-		assert(PQntuples(result) == 1);
-		assert(PQgetlength(result, 0, 1) == sizeof(*random_number));
-		// Use memcpy() in case the result is not aligned.
-		memcpy(random_number, PQgetvalue(result, 0, 1), sizeof(*random_number));
-		*random_number = ntohl(*random_number);
-		PQclear(result);
+		process_result(result, query_ctx->res + query_ctx->num_result);
+		query_ctx->num_query_in_progress--;
+		query_ctx->num_result++;
+
+		const size_t num_query_remaining = query_ctx->num_query - query_ctx->num_result;
 
-		if (query_ctx->num_result < query_ctx->num_query) {
-			query_ctx->single.id = htonl(query_ctx->res[query_ctx->num_result].id);
+		if (query_ctx->num_query_in_progress < num_query_remaining) {
+			const size_t idx = query_ctx->num_result + query_ctx->num_query_in_progress;
 
-			if (!execute_query(ctx, &query_ctx->single.param))
+			query_param->id = htonl(query_ctx->res[idx].id);
+
+			if (!execute_query(ctx, &query_param->param)) {
+				query_ctx->num_query_in_progress++;
+				PQclear(result);
 				return DONE;
+			}
 
-			send_service_unavailable_error(DB_REQ_ERROR, query_ctx->single.req);
+			yajl_gen_free(query_ctx->gen);
+			query_ctx->gen = NULL;
+			send_service_unavailable_error(DB_REQ_ERROR, query_ctx->req);
 		}
-		else if (query_ctx->update_state == NO_UPDATE) {
+		else if (query_ctx->num_result == query_ctx->num_query)
 			serialize_items(query_ctx->res,
 			                query_ctx->num_result,
-			                query_ctx->single.gen,
-			                query_ctx->single.req);
-			return DONE;
-		}
-		else {
-			query_ctx->single.param.command = UPDATE_QUERY;
-			query_ctx->single.param.nParams = 0;
-			query_ctx->single.param.on_result = on_update_result;
-			query_ctx->single.param.on_write_ready = on_update_write_ready;
-			query_ctx->single.param.paramFormats = NULL;
-			query_ctx->single.param.paramLengths = NULL;
-			query_ctx->single.param.paramValues = NULL;
-			query_ctx->single.param.flags = 0;
-
-			if (!execute_query(ctx, &query_ctx->single.param))
-				return DONE;
+			                query_ctx->gen,
+			                query_ctx->req);
 
-			send_service_unavailable_error(DB_REQ_ERROR, query_ctx->single.req);
-		}
-	}
-	else {
-		send_error(BAD_GATEWAY, PQresultErrorMessage(result), query_ctx->single.req);
-		PQclear(result);
+		h2o_mem_release_shared(query_ctx);
 	}
+	else
+		on_multiple_query_error(param, PQresultErrorMessage(result));
 
-	yajl_gen_free(query_ctx->single.gen);
+	PQclear(result);
 	return DONE;
 }
 
+static void on_multiple_query_timeout(db_query_param_t *param)
+{
+	const query_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(query_param_t,
+	                                                                 param,
+	                                                                 param);
+	multiple_query_ctx_t * const query_ctx = query_param->ctx;
+
+	if (query_ctx->gen) {
+		yajl_gen_free(query_ctx->gen);
+		query_ctx->gen = NULL;
+		send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
+	}
+
+	query_ctx->num_query_in_progress--;
+	h2o_mem_release_shared(query_ctx);
+}
+
+static void on_single_query_error(db_query_param_t *param, const char *error_string)
+{
+	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
+	                                                              param,
+	                                                              param);
+
+	send_error(BAD_GATEWAY, error_string, query_ctx->req);
+}
+
 static result_return_t on_single_query_result(db_query_param_t *param, PGresult *result)
 {
 	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
@@ -275,9 +279,17 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		random_number = ntohl(random_number);
 		PQclear(result);
 
-		if (!serialize_item(ntohl(query_ctx->id), random_number, query_ctx->gen) &&
-		    !send_json_response(query_ctx->gen, NULL, query_ctx->req))
-			return DONE;
+		const yajl_gen gen = get_json_generator(&query_ctx->req->pool);
+
+		if (gen) {
+			// The response is small enough, so that it is simpler to copy it
+			// instead of doing a delayed deallocation of the JSON generator.
+			if (!serialize_item(ntohl(query_ctx->id), random_number, gen) &&
+			    !send_json_response(gen, true, query_ctx->req))
+				return DONE;
+
+			yajl_gen_free(gen);
+		}
 
 		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, query_ctx->req);
 	}
@@ -286,119 +298,154 @@ static result_return_t on_single_query_result(db_query_param_t *param, PGresult
 		PQclear(result);
 	}
 
-	yajl_gen_free(query_ctx->gen);
 	return DONE;
 }
 
-static result_return_t on_update_result(db_query_param_t *param, PGresult *result)
+static void on_single_query_timeout(db_query_param_t *param)
 {
-	multiple_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(multiple_query_ctx_t,
-	                                                                single.param,
-	                                                                param);
-	result_return_t ret = SUCCESS;
-	const ExecStatusType status = PQresultStatus(result);
-
-	switch (query_ctx->update_state) {
-		case CREATE:
-		case COPY_2:
-			if (status != PGRES_COMMAND_OK)
-				goto error;
-
-			query_ctx->update_state++;
-			break;
-		case COPY_1:
-			if (status != PGRES_COPY_IN)
-				goto error;
-
-			ret = WANT_WRITE;
-			break;
-		case UPDATE:
-			if (status != PGRES_COMMAND_OK)
-				goto error;
-
-			serialize_items(query_ctx->res,
-			                query_ctx->num_result,
-			                query_ctx->single.gen,
-			                query_ctx->single.req);
-			ret = DONE;
-			break;
-		default:
-			goto error;
-	}
+	single_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(single_query_ctx_t,
+	                                                              param,
+	                                                              param);
 
-	PQclear(result);
-	return ret;
-error:
-	yajl_gen_free(query_ctx->single.gen);
-	send_error(BAD_GATEWAY, PQresultErrorMessage(result), query_ctx->single.req);
-	PQclear(result);
-	return DONE;
+	send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, query_ctx->req);
 }
 
-static int on_update_write_ready(db_query_param_t *param, PGconn *db_conn)
+static void on_update_error(db_query_param_t *param, const char *error_string)
 {
-	multiple_query_ctx_t * const query_ctx = H2O_STRUCT_FROM_MEMBER(multiple_query_ctx_t,
-	                                                                single.param,
-	                                                                param);
-	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
-	                                                      event_loop.h2o_ctx,
-	                                                      query_ctx->single.req->conn->ctx);
-
-	if (query_ctx->num_result == query_ctx->num_query && query_ctx->update_state != COPY_2) {
-		const int rc = PQputCopyData(db_conn, H2O_STRLIT(COPY_HEADER));
-
-		if (!rc)
-			return 1;
-		else if (rc < 0)
-			return rc;
-
-		query_ctx->num_result = 0;
-		query_ctx->update_state = COPY_2;
+	const update_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(update_param_t,
+	                                                                  param,
+	                                                                  param);
+	update_ctx_t * const update_ctx = query_param->ctx;
+
+	if (update_ctx->gen) {
+		yajl_gen_free(update_ctx->gen);
+		update_ctx->gen = NULL;
+		send_error(BAD_GATEWAY, error_string, update_ctx->req);
 	}
 
-	if (query_ctx->num_result < query_ctx->num_query) {
-		assert(query_ctx->num_query - query_ctx->num_result <= MAX_QUERIES);
-
-		// There are at most MAX_QUERIES elements, so allocate on the stack.
-		struct __attribute__ ((__packed__)) {
-			uint16_t field_count;
-			uint32_t id_size;
-			uint32_t id;
-			uint32_t random_number_size;
-			uint32_t random_number;
-		} data[query_ctx->num_query - query_ctx->num_result];
-
-		memset(&data, 0, sizeof(data));
-
-		for (size_t i = 0; i < query_ctx->num_query; i++) {
-			query_ctx->res[i].random_number = get_random_number(MAX_ID, &ctx->random_seed) + 1;
-			data[i].field_count = htons(2);
-			data[i].id_size = htonl(sizeof(data->id));
-			data[i].id = htonl(query_ctx->res[i].id);
-			data[i].random_number_size = htonl(sizeof(data->random_number));
-			data[i].random_number = htonl(query_ctx->res[i].random_number);
-		}
+	update_ctx->num_query_in_progress--;
+	h2o_mem_release_shared(update_ctx);
+}
 
-		const int rc = PQputCopyData(db_conn, (const char *) &data, sizeof(data));
+static result_return_t on_update_result(db_query_param_t *param, PGresult *result)
+{
+	update_param_t * const update_param = H2O_STRUCT_FROM_MEMBER(update_param_t,
+	                                                             param,
+	                                                             param);
+	update_ctx_t * const update_ctx = update_param->ctx;
+	result_return_t ret = DONE;
+
+	if (update_ctx->gen) {
+		if (update_param->update) {
+			if (PQresultStatus(result) == PGRES_COMMAND_OK) {
+				thread_context_t * const ctx =
+					H2O_STRUCT_FROM_MEMBER(thread_context_t,
+					                       event_loop.h2o_ctx,
+					                       update_ctx->req->conn->ctx);
+				const size_t num_query_remaining =
+					update_ctx->num_query - update_ctx->num_result;
+
+				update_ctx->num_query_in_progress--;
+
+				if (update_ctx->num_query_in_progress < num_query_remaining) {
+					const size_t idx = update_ctx->num_result +
+					                   update_ctx->num_query_in_progress;
+
+					update_param->random_number =
+						get_random_number(MAX_ID, &ctx->random_seed) + 1;
+					update_param->update = false;
+					snprintf(update_param->command,
+					         MAX_UPDATE_QUERY_LEN,
+					         UPDATE_QUERY,
+					         update_ctx->res[idx].id,
+					         update_ctx->res[idx].id,
+					         update_param->random_number);
+
+					if (!execute_query(ctx, &update_param->param)) {
+						update_ctx->num_query_in_progress++;
+						PQclear(result);
+						return DONE;
+					}
+
+					yajl_gen_free(update_ctx->gen);
+					update_ctx->gen = NULL;
+					send_service_unavailable_error(DB_REQ_ERROR,
+					                               update_ctx->req);
+				}
+				else if (update_ctx->num_result == update_ctx->num_query)
+					serialize_items(update_ctx->res,
+					                update_ctx->num_result,
+					                update_ctx->gen,
+					                update_ctx->req);
+
+				h2o_mem_release_shared(update_ctx);
+			}
+			else
+				on_update_error(param, PQresultErrorMessage(result));
+		}
+		else if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+			process_result(result, update_ctx->res + update_ctx->num_result);
+			update_ctx->res[update_ctx->num_result].random_number =
+				update_param->random_number;
+			update_ctx->num_result++;
+			update_param->update = true;
+			ret = SUCCESS;
+		}
+		else
+			on_update_error(param, PQresultErrorMessage(result));
+	}
+	else {
+		update_ctx->num_query_in_progress--;
+		h2o_mem_release_shared(update_ctx);
+	}
 
-		if (!rc)
-			return 1;
-		else if (rc < 0)
-			return rc;
+	PQclear(result);
+	return ret;
+}
 
-		query_ctx->num_result = query_ctx->num_query;
+static void on_update_timeout(db_query_param_t *param)
+{
+	const update_param_t * const query_param = H2O_STRUCT_FROM_MEMBER(update_param_t,
+	                                                                  param,
+	                                                                  param);
+	update_ctx_t * const update_ctx = query_param->ctx;
+
+	if (update_ctx->gen) {
+		yajl_gen_free(update_ctx->gen);
+		update_ctx->gen = NULL;
+		send_error(GATEWAY_TIMEOUT, DB_TIMEOUT_ERROR, update_ctx->req);
 	}
 
-	if (query_ctx->num_result == query_ctx->num_query) {
-		const int rc = PQputCopyEnd(db_conn, NULL);
+	update_ctx->num_query_in_progress--;
+	h2o_mem_release_shared(update_ctx);
+}
 
-		if (!rc)
-			return 1;
-		else if (rc < 0)
-			return rc;
+static void process_result(PGresult *result, query_result_t *out)
+{
+	assert(PQnfields(result) == 2);
+	assert(PQntuples(result) == 1);
+
+	const char * const id = PQgetvalue(result, 0, 0);
+	const char * const random_number = PQgetvalue(result, 0, 1);
+
+	if (PQfformat(result, 0)) {
+		assert(PQgetlength(result, 0, 0) == sizeof(out->id));
+		// Use memcpy() in case the result is not aligned; the reason we are
+		// copying over the id is because the results may arrive in any order.
+		memcpy(&out->id, id, sizeof(out->id));
+		out->id = ntohl(out->id);
 	}
+	else
+		out->id = atoi(id);
 
-	return PQflush(db_conn);
+	if (PQfformat(result, 1)) {
+		assert(PQgetlength(result, 0, 1) == sizeof(out->random_number));
+		// Use memcpy() in case the result is not aligned.
+		memcpy(&out->random_number, random_number, sizeof(out->random_number));
+		out->random_number = ntohl(out->random_number);
+	}
+	else
+		out->random_number = atoi(random_number);
 }
 
 static int serialize_item(uint32_t id, uint32_t random_number, yajl_gen gen)
@@ -427,18 +474,84 @@ static void serialize_items(const query_result_t *res,
 
 	CHECK_YAJL_STATUS(yajl_gen_array_close, gen);
 
-	if (send_json_response(gen, NULL, req)) {
+	if (!send_json_response(gen, false, req))
+		return;
+
 error_yajl:
-		yajl_gen_free(gen);
-		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)
 {
 	IGNORE_FUNCTION_PARAMETER(self);
 
-	return do_multiple_queries(NO_UPDATE, req);
+	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
+	                                                      event_loop.h2o_ctx,
+	                                                      req->conn->ctx);
+
+	const size_t num_query = get_query_number(req);
+	size_t base_size = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
+
+	base_size = ((base_size + _Alignof(query_param_t) - 1) / _Alignof(query_param_t));
+	base_size = base_size * _Alignof(query_param_t);
+
+	const size_t num_query_in_progress = MIN(num_query, ctx->config->max_db_conn_num);
+	const size_t sz = base_size + num_query_in_progress * sizeof(query_param_t);
+
+	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc_shared(&req->pool,
+	                                                              sz,
+	                                                              cleanup_multiple_query);
+
+	if (query_ctx) {
+		memset(query_ctx, 0, sz);
+		query_ctx->num_query = num_query;
+		query_ctx->num_query_in_progress = num_query_in_progress;
+		query_ctx->req = req;
+		query_ctx->query_param = (query_param_t *) ((char *) query_ctx + base_size);
+		initialize_ids(num_query, query_ctx->res, &ctx->random_seed);
+
+		for (size_t i = 0; i < query_ctx->num_query_in_progress; i++) {
+			query_ctx->query_param[i].ctx = query_ctx;
+			// We need a copy of id because the original may be overwritten
+			// by a completed query.
+			query_ctx->query_param[i].id = htonl(query_ctx->res[i].id);
+			query_ctx->query_param[i].id_format = 1;
+			query_ctx->query_param[i].id_len = sizeof(query_ctx->query_param[i].id);
+			query_ctx->query_param[i].id_pointer =
+				(const char *) &query_ctx->query_param[i].id;
+			query_ctx->query_param[i].param.command = WORLD_TABLE_NAME;
+			query_ctx->query_param[i].param.nParams = 1;
+			query_ctx->query_param[i].param.on_error = on_multiple_query_error;
+			query_ctx->query_param[i].param.on_result = on_multiple_query_result;
+			query_ctx->query_param[i].param.on_timeout = on_multiple_query_timeout;
+			query_ctx->query_param[i].param.paramFormats =
+				&query_ctx->query_param[i].id_format;
+			query_ctx->query_param[i].param.paramLengths =
+				&query_ctx->query_param[i].id_len;
+			query_ctx->query_param[i].param.paramValues =
+				&query_ctx->query_param[i].id_pointer;
+			query_ctx->query_param[i].param.flags = IS_PREPARED;
+			query_ctx->query_param[i].param.resultFormat = 1;
+
+			if (execute_query(ctx, &query_ctx->query_param[i].param)) {
+				query_ctx->num_query_in_progress = i;
+				send_service_unavailable_error(DB_REQ_ERROR, req);
+				return 0;
+			}
+
+			h2o_mem_addref_shared(query_ctx);
+		}
+
+		// Create a JSON generator while the queries are processed.
+		query_ctx->gen = get_json_generator(&req->pool);
+
+		if (!query_ctx->gen)
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+
+	return 0;
 }
 
 int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
@@ -450,18 +563,29 @@ int single_query(struct st_h2o_handler_t *self, h2o_req_t *req)
 	                                                      req->conn->ctx);
 	single_query_ctx_t * const query_ctx = h2o_mem_alloc_pool(&req->pool, sizeof(*query_ctx));
 
-	if (!query_ctx || initialize_single_query_context(req,
-	                                                  on_single_query_result,
-	                                                  query_ctx))
-		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
-	else {
+	if (query_ctx) {
+		memset(query_ctx, 0, sizeof(*query_ctx));
 		query_ctx->id = htonl(get_random_number(MAX_ID, &ctx->random_seed) + 1);
+		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_single_query_error;
+		query_ctx->param.on_result = on_single_query_result;
+		query_ctx->param.on_timeout = on_single_query_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;
 
-		if (execute_query(ctx, &query_ctx->param)) {
-			yajl_gen_free(query_ctx->gen);
+		if (execute_query(ctx, &query_ctx->param))
 			send_service_unavailable_error(DB_REQ_ERROR, req);
-		}
 	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
 
 	return 0;
 }
@@ -470,5 +594,67 @@ int updates(struct st_h2o_handler_t *self, h2o_req_t *req)
 {
 	IGNORE_FUNCTION_PARAMETER(self);
 
-	return do_multiple_queries(CREATE, req);
+	thread_context_t * const ctx = H2O_STRUCT_FROM_MEMBER(thread_context_t,
+	                                                      event_loop.h2o_ctx,
+	                                                      req->conn->ctx);
+
+	const size_t num_query = get_query_number(req);
+	size_t base_size = offsetof(update_ctx_t, res) + num_query * sizeof(query_result_t);
+
+	base_size = ((base_size + _Alignof(update_param_t) - 1) / _Alignof(update_param_t));
+	base_size = base_size * _Alignof(update_param_t);
+
+	const size_t num_query_in_progress = MIN(num_query, ctx->config->max_db_conn_num);
+	const size_t struct_size = base_size + num_query_in_progress * sizeof(update_param_t);
+	const size_t sz = struct_size + num_query_in_progress * MAX_UPDATE_QUERY_LEN;
+	update_ctx_t * const update_ctx = h2o_mem_alloc_shared(&req->pool, sz, cleanup_update);
+
+	if (update_ctx) {
+		memset(update_ctx, 0, struct_size);
+		update_ctx->num_query = num_query;
+		update_ctx->num_query_in_progress = num_query_in_progress;
+		update_ctx->req = req;
+		update_ctx->update_param = (update_param_t *) ((char *) update_ctx + base_size);
+		initialize_ids(num_query, update_ctx->res, &ctx->random_seed);
+
+		char *command = (char *) update_ctx + struct_size;
+
+		for (size_t i = 0; i < update_ctx->num_query_in_progress; i++) {
+			update_ctx->update_param[i].command = command;
+			command += MAX_UPDATE_QUERY_LEN;
+			update_ctx->update_param[i].ctx = update_ctx;
+			update_ctx->update_param[i].param.command =
+				update_ctx->update_param[i].command;
+			update_ctx->update_param[i].param.on_error = on_update_error;
+			update_ctx->update_param[i].param.on_result = on_update_result;
+			update_ctx->update_param[i].param.on_timeout = on_update_timeout;
+			update_ctx->update_param[i].param.resultFormat = 1;
+			update_ctx->update_param[i].random_number =
+				get_random_number(MAX_ID, &ctx->random_seed) + 1;
+			snprintf(update_ctx->update_param[i].command,
+			         MAX_UPDATE_QUERY_LEN,
+			         UPDATE_QUERY,
+			         update_ctx->res[i].id,
+			         update_ctx->res[i].id,
+			         update_ctx->update_param[i].random_number);
+
+			if (execute_query(ctx, &update_ctx->update_param[i].param)) {
+				update_ctx->num_query_in_progress = i;
+				send_service_unavailable_error(DB_REQ_ERROR, req);
+				return 0;
+			}
+
+			h2o_mem_addref_shared(update_ctx);
+		}
+
+		// Create a JSON generator while the queries are processed.
+		update_ctx->gen = get_json_generator(&req->pool);
+
+		if (!update_ctx->gen)
+			send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+	}
+	else
+		send_error(INTERNAL_SERVER_ERROR, MEM_ALLOC_ERR_MSG, req);
+
+	return 0;
 }

+ 1 - 1
frameworks/C/lwan/setup-mysql.sh

@@ -6,7 +6,7 @@ export MYSQL_PASS=benchmarkdbpass
 export MYSQL_HOST=$DBHOST
 export MYSQL_DB=hello_world
 
-fw_depends lwan
+fw_depends mysql lwan
 
 cd $LWAN_ROOT/techempower
 $LWAN_BUILD/techempower/techempower &

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

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends onion
+fw_depends mysql onion
 
 sed -i 's|127.0.0.1|'${DBHOST}'|g' hello.c
 

+ 5 - 5
frameworks/CSharp/aspnet/benchmark_config.json

@@ -272,7 +272,7 @@
       "versus": "aspnet-mono"
     },
     "mono-mysql-raw": {
-      "setup_file": "setup_nginx",
+      "setup_file": "setup_mysql",
       "json_url": "/json/default",
       "plaintext_url": "/plaintext",
       "db_url": "/ado/mysql",
@@ -296,7 +296,7 @@
       "versus": "aspnet"
     },
     "mono-postgresql-raw": {
-      "setup_file": "setup_nginx",
+      "setup_file": "setup_postgresql",
       "db_url": "/ado/postgresql",
       "query_url": "/ado/postgresql?queries=",
       "fortune_url": "/ado/postgresql/fortunes",
@@ -318,7 +318,7 @@
       "versus": "aspnet"
     },
     "mono-mongodb-raw": {
-      "setup_file": "setup_nginx",
+      "setup_file": "setup_mongodb",
       "db_url": "/mongodb",
       "query_url": "/mongodb?queries=",
       "fortune_url": "/mongodb/fortunes",
@@ -340,7 +340,7 @@
       "versus": "aspnet"
     },
     "mono-mysql-entityframework": {
-      "setup_file": "setup_nginx",
+      "setup_file": "setup_mysql",
       "db_url": "/entityframework/mysql",
       "query_url": "/entityframework/mysql?queries=",
       "fortune_url": "/entityframework/mysql/fortunes",
@@ -362,7 +362,7 @@
       "versus": "aspnet"
     },
     "mono-postgresql-entityframework": {
-      "setup_file": "setup_nginx",
+      "setup_file": "setup_postgresql",
       "db_url": "/entityframework/postgresql",
       "query_url": "/entityframework/postgresql?queries=",
       "fortune_url": "/entityframework/postgresql/fortunes",

+ 5 - 0
frameworks/CSharp/aspnet/setup_mongodb.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+fw_depends mongodb
+
+source ./setup_nginx.sh

+ 5 - 0
frameworks/CSharp/aspnet/setup_mysql.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+fw_depends mysql
+
+source ./setup_nginx.sh

+ 5 - 0
frameworks/CSharp/aspnet/setup_postgresql.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+fw_depends postgresql
+
+source ./setup_nginx.sh

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

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

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

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

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

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

+ 1 - 1
frameworks/CSharp/nancy/setup_nginx.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends nginx mono
+fw_depends mysql nginx mono
 
 sed -i 's|localhost|'"${DBHOST}"'|g' src/Web.config
 sed -i 's|include /usr/local/nginx/conf/fastcgi_params;|include '"${NGINX_HOME}"'/conf/fastcgi_params;|g' nginx.conf

+ 2 - 2
frameworks/CSharp/revenj/README.md

@@ -26,6 +26,6 @@ Data structures are defined in a DSL schema
 The tests were run with:
 
  * [Mono 4.2](http://www.mono-project.com/)
- * [.NET 4.0](https://www.microsoft.com/net)
+ * [.NET 4.5](https://www.microsoft.com/net)
  * [Postgres 9.3](http://www.postgresql.org/)
- * [Revenj.NET 1.3.1](http://github.com/ngs-doo/revenj)
+ * [Revenj.NET 1.4.1](http://github.com/ngs-doo/revenj)

+ 13 - 11
frameworks/CSharp/revenj/Revenj.Bench.sln

@@ -1,18 +1,11 @@
 
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revenj.Bench", "Revenj.Bench\Revenj.Bench.csproj", "{14DC9873-30EA-41DA-8D7B-5AA03E7E2EE2}"
 EndProject
 Global
-	GlobalSection(DslPlatformSolutionProperties) = preSolution
-		Php.Name = Php
-		Php.Target = Php
-		Postgres.Compile = True
-		Postgres.Name = ServerModel
-		Postgres.Target = exe
-		Postgres.Dependencies = exe
-		Postgres.WithManualJson = True
-	EndGlobalSection
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Release|Any CPU = Release|Any CPU
@@ -26,4 +19,13 @@ Global
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(DslPlatformSolutionProperties) = preSolution
+		Php.Name = Php
+		Php.Target = Php
+		Postgres.Compile = True
+		Postgres.Name = ServerModel
+		Postgres.Target = exe
+		Postgres.Dependencies = exe
+		Postgres.WithManualJson = True
+	EndGlobalSection
 EndGlobal

+ 33 - 16
frameworks/CSharp/revenj/Revenj.Bench/Context.cs

@@ -1,34 +1,51 @@
 using System;
-using System.IO;
 using FrameworkBench;
 using Revenj.DatabasePersistence;
-using Revenj.DatabasePersistence.Postgres;
 using Revenj.DomainPatterns;
 using Revenj.Extensibility;
-using Revenj.Utility;
 
 namespace Revenj.Bench
 {
 	internal class Context
 	{
-		public readonly ChunkedMemoryStream Stream;
-		public readonly TextWriter Writer;
 		public readonly IPersistableRepository<World> WorldRepository;
 		public readonly IQueryableRepository<Fortune> FortuneRepository;
-		public readonly IRepositoryBulkReader BulkReader;
-		public readonly Lazy<World>[] LazyWorlds = new Lazy<World>[512];
+		public readonly Random Random = new Random(0);
 		public readonly World[] Worlds = new World[512];
+		//private readonly IRepositoryBulkReader BulkReader;
+		//private readonly Lazy<World>[] LazyWorlds = new Lazy<World>[512];
 
-		public Context(IServiceProvider service)
+		public Context(IObjectFactory factory, IDatabaseQueryManager manager)
 		{
-			Stream = ChunkedMemoryStream.Static();
-			Writer = Stream.GetWriter();
-			var dqm = service.Resolve<IDatabaseQueryManager>();
-			var factory = service.Resolve<IObjectFactory>().CreateInnerFactory();
-			factory.RegisterInterfaces(dqm.StartQuery(false));
-			WorldRepository = factory.Resolve<IPersistableRepository<World>>();
-			FortuneRepository = factory.Resolve<IQueryableRepository<Fortune>>();
-			BulkReader = factory.BulkRead(ChunkedMemoryStream.Static());
+			var scope = factory.CreateScope(null);
+			scope.RegisterInterfaces(manager.StartQuery(false));
+			WorldRepository = scope.Resolve<IPersistableRepository<World>>();
+			FortuneRepository = scope.Resolve<IQueryableRepository<Fortune>>();
+			//BulkReader = scope.BulkRead(ChunkedMemoryStream.Static());
+		}
+
+		/* bulk loading of worlds. use such pattern for production code */
+		/*public void LoadWorldsFast(int repeat, World[] worlds)
+		{
+			BulkReader.Reset(true);
+			for (int i = 0; i < repeat; i++)
+			{
+				var id = Random.Next(10000) + 1;
+				LazyWorlds[i] = BulkReader.Find<World>(id.ToString());
+			}
+			BulkReader.Execute();
+			for (int i = 0; i < repeat; i++)
+				worlds[i] = LazyWorlds[i].Value;
+		}*/
+
+		/* multiple roundtrips loading of worlds. don't write such production code */
+		public void LoadWorldsSlow(int repeat, World[] worlds)
+		{
+			for (int i = 0; i < repeat; i++)
+			{
+				var id = Random.Next(10000) + 1;
+				worlds[i] = WorldRepository.Find(id);
+			}
 		}
 	}
 }

+ 19 - 6
frameworks/CSharp/revenj/Revenj.Bench/FortuneTemplate.cs

@@ -1,18 +1,31 @@
 using System.Collections.Generic;
 using System.IO;
+using System.Text;
+using System.Web;
+using Revenj.Http;
 
 namespace Revenj.Bench
 {
-	public partial class Fortunes
+	public partial class Fortunes : IHtmlView
 	{
-		private readonly TextWriter writer;
-
-		public Fortunes(List<KeyValuePair<int, string>> arg, TextWriter writer)
+		private readonly static StringBuilder Fake = new StringBuilder(0);
+		public Fortunes(List<KeyValuePair<int, string>> arg)
 		{
 			this._fortunesField = arg;
-			this.writer = writer;
+			this.GenerationEnvironment = Fake;
 		}
 
-		public new void Write(string what) { writer.Write(what); }
+		private new void Write(string what) { writer.Write(what); }
+
+		public void Show(int what) { writer.Write(what); }
+		public void Show(string what) { HttpUtility.HtmlEncode(what, writer); }
+
+		private TextWriter writer;
+
+		public void Render(TextWriter writer)
+		{
+			this.writer = writer;
+			TransformText();
+		}
 	}
 }

+ 9 - 9
frameworks/CSharp/revenj/Revenj.Bench/Fortunes.cs

@@ -12,7 +12,7 @@ namespace Revenj.Bench
     using System;
     
     
-    #line 1 "C:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
+    #line 1 "D:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
     [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "10.0.0.0")]
     public partial class Fortunes : FortunesBase
     {
@@ -23,29 +23,29 @@ namespace Revenj.Bench
             this.Write("\n<!DOCTYPE html>\n<html>\n<head><title>Fortunes</title></head>\n<body><table><tr><th" +
                     ">id</th><th>message</th></tr>\n");
             
-            #line 1 "C:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
- for (var i=0; i<fortunes.Count;i++) {
+            #line 1 "D:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
+ for (var i=0; i<fortunes.Count;i++) {
 	var f = fortunes[i]; 
             
             #line default
             #line hidden
             this.Write("\n<tr><td>");
             
-            #line 1 "C:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
- Write(f.Key.ToString()); 
+            #line 1 "D:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
+ Show(f.Key); 
             
             #line default
             #line hidden
             this.Write("</td><td>");
             
-            #line 1 "C:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
- Write(System.Web.HttpUtility.HtmlEncode(f.Value)); 
+            #line 1 "D:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
+ Show(f.Value); 
             
             #line default
             #line hidden
             this.Write("</td></tr>\n");
             
-            #line 1 "C:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
+            #line 1 "D:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
  } 
             
             #line default
@@ -54,7 +54,7 @@ namespace Revenj.Bench
             return this.GenerationEnvironment.ToString();
         }
         
-        #line 1 "C:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
+        #line 1 "D:\Projects\FrameworkBenchmarks\frameworks\CSharp\revenj\Revenj.Bench\Fortunes.tt"
 
 private global::System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<int, string>> _fortunesField;
 

+ 1 - 1
frameworks/CSharp/revenj/Revenj.Bench/Fortunes.tt

@@ -7,7 +7,7 @@
 <body><table><tr><th>id</th><th>message</th></tr>
 <# for (var i=0; i<fortunes.Count;i++) {
 	var f = fortunes[i]; #>
-<tr><td><# Write(f.Key.ToString()); #></td><td><# Write(System.Web.HttpUtility.HtmlEncode(f.Value)); #></td></tr>
+<tr><td><# Show(f.Key); #></td><td><# Show(f.Value); #></td></tr>
 <# } #>
 </table>
 </body>

+ 36 - 119
frameworks/CSharp/revenj/Revenj.Bench/RestService.cs

@@ -1,187 +1,104 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.ServiceModel;
-using System.ServiceModel.Web;
 using System.Text;
+using System.Threading;
 using FrameworkBench;
 using Revenj.Api;
+using Revenj.DatabasePersistence;
 using Revenj.DomainPatterns;
+using Revenj.Extensibility;
+using Revenj.Http;
 using Revenj.Serialization;
 using Revenj.Utility;
 
 namespace Revenj.Bench
 {
-	[ServiceContract(Namespace = "https://github.com/ngs-doo/revenj")]
-	public interface IRestService
-	{
-		[OperationContract]
-		[WebGet(UriTemplate = "/plaintext")]
-		Stream PlainText();
-
-		[OperationContract]
-		[WebGet(UriTemplate = "/json")]
-		Stream JSON();
-
-		[OperationContract]
-		[WebGet(UriTemplate = "/db")]
-		Stream SingleQuery();
-
-		[OperationContract]
-		[WebGet(UriTemplate = "/queries/{count}")]
-		Stream MultipleQueries(string count);
-
-		[OperationContract]
-		[WebGet(UriTemplate = "/updates/{count}")]
-		Stream Updates(string count);
-
-		[OperationContract]
-		[WebGet(UriTemplate = "/fortunes")]
-		Stream Fortunes();
-	}
-
-	public class RestService : IRestService
+	[Controller("bench")]
+	public class RestService
 	{
 		private static readonly ChunkedMemoryStream HelloWorld = ChunkedMemoryStream.Static();
-		private static readonly string[] IDs = new string[10001];
-		[ThreadStatic]
-		private static Context Context;
-		private static Context GetContext(IServiceProvider services)
-		{
-			if (Context == null)
-				Context = new Context(services);
-			Context.Stream.Reset();
-			return Context;
-		}
 
 		static RestService()
 		{
 			var hwText = Encoding.UTF8.GetBytes("Hello, World!");
 			HelloWorld.Write(hwText, 0, hwText.Length);
 			HelloWorld.Position = 0;
-			for (int i = 0; i < IDs.Length; i++)
-				IDs[i] = i.ToString();
 		}
 
-		private Random Random = new Random(0);
-		private readonly IServiceProvider Services;
+		private readonly ThreadLocal<Context> Context;
 
-		public RestService(IServiceProvider services)
+		public RestService(IObjectFactory factory, IDatabaseQueryManager queryManager)
 		{
-			this.Services = services;
+			this.Context = new ThreadLocal<Context>(() => new Context(factory, queryManager));
 		}
 
-		public Stream PlainText()
+		[Route(HTTP.GET, "/plaintext", false)]
+		public Stream PlainText(IResponseContext response)
 		{
-			ThreadContext.Response.ContentType = "text/plain";
+			response.ContentType = "text/plain";
 			return HelloWorld;
 		}
 
-		private Stream ReturnJSON(IJsonObject value, ChunkedMemoryStream cms)
-		{
-			value.Serialize(cms);
-			ThreadContext.Response.ContentType = "application/json";
-			return cms;
-		}
-
-		public Stream JSON()
-		{
-			var ctx = GetContext(Services);
-			return ReturnJSON(new Message { message = "Hello, World!" }, ctx.Stream);
-		}
-
-		public Stream SingleQuery()
-		{
-			var id = Random.Next(10000) + 1;
-			var ctx = GetContext(Services);
-			var world = ctx.WorldRepository.Find(IDs[id]);
-			return ReturnJSON(world, ctx.Stream);
-		}
-
-		/* bulk loading of worlds. use such pattern for production code */
-		private void LoadWorldsFast(int repeat, Context ctx)
+		[Route(HTTP.GET, "/json", false)]
+		public Message JSON()
 		{
-			var reader = ctx.BulkReader;
-			var lazyResult = ctx.LazyWorlds;
-			var worlds = ctx.Worlds;
-			reader.Reset(true);
-			for (int i = 0; i < repeat; i++)
-			{
-				var id = Random.Next(10000) + 1;
-				lazyResult[i] = reader.Find<World>(IDs[id]);
-			}
-			reader.Execute();
-			for (int i = 0; i < repeat; i++)
-				worlds[i] = lazyResult[i].Value;
+			return new Message { message = "Hello, World!" };
 		}
 
-		/* multiple roundtrips loading of worlds. don't write such production code */
-		private void LoadWorldsSlow(int repeat, Context ctx)
+		[Route(HTTP.GET, "/db")]
+		public World SingleQuery()
 		{
-			var worlds = ctx.Worlds;
-			var repository = ctx.WorldRepository;
-			for (int i = 0; i < repeat; i++)
-			{
-				var id = Random.Next(10000) + 1;
-				worlds[i] = repository.Find(IDs[id]);
-			}
+			var ctx = Context.Value;
+			var id = ctx.Random.Next(10000) + 1;
+			return ctx.WorldRepository.Find(id);
 		}
 
-		public Stream MultipleQueries(string count)
+		//while IList<World> would work, it would fall back to IEnumerable<IJsonObject> which would create garbage
+		[Route(HTTP.GET, "/queries/{count}")]
+		public IList<IJsonObject> MultipleQueries(string count, IResponseContext response)
 		{
 			int repeat;
 			int.TryParse(count, out repeat);
 			if (repeat < 1) repeat = 1;
 			else if (repeat > 500) repeat = 500;
-			var ctx = GetContext(Services);
-			LoadWorldsSlow(repeat, ctx);
-			var cms = ctx.Stream;
-			ctx.Worlds.Serialize(cms, repeat);
-			ThreadContext.Response.ContentType = "application/json";
-			return cms;
+			var ctx = Context.Value;
+			ctx.LoadWorldsSlow(repeat, ctx.Worlds);
+			return new ArraySegment<IJsonObject>(ctx.Worlds, 0, repeat);
 		}
 
 		private static readonly Comparison<World> ASC = (l, r) => l.id - r.id;
 
-		public Stream Updates(string count)
+		[Route(HTTP.GET, "/updates/{count}")]
+		public World[] Updates(string count)
 		{
 			int repeat;
 			int.TryParse(count, out repeat);
 			if (repeat < 1) repeat = 1;
 			else if (repeat > 500) repeat = 500;
-			var ctx = GetContext(Services);
-			LoadWorldsSlow(repeat, ctx);
+			var ctx = Context.Value;
 			var result = new World[repeat];
-			Array.Copy(ctx.Worlds, result, repeat);
+			ctx.LoadWorldsSlow(repeat, result);
 			for (int i = 0; i < result.Length; i++)
-				result[i].randomNumber = Random.Next(10000) + 1;
+				result[i].randomNumber = ctx.Random.Next(10000) + 1;
 			Array.Sort(result, ASC);
 			ctx.WorldRepository.Update(result);
-			var cms = ctx.Stream;
-			result.Serialize(cms);
-			ThreadContext.Response.ContentType = "application/json";
-			return cms;
+			return result;
 		}
 
 		private static readonly Comparison<KeyValuePair<int, string>> Comparison = (l, r) => string.Compare(l.Value, r.Value, StringComparison.Ordinal);
 
-		public Stream Fortunes()
+		[Route(HTTP.GET, "/fortunes")]
+		public Fortunes Fortunes()
 		{
-			var ctx = GetContext(Services);
+			var ctx = Context.Value;
 			var fortunes = ctx.FortuneRepository.Search();
 			var list = new List<KeyValuePair<int, string>>(fortunes.Length + 1);
 			foreach (var f in fortunes)
 				list.Add(new KeyValuePair<int, string>(f.id, f.message));
 			list.Add(new KeyValuePair<int, string>(0, "Additional fortune added at request time."));
 			list.Sort(Comparison);
-			var cms = ctx.Stream;
-			var writer = cms.GetWriter();
-			var template = new Fortunes(list, writer);
-			template.TransformText();
-			writer.Flush();
-			cms.Position = 0;
-			ThreadContext.Response.ContentType = "text/html; charset=UTF-8";
-			return cms;
+			return new Fortunes(list);
 		}
 	}
 }

+ 13 - 30
frameworks/CSharp/revenj/Revenj.Bench/Revenj.Bench.csproj

@@ -10,8 +10,9 @@
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>Revenj.Bench</RootNamespace>
     <AssemblyName>Revenj.Bench</AssemblyName>
-    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -21,6 +22,7 @@
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -29,43 +31,24 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="Revenj.Api.Interface">
+    <Reference Include="Revenj.Api.Interface" >
       <HintPath>..\exe\Revenj.Api.Interface.dll</HintPath>
-      <Private>False</Private>
     </Reference>
-    <Reference Include="Revenj.DatabasePersistence.Interface">
-      <HintPath>..\exe\Revenj.DatabasePersistence.Interface.dll</HintPath>
-      <Private>False</Private>
+    <Reference Include="Revenj.Core" >
+      <HintPath>..\exe\Revenj.Core.dll</HintPath>
     </Reference>
-    <Reference Include="Revenj.DatabasePersistence.Postgres">
-      <HintPath>..\exe\Revenj.DatabasePersistence.Postgres.dll</HintPath>
-      <Private>False</Private>
+    <Reference Include="Revenj.Core.Interface" >
+      <HintPath>..\exe\Revenj.Core.Interface.dll</HintPath>
     </Reference>
-    <Reference Include="Revenj.DomainPatterns.Interface">
-      <HintPath>..\exe\Revenj.DomainPatterns.Interface.dll</HintPath>
-      <Private>False</Private>
+    <Reference Include="Revenj.Http">
+      <HintPath>..\exe\Revenj.Http.exe</HintPath>
+      <ExecutableExtension>.exe</ExecutableExtension>
     </Reference>
-    <Reference Include="Revenj.Extensibility.Interface">
-      <HintPath>..\exe\Revenj.Extensibility.Interface.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Revenj.Processing">
+    <Reference Include="Revenj.Processing" >
       <HintPath>..\exe\Revenj.Processing.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Revenj.Serialization">
-      <HintPath>..\exe\Revenj.Serialization.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Revenj.Serialization.Interface">
-      <HintPath>..\exe\Revenj.Serialization.Interface.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="Revenj.Utility">
-      <HintPath>..\exe\Revenj.Utility.dll</HintPath>
-      <Private>False</Private>
     </Reference>
     <Reference Include="ServerModel">
       <HintPath>..\exe\ServerModel.dll</HintPath>

+ 3 - 10
frameworks/CSharp/revenj/Revenj.Http.exe.config

@@ -1,30 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <configuration>
   <configSections>
-    <section name="autofacConfiguration" type="Revenj.Extensibility.Autofac.Configuration.SectionHandler, Revenj.Extensibility" />
+    <section name="autofacConfiguration" type="Revenj.Extensibility.Autofac.Configuration.SectionHandler, Revenj.Core" />
   </configSections>
   <appSettings>
     <add key="PluginsPath" value="." />
     <add key="ServerAssembly" value="ServerModel.dll"/>
     <add key="ConnectionString" value="server=localhost;port=5432;database=hello_world;user=benchmarkdbuser;password=benchmarkdbpass" />
     <add key="HttpAddress_local" value="http://0.0.0.0:8080/" />
-    <add key="Revenj.HttpServer" value="Socket"/>
+    <add key="Revenj.HttpServer" value="Revenj"/>
     <add key="CustomAuth" value="Revenj.Http.NoAuth"/>
     <add key="Revenj.Notifications" value="disabled"/>
   </appSettings>
-  <system.serviceModel>
-    <serviceHostingEnvironment>
-      <serviceActivations>
-        <add relativeAddress="bench" service="Revenj.Bench.RestService, Revenj.Bench" />
-      </serviceActivations>
-    </serviceHostingEnvironment>
-  </system.serviceModel>
   <autofacConfiguration>
     <modules>
       <module type="Revenj.Wcf.StandardModule, Revenj.Wcf" />
     </modules>
     <components>
-      <component type="Revenj.Http.NoAuth, Revenj.Http" service="Revenj.Security.IPermissionManager, Revenj.Security.Interface" />
+      <component type="Revenj.Http.NoAuth, Revenj.Http" service="Revenj.Security.IPermissionManager, Revenj.Core.Interface" />
     </components>
   </autofacConfiguration>
 </configuration>

+ 1 - 1
frameworks/CSharp/revenj/benchmark_config.json

@@ -1,5 +1,5 @@
 {
-  "framework": "Revenj",
+  "framework": "revenj",
   "tests": [{
     "windows": {
       "setup_file": "setup",

+ 2 - 2
frameworks/CSharp/revenj/setup.ps1

@@ -39,11 +39,11 @@ if ($action -eq 'start') {
 
 	echo "Download DSL compiler client"
 	$client = new-object System.Net.WebClient
-	$client.DownloadFile( "https://github.com/ngs-doo/dsl-compiler-client/releases/download/1.7.0/dsl-clc.jar", $dslclc )
+	$client.DownloadFile( "https://github.com/ngs-doo/dsl-compiler-client/releases/download/1.8.2/dsl-clc.jar", $dslclc )
 
 	echo "Download Revenj HTTP server"
 	$client = new-object System.Net.WebClient
-	$client.DownloadFile( "https://github.com/ngs-doo/revenj/releases/download/1.3.1/http-server.zip", $httpZip )
+	$client.DownloadFile( "https://github.com/ngs-doo/revenj/releases/download/1.4.1/http-server.zip", $httpZip )
 
 	echo "Unzipping HTTP server"
 	[System.IO.Compression.ZipFile]::ExtractToDirectory($httpZip, $exe)

+ 4 - 5
frameworks/CSharp/revenj/setup.sh

@@ -1,15 +1,15 @@
 #!/bin/bash
 
-fw_depends java mono dsl_platform
+fw_depends postgresql java mono dsl_platform
 
 echo "Cleaning up..."
 rm -rf $TROOT/exe $TROOT/tmp $TROOT/dsl-clc.jar $TROOT/http-server.zip
 
 echo "Download DSL compiler client"
-wget -O $TROOT/dsl-clc.jar https://github.com/ngs-doo/dsl-compiler-client/releases/download/1.7.0/dsl-clc.jar
+wget -O $TROOT/dsl-clc.jar https://github.com/ngs-doo/dsl-compiler-client/releases/download/1.8.2/dsl-clc.jar
 
-echo "Download Revenj.NET HTTP server 1.3.1"
-wget -O $TROOT/http-server.zip https://github.com/ngs-doo/revenj/releases/download/1.3.1/http-server.zip
+echo "Download Revenj.NET HTTP server 1.4.1"
+wget -O $TROOT/http-server.zip https://github.com/ngs-doo/revenj/releases/download/1.4.1/http-server.zip
 
 echo "Unzipping HTTP server"
 unzip $TROOT/http-server.zip -d $TROOT/exe
@@ -33,4 +33,3 @@ cat $TROOT/Revenj.Http.exe.config | sed 's|\(ConnectionString.*server=\)localhos
 
 echo "Running the Revenj instance"
 mono $TROOT/exe/Revenj.Http.exe
-sleep 5

+ 1 - 1
frameworks/CSharp/servicestack/setup_nginx.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends nginx mono
+fw_depends mysql postgresql mongodb nginx mono
 
 sed -i 's|localhost|'"$DBHOST"'|g' src/Web.config
 sed -i 's|/usr/local/nginx/|'"${IROOT}"'/nginx/|g' nginx.conf

+ 1 - 1
frameworks/CSharp/servicestack/setup_xsp.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends nginx mono
+fw_depends mysql postgresql mongodb nginx mono
 
 sed -i 's|localhost|'"$DBHOST"'|g' src/Web.config
 # extra cleaning

+ 1 - 1
frameworks/Clojure/compojure/setup.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-fw_depends java resin leiningen
+fw_depends mysql java resin leiningen
 
 sed -i 's|127.0.0.1|'"${DBHOST}"'|g' hello/src/hello/handler.clj
 

部分文件因文件數量過多而無法顯示