Эх сурвалжийг харах

Add Roda+Sequel benchmarks (#2516)

Mike Pastore 8 жил өмнө
parent
commit
71c394b57f

+ 1 - 0
.travis.yml

@@ -169,6 +169,7 @@ env:
     - "TESTDIR=Ruby/rack-sequel"
     - "TESTDIR=Ruby/rails"
     - "TESTDIR=Ruby/rails-stripped"
+    - "TESTDIR=Ruby/roda-sequel"
     - "TESTDIR=Ruby/sinatra"
     - "TESTDIR=Ruby/sinatra-sequel"
     - "TESTDIR=Rust/iron"

+ 4 - 0
frameworks/Ruby/roda-sequel/.gitignore

@@ -0,0 +1,4 @@
+.bundle
+vendor/bundle
+passenger.*
+Gemfile.lock

+ 25 - 0
frameworks/Ruby/roda-sequel/Gemfile

@@ -0,0 +1,25 @@
+source 'https://rubygems.org'
+
+gem 'erubi', '~> 1.4'
+gem 'json', '~> 2.0'
+gem 'passenger', '~> 5.1', :platforms=>[:ruby, :mswin], :require=>false
+gem 'puma', '~> 3.6', :require=>false
+gem 'sequel', '~> 4.40',
+  :git=>'https://github.com/jeremyevans/sequel.git', :branch=>'master'
+gem 'roda', '~> 2.22',
+  :git=>'https://github.com/jeremyevans/roda.git', :branch=>'master'
+gem 'sysrandom', '~> 1.0', :require=>'sysrandom/securerandom'
+gem 'tilt', '~> 2.0', :require=>'tilt/erb'
+gem 'torquebox-web', '>= 4.0.0.beta3', '< 5', :platforms=>:jruby, :require=>false
+gem 'unicorn', '~> 5.2', :platforms=>[:ruby, :mswin], :require=>false
+
+group :mysql do
+  gem 'jdbc-mysql', '~> 5.1', :platforms=>:jruby, :require=>'jdbc/mysql'
+  gem 'mysql2', '~> 0.4', :platforms=>[:ruby, :mswin]
+end
+
+group :postgresql do
+  gem 'jdbc-postgres', '~> 9.4', :platforms=>:jruby, :require=>'jdbc/postgres'
+  gem 'pg', '~> 0.19', :platforms=>[:ruby, :mswin]
+  gem 'sequel_pg', '~> 1.6', :platforms=>:ruby, :require=>false
+end

+ 2 - 0
frameworks/Ruby/roda-sequel/Makefile

@@ -0,0 +1,2 @@
+json: benchmark_config.yaml
+	ruby -r json -r yaml -e 'puts JSON.pretty_generate(YAML.load(ARGF.read))' <benchmark_config.yaml >benchmark_config.json

+ 52 - 0
frameworks/Ruby/roda-sequel/README.md

@@ -0,0 +1,52 @@
+# Ruby [Roda](http://roda.jeremyevans.net)-[Sequel](http://sequel.jeremyevans.net) Benchmarking Test
+
+The information below contains information specific to the Roda benchmarking
+test. For further guidance, review the
+[documentation](http://frameworkbenchmarks.readthedocs.org/en/latest/). Also
+note the additional information provided in the [Ruby README](../).
+
+This is the Ruby Roda portion of a [benchmarking test suite](../../)
+comparing a variety of web platforms.
+
+## Infrastructure Software Versions
+
+The tests will be run with:
+
+* [Ruby 2.4](http://www.ruby-lang.org)
+* [JRuby 9.1](http://jruby.org)
+* [Rubinius 3](https://rubinius.com)\*
+* [Puma 3.6](http://puma.io)
+* [Passenger 5.1](https://www.phusionpassenger.com)
+* [Unicorn 5.2](https://bogomips.org/unicorn/)
+* [TorqueBox 4.0](http://torquebox.org)
+* [Roda 2.22](http://roda.jeremyevans.net)
+* [Sequel 4.43](http://sequel.jeremyevans.net)
+* [Erubi 1.2](https://github.com/jeremyevans/erubi)
+* [MySQL 5.5](https://www.mysql.com)
+* [Postgres 9.3](https://www.postgresql.org)
+
+\* - Tests are developed but currently disabled due to compatibility issues.
+
+## Paths & Source for Tests
+
+* [JSON Serialization](hello_world.rb): "/json"
+* [Single Database Query](hello_world.rb): "/db"
+* [Multiple Database Queries](hello_world.rb): "/db?queries={#}"
+* [Fortunes](hello_world.rb): "/fortune"
+* [Database Updates](hello_world.rb): "/update?queries={#}"
+* [Plaintext](hello_world.rb): "/plaintext"
+
+## Get Help
+
+### Experts
+
+_No experts listed, yet. If you're an expert, add yourself!_
+
+### Community
+
+* [Roda Google Group](http://groups.google.com/group/ruby-roda)
+* `#roda` IRC Channel ([irc.freenode.net](irc://irc.freenode.net/roda))
+
+### Resources
+
+* [Roda Source Code](https://github.com/jeremyevans/roda)

+ 237 - 0
frameworks/Ruby/roda-sequel/benchmark_config.json

@@ -0,0 +1,237 @@
+{
+  "framework": "roda-sequel",
+  "tests": [
+    {
+      "default": {
+        "setup_file": "run_mri_puma",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "Rack",
+        "webserver": "Puma",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-puma-mri",
+        "versus": "rack-sequel-puma-mri",
+        "notes": ""
+      },
+      "postgres": {
+        "setup_file": "run_mri_puma",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "Rack",
+        "webserver": "Puma",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-postgres-puma-mri",
+        "versus": "rack-sequel-postgres-puma-mri",
+        "notes": ""
+      },
+      "puma-jruby": {
+        "setup_file": "run_jruby_puma",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "JRuby",
+        "webserver": "Puma",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-puma-jruby",
+        "versus": "rack-sequel-puma-jruby",
+        "notes": ""
+      },
+      "postgres-puma-jruby": {
+        "setup_file": "run_jruby_puma",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "JRuby",
+        "webserver": "Puma",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-postgres-puma-jruby",
+        "versus": "rack-sequel-postgres-puma-jruby",
+        "notes": ""
+      },
+      "passenger-mri": {
+        "setup_file": "run_mri_passenger",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "Rack",
+        "webserver": "Passenger Standalone",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-passenger-mri",
+        "versus": "rack-sequel-passenger-mri",
+        "notes": ""
+      },
+      "postgres-passenger-mri": {
+        "setup_file": "run_mri_passenger",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "Rack",
+        "webserver": "Passenger Standalone",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-postgres-passenger-mri",
+        "versus": "rack-sequel-postgres-passenger-mri",
+        "notes": ""
+      },
+      "unicorn-mri": {
+        "setup_file": "run_mri_unicorn",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "Rack",
+        "webserver": "Unicorn",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-unicorn-mri",
+        "versus": "rack-sequel-unicorn-mri",
+        "notes": ""
+      },
+      "postgres-unicorn-mri": {
+        "setup_file": "run_mri_unicorn",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "Rack",
+        "webserver": "Unicorn",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-postgres-unicorn-mri",
+        "versus": "rack-sequel-postgres-unicorn-mri",
+        "notes": ""
+      },
+      "torquebox-jruby": {
+        "setup_file": "run_jruby_torquebox",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "JRuby",
+        "webserver": "Puma",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-torquebox-jruby",
+        "versus": "rack-sequel-torquebox-jruby",
+        "notes": ""
+      },
+      "postgres-torquebox-jruby": {
+        "setup_file": "run_jruby_torquebox",
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "roda-sequel",
+        "language": "Ruby",
+        "orm": "Full",
+        "platform": "JRuby",
+        "webserver": "Puma",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "roda-sequel-postgres-torquebox-jruby",
+        "versus": "rack-sequel-postgres-torquebox-jruby",
+        "notes": ""
+      }
+    }
+  ]
+}

+ 82 - 0
frameworks/Ruby/roda-sequel/benchmark_config.yaml

@@ -0,0 +1,82 @@
+---
+framework: roda-sequel
+tests:
+  - default: &default
+      setup_file: run_mri_puma
+      json_url: /json
+      db_url: /db
+      query_url: /queries?queries=
+      fortune_url: /fortunes
+      update_url: /updates?queries=
+      plaintext_url: /plaintext
+      port: 8080
+      approach: Realistic
+      classification: Micro
+      database: MySQL
+      framework: roda-sequel
+      language: Ruby
+      orm: Full
+      platform: Rack
+      webserver: Puma
+      os: Linux
+      database_os: Linux
+      display_name: roda-sequel-puma-mri
+      versus: rack-sequel-puma-mri
+      notes: ""
+    postgres:
+      <<: *default
+      database: Postgres
+      display_name: roda-sequel-postgres-puma-mri
+      versus: rack-sequel-postgres-puma-mri
+    puma-jruby:
+      <<: *default
+      setup_file: run_jruby_puma
+      platform: JRuby
+      display_name: roda-sequel-puma-jruby
+      versus: rack-sequel-puma-jruby
+    postgres-puma-jruby:
+      <<: *default
+      setup_file: run_jruby_puma
+      database: Postgres
+      platform: JRuby
+      display_name: roda-sequel-postgres-puma-jruby
+      versus: rack-sequel-postgres-puma-jruby
+    passenger-mri:
+      <<: *default
+      setup_file: run_mri_passenger
+      webserver: Passenger Standalone
+      display_name: roda-sequel-passenger-mri
+      versus: rack-sequel-passenger-mri
+    postgres-passenger-mri:
+      <<: *default
+      setup_file: run_mri_passenger
+      database: Postgres
+      webserver: Passenger Standalone
+      display_name: roda-sequel-postgres-passenger-mri
+      versus: rack-sequel-postgres-passenger-mri
+    unicorn-mri:
+      <<: *default
+      setup_file: run_mri_unicorn
+      webserver: Unicorn
+      display_name: roda-sequel-unicorn-mri
+      versus: rack-sequel-unicorn-mri
+    postgres-unicorn-mri:
+      <<: *default
+      setup_file: run_mri_unicorn
+      database: Postgres
+      webserver: Unicorn
+      display_name: roda-sequel-postgres-unicorn-mri
+      versus: rack-sequel-postgres-unicorn-mri
+    torquebox-jruby:
+      <<: *default
+      setup_file: run_jruby_torquebox
+      platform: JRuby
+      display_name: roda-sequel-torquebox-jruby
+      versus: rack-sequel-torquebox-jruby
+    postgres-torquebox-jruby:
+      <<: *default
+      setup_file: run_jruby_torquebox
+      database: Postgres
+      platform: JRuby
+      display_name: roda-sequel-postgres-torquebox-jruby
+      versus: rack-sequel-postgres-torquebox-jruby

+ 2 - 0
frameworks/Ruby/roda-sequel/config.ru

@@ -0,0 +1,2 @@
+require_relative 'hello_world'
+run HelloWorld.freeze.app

+ 41 - 0
frameworks/Ruby/roda-sequel/config/auto_tune.rb

@@ -0,0 +1,41 @@
+#!/usr/bin/env ruby
+# Instantiate about one process per X MiB of available memory, scaling up to as
+# close to MAX_THREADS as possible while observing an upper bound based on the
+# number of virtual/logical CPUs. If there are fewer processes than
+# MAX_THREADS, add threads per process to reach MAX_THREADS.
+require 'etc'
+
+KB_PER_WORKER = 64 * 1_024 # average of peak PSS of single-threaded processes (watch smem -k)
+MIN_WORKERS = 2
+MAX_WORKERS_PER_VCPU = 1.25 # virtual/logical
+MIN_THREADS_PER_WORKER = 4
+MAX_THREADS = Integer(ENV['MAX_CONCURRENCY'] || 256)
+
+def meminfo(arg)
+  File.open('/proc/meminfo') do |f|
+    f.each_line do |line|
+      key, value = line.split(/:\s+/)
+      return value.split(/\s+/).first.to_i if key == arg
+    end
+  end
+
+  fail "Unable to find `#{arg}' in /proc/meminfo!"
+end
+
+def auto_tune
+  avail_mem = meminfo('MemAvailable') * 0.8 - MAX_THREADS * 1_024
+
+  workers = [
+    [(1.0 * avail_mem / KB_PER_WORKER).floor, MIN_WORKERS].max,
+    (Etc.nprocessors * MAX_WORKERS_PER_VCPU).ceil
+  ].min
+
+  threads_per_worker = [
+    workers < MAX_THREADS ? (1.0 * MAX_THREADS / workers).ceil : -Float::INFINITY,
+    MIN_THREADS_PER_WORKER
+  ].max
+
+  [workers, threads_per_worker]
+end
+
+p auto_tune if $0 == __FILE__

+ 6 - 0
frameworks/Ruby/roda-sequel/config/bundle_install.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Ensure we don't accidentally (try to) use gems for the wrong platform.
+rm -f $TROOT/Gemfile.lock
+
+bundle install --jobs=4 --gemfile=$TROOT/Gemfile --path=vendor/bundle

+ 13 - 0
frameworks/Ruby/roda-sequel/config/common_run.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+
+if [[ $LOGDIR == *postgres* ]] ; then
+  DBTYPE=postgresql
+else
+  DBTYPE=mysql
+fi
+
+export DBTYPE
+
+if [[ $LOGDIR == *jruby* ]] ; then
+  . $(dirname $0)/config/java_tune.sh
+fi

+ 24 - 0
frameworks/Ruby/roda-sequel/config/java_tune.sh

@@ -0,0 +1,24 @@
+#!/bin/sh
+stack_size=1
+cache_size=240
+meta_size=192
+
+# Factor in Ruby's per-thread overhead...
+avail_mem=$(awk '/^MemAvailable/ { print int(0.7 * $2 / 1024) - ( \
+  $ENVIRON["MAX_CONCURRENCY"] * $ENVIRON["THREAD_FACTOR"] \
+); exit }' /proc/meminfo)
+
+# ...as well as Java's per-thread stack size.
+heap_size=$(( avail_mem - meta_size - cache_size - (
+  stack_size * MAX_CONCURRENCY * THREAD_FACTOR
+) ))
+
+JRUBY_OPTS="-J-server -J-XX:+AggressiveOpts -J-Djava.net.preferIPv4Stack=true"
+JRUBY_OPTS="$JRUBY_OPTS -J-XX:+UseSerialGC"
+#JRUBY_OPTS="$JRUBY_OPTS -J-XX:+UseG1GC -J-XX:MaxGCPauseMillis=100"
+JRUBY_OPTS="$JRUBY_OPTS -J-Xms${heap_size}m -J-Xmx${heap_size}m"
+JRUBY_OPTS="$JRUBY_OPTS -J-Xss${stack_size}m"
+JRUBY_OPTS="$JRUBY_OPTS -J-XX:MaxMetaspaceSize=${meta_size}m"
+JRUBY_OPTS="$JRUBY_OPTS -J-XX:ReservedCodeCacheSize=${cache_size}m"
+
+export JRUBY_OPTS

+ 7 - 0
frameworks/Ruby/roda-sequel/config/mri_puma.rb

@@ -0,0 +1,7 @@
+require_relative 'auto_tune'
+
+# FWBM only... use the puma_auto_tune gem in production!
+num_workers, num_threads = auto_tune
+
+workers num_workers
+threads num_threads, num_threads

+ 6 - 0
frameworks/Ruby/roda-sequel/config/mri_unicorn.rb

@@ -0,0 +1,6 @@
+require_relative 'auto_tune'
+
+# FWBM only...
+num_workers, = auto_tune
+
+worker_processes num_workers

+ 159 - 0
frameworks/Ruby/roda-sequel/hello_world.rb

@@ -0,0 +1,159 @@
+# frozen_string_literal: true
+require 'bundler'
+require 'time'
+
+MAX_PK = 10_000
+QUERIES_MIN = 1
+QUERIES_MAX = 500
+SEQUEL_NO_ASSOCIATIONS = true
+
+Bundler.require(:default) # Load core modules
+
+def connect(dbtype)
+  Bundler.require(dbtype) # Load database-specific modules
+
+  adapters = {
+    :mysql=>{ :jruby=>'jdbc:mysql', :mri=>'mysql2' },
+    :postgresql=>{ :jruby=>'jdbc:postgresql', :mri=>'postgres' }
+  }
+
+  opts = {}
+
+  # Determine threading/thread pool size and timeout
+  if defined?(JRUBY_VERSION)
+    opts[:max_connections] = Integer(ENV.fetch('MAX_CONCURRENCY'))
+    opts[:pool_timeout] = 10
+  elsif defined?(Puma)
+    opts[:max_connections] = Puma.cli_config.options.fetch(:max_threads)
+    opts[:pool_timeout] = 10
+  else
+    Sequel.single_threaded = true
+  end
+
+  Sequel.connect \
+    '%<adapter>s://%<host>s/%<database>s?user=%<user>s&password=%<password>s' % {
+      :adapter=>adapters.fetch(dbtype).fetch(defined?(JRUBY_VERSION) ? :jruby : :mri),
+      :host=>ENV.fetch('DBHOST', '127.0.0.1'),
+      :database=>'hello_world',
+      :user=>'benchmarkdbuser',
+      :password=>'benchmarkdbpass'
+    }, opts
+end
+
+DB = connect(ENV.fetch('DBTYPE').to_sym).tap do |db|
+  db.extension(:freeze_datasets)
+  db.freeze
+end
+
+# Define ORM models
+class World < Sequel::Model(:World)
+  def_column_alias(:randomnumber, :randomNumber) if DB.database_type == :mysql
+end
+
+class Fortune < Sequel::Model(:Fortune)
+  # Allow setting id to zero (0) per benchmark requirements
+  unrestrict_primary_key
+end
+
+# Our Rack application to be executed by rackup
+class HelloWorld < Roda
+  plugin :default_headers, 'Content-Type'=>'text/html; charset=utf-8'
+  plugin :json
+  plugin :render, :escape=>:erubi, :layout_opts=>{ :cache_key=>'default_layout' }
+  plugin :static_routing
+
+  server_string =
+    if defined?(PhusionPassenger)
+      [
+        PhusionPassenger::SharedConstants::SERVER_TOKEN_NAME,
+        PhusionPassenger::VERSION_STRING
+      ].join('/').freeze
+    elsif defined?(Puma)
+      Puma::Const::PUMA_SERVER_STRING
+    elsif defined?(Unicorn)
+      Unicorn::HttpParser::DEFAULTS['SERVER_SOFTWARE']
+    end
+
+  plugin :default_headers, 'Server'=>server_string if server_string
+
+  def bounded_queries
+    queries = request['queries'].to_i
+    return QUERIES_MIN if queries < QUERIES_MIN
+    return QUERIES_MAX if queries > QUERIES_MAX
+    queries
+  end
+
+  # Return a random number between 1 and MAX_PK
+  def rand1
+    Random.rand(MAX_PK).succ
+  end
+
+  # Return an array of `n' unique random numbers between 1 and MAX_PK
+  def randn(n)
+    (1..MAX_PK).to_a.shuffle!.take(n)
+  end
+
+  # Test type 1: JSON serialization
+  static_get '/json' do
+    response['Date'] = Time.now.httpdate
+
+    { :message=>'Hello, World!' }
+  end
+
+  # Test type 2: Single database query
+  static_get '/db' do
+    response['Date'] = Time.now.httpdate
+
+    World.with_pk(rand1).values
+  end
+
+  # Test type 3: Multiple database queries
+  static_get '/queries' do
+    response['Date'] = Time.now.httpdate
+
+    # Benchmark requirements explicitly forbid a WHERE..IN here, so be good
+    randn(bounded_queries)
+      .map! { |id| World.with_pk(id).values }
+  end
+
+  # Test type 4: Fortunes
+  static_get '/fortunes' do
+    response['Date'] = Time.now.httpdate
+
+    @fortunes = Fortune.all
+    @fortunes << Fortune.new(
+      :id=>0,
+      :message=>'Additional fortune added at request time.'
+    )
+    @fortunes.sort_by!(&:message)
+
+    view :fortunes
+  end
+
+  # Test type 5: Database updates
+  static_get '/updates' do
+    response['Date'] = Time.now.httpdate
+
+    # Benchmark requirements explicitly forbid a WHERE..IN here, transactions
+    # are optional, batch updates are allowed (but each transaction can only
+    # read and write a single record?), so... be good
+    randn(bounded_queries).map! do |id|
+      DB.transaction do
+        world = World.for_update.with_pk(id)
+        world.update(:randomnumber=>rand1)
+        world.values
+      end
+    end
+  end
+
+  # Test type 6: Plaintext
+  static_get '/plaintext' do
+    response['Content-Type'] = 'text/plain'
+    response['Date'] = Time.now.httpdate
+
+    'Hello, World!'
+  end
+
+  # Even though we don't have any non-static routes, this is still required.
+  route { |_| }
+end

+ 13 - 0
frameworks/Ruby/roda-sequel/run_jruby_puma.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+
+THREAD_FACTOR=1
+
+. $(dirname $0)/config/common_run.sh
+
+fw_depends $DBTYPE rvm jruby-9.1
+
+rvm use jruby-$JRUBY_VERSION
+
+. $(dirname $0)/config/bundle_install.sh
+
+bundle exec puma -t $MAX_CONCURRENCY:$MAX_CONCURRENCY -b tcp://0.0.0.0:8080 -e production &

+ 13 - 0
frameworks/Ruby/roda-sequel/run_jruby_torquebox.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+
+THREAD_FACTOR=2
+
+. $(dirname $0)/config/common_run.sh
+
+fw_depends $DBTYPE rvm jruby-9.1
+
+rvm use jruby-$JRUBY_VERSION
+
+. $(dirname $0)/config/bundle_install.sh
+
+bundle exec torquebox run --io-threads $MAX_CONCURRENCY --worker-threads $MAX_CONCURRENCY -b 0.0.0.0 -p 8080 -e production &

+ 20 - 0
frameworks/Ruby/roda-sequel/run_mri_passenger.sh

@@ -0,0 +1,20 @@
+#!/bin/bash
+
+. $(dirname $0)/config/common_run.sh
+
+fw_depends $DBTYPE rvm ruby-2.4
+
+rvm use ruby-$MRI_VERSION
+
+. $(dirname $0)/config/bundle_install.sh
+
+# TODO: https://github.com/phusion/passenger/issues/1916
+export _PASSENGER_FORCE_HTTP_SESSION=true
+
+# FWBM only... Passenger will auto-tune itself in production!
+instances=$(ruby -r$(dirname $0)/config/auto_tune -e 'puts auto_tune.first')
+
+bundle exec passenger start --log-level 1 \
+  --engine builtin --disable-turbocaching --disable-security-update-check \
+  --spawn-method direct --max-pool-size $instances --min-instances $instances --max-request-queue-size 1024 \
+  --address 0.0.0.0 --port 8080 --environment production &

+ 11 - 0
frameworks/Ruby/roda-sequel/run_mri_puma.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+
+. $(dirname $0)/config/common_run.sh
+
+fw_depends $DBTYPE rvm ruby-2.4
+
+rvm use ruby-$MRI_VERSION
+
+. $(dirname $0)/config/bundle_install.sh
+
+bundle exec puma -C config/mri_puma.rb -b tcp://0.0.0.0:8080 -e production &

+ 11 - 0
frameworks/Ruby/roda-sequel/run_mri_unicorn.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+
+. $(dirname $0)/config/common_run.sh
+
+fw_depends $DBTYPE rvm ruby-2.4
+
+rvm use ruby-$MRI_VERSION
+
+. $(dirname $0)/config/bundle_install.sh
+
+bundle exec unicorn -c config/mri_unicorn.rb -o 0.0.0.0 -p 8080 -E production &

+ 1 - 0
frameworks/Ruby/roda-sequel/source_code

@@ -0,0 +1 @@
+./roda-sequel/hello_world.rb

+ 12 - 0
frameworks/Ruby/roda-sequel/views/fortunes.erb

@@ -0,0 +1,12 @@
+<table>
+<tr>
+  <th>id</th>
+  <th>message</th>
+</tr>
+<% @fortunes.each do |fortune| %>
+<tr>
+  <td><%= fortune.id %></td>
+  <td><%= fortune.message %></td>
+</tr>
+<% end %>
+</table>

+ 11 - 0
frameworks/Ruby/roda-sequel/views/layout.erb

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Fortunes</title>
+</head>
+
+<body>
+<%== yield %>
+</body>
+
+</html>