Browse Source

sinatra-sequel on MRI only (for now)

Mike Pastore 9 years ago
parent
commit
f3e52fce4d

+ 1 - 0
.travis.yml

@@ -157,6 +157,7 @@ env:
     - "TESTDIR=Ruby/rails"
     - "TESTDIR=Ruby/rails-stripped"
     - "TESTDIR=Ruby/sinatra"
+    - "TESTDIR=Ruby/sinatra-sequel"
     - "TESTDIR=Rust/iron"
     - "TESTDIR=Rust/nickel"
     - "TESTDIR=Rust/hyper"

+ 2 - 0
frameworks/Ruby/sinatra-sequel/.gitignore

@@ -0,0 +1,2 @@
+.bundle
+vendor/bundle

+ 8 - 17
frameworks/Ruby/sinatra-sequel/Gemfile

@@ -1,24 +1,15 @@
-source 'http://rubygems.org'
-
-platforms :jruby do
-  gem "activerecord-jdbcmysql-adapter", "~> 1.3.9", :require => false
-  gem 'torqbox', '0.1.7'
-  gem "trinidad", '1.4.6'
-end
+source 'https://rubygems.org'
 
 platforms :ruby do
-  gem 'mysql2', '0.3.16'
-  gem "unicorn", '4.8.3'
-  gem "thin", '1.6.2'
+  gem 'mysql2', '0.4.0'
+  gem 'json', '1.8.1', :require => 'json/ext'
 end
 
-gem "puma", '2.9.0'
+gem 'puma', '2.15.3'
 
-gem "activerecord-import", '0.5.0'
-gem 'activerecord', '4.1.4', :require => 'active_record'
+gem 'sequel', '4.28.0'
 
-gem "sinatra-activerecord", '2.0.2'
-gem 'sinatra', '1.4.5'
-gem 'sinatra-contrib', '1.4.2'
+gem 'sinatra', '1.4.6', :require => 'sinatra/base'
+gem 'sinatra-contrib', '1.4.6', :require => 'sinatra/json'
 
-gem 'slim', '2.0.3'
+gem 'slim', '3.0.6'

+ 9 - 13
frameworks/Ruby/sinatra-sequel/README.md

@@ -1,24 +1,20 @@
 # Ruby [Sinatra](http://www.sinatrarb.com/) Benchmarking Test
 
-The information below contains information specific to Sinatra. 
-For further guidance, review the 
-[documentation](http://frameworkbenchmarks.readthedocs.org/en/latest/). 
+The information below contains information specific to Sinatra.
+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 Sinatra portion of a [benchmarking test suite](../../) 
+This is the Ruby Sinatra portion of a [benchmarking test suite](../../)
 comparing a variety of web platforms.
 
 ## Infrastructure Software Versions
 The tests were run with:
-* [Ruby 2.0.0-p0](http://www.ruby-lang.org/)
-* [JRuby 1.7.8](http://jruby.org/)
-* [Rubinius 2.2.10](http://rubini.us/)
-* [Sinatra 1.3.4](http://www.sinatrarb.com/)
-* [Unicorn 4.6.2](http://unicorn.bogomips.org/)
-* [TorqBox 0.1.7](http://torquebox.org/torqbox/)
-* [Puma 2.9.0](http://puma.io/)
-* [Thin 1.6.2](http://code.macournoyer.com/thin/)
+* [Ruby 2.2.3](http://www.ruby-lang.org/)
+* [Sinatra 1.4.6](http://www.sinatrarb.com/)
+* [Puma 2.15.3](http://puma.io/)
 * [MySQL 5.5.29](https://dev.mysql.com/)
+* [Sequel 4.28.0](http://sequel.jeremyevans.net/documentation.html)
 
 ## Paths & Source for Tests
 
@@ -39,8 +35,8 @@ _No experts listed, yet. If you're an expert, add yourself!_
 
 * [Sinatra Google Group](https://groups.google.com/forum/#!forum/sinatrarb)
 * `#sinatra` IRC Channel ([irc.freenode.net](http://freenode.net/))
+* `#sinatrarb` on the [Sinatra and Friends](http://sinatra-slack.herokuapp.com) Slack
 
 ### Resources
 
 * [Sinatra Source Code](https://github.com/sinatra/sinatra)
-* [PR: passenger-install-apache2-module doesn't work on ruby 2.0](https://github.com/FooBarWidget/passenger/pull/71)

+ 5 - 143
frameworks/Ruby/sinatra-sequel/benchmark_config.json

@@ -1,5 +1,5 @@
 {
-  "framework": "sinatra",
+  "framework": "sinatra-sequel",
   "tests": [{
     "default": {
       "setup_file": "run_mri_puma",
@@ -7,160 +7,22 @@
       "db_url": "/db",
       "query_url": "/queries?queries=",
       "fortune_url": "/fortunes",
-      "update_url": "/updates?queries=", 
+      "update_url": "/updates?queries=",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Micro",
       "database": "MySQL",
-      "framework": "sinatra",
+      "framework": "sinatra-sequel",
       "language": "Ruby",
       "orm": "Full",
       "platform": "Rack",
       "webserver": "Puma",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "sinatra-puma-mri",
+      "display_name": "sinatra-sequel-puma-mri",
       "notes": "",
       "versus": "rack-puma-mri"
-    },
-    "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": "sinatra",
-      "language": "Ruby",
-      "orm": "Full",
-      "platform": "JRuby",
-      "webserver": "Puma",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "sinatra-puma-jruby",
-      "notes": "",
-      "versus": "rack-puma-jruby"
-    },
-    "puma-rbx": {
-      "setup_file": "run_rbx_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": "sinatra",
-      "language": "Ruby",
-      "orm": "Full",
-      "platform": "Rubinius",
-      "webserver": "Puma",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "sinatra-puma-rbx",
-      "notes": "",
-      "versus": "rack-puma-rbx"
-    },
-    "thin": {
-      "setup_file": "run_thin",
-      "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": "sinatra",
-      "language": "Ruby",
-      "orm": "Full",
-      "platform": "Rack",
-      "webserver": "Thin",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "sinatra-thin",
-      "notes": "",
-      "versus": "rack-thin"
-    },
-    "torqbox-jruby": {
-      "setup_file": "run_torqbox",
-      "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": "sinatra",
-      "language": "Ruby",
-      "orm": "Full",
-      "platform": "JRuby",
-      "webserver": "TorqBox",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "sinatra-torqbox-jruby",
-      "notes": "",
-      "versus": "rack-torqbox-jruby"
-    },
-    "trinidad-jruby": {
-      "setup_file": "run_trinidad",
-      "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": "sinatra",
-      "language": "Ruby",
-      "orm": "Full",
-      "platform": "JRuby",
-      "webserver": "Trinidad",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "sinatra-trinidad-jruby",
-      "notes": "",
-      "versus": "rack-trinidad-jruby"
-    },
-    "unicorn": {
-      "setup_file": "run_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": "sinatra",
-      "language": "Ruby",
-      "orm": "Full",
-      "platform": "Rack",
-      "webserver": "Unicorn",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "sinatra-unicorn",
-      "notes": "",
-      "versus": "rack-unicorn"
-    }    
+    }
   }]
 }

+ 2 - 2
frameworks/Ruby/sinatra-sequel/config.ru

@@ -1,2 +1,2 @@
-require './hello_world'
-run Sinatra::Application
+require_relative 'hello_world'
+run HelloWorld

+ 0 - 3
frameworks/Ruby/sinatra-sequel/config/puma.rb

@@ -1,3 +0,0 @@
-environment 'production'
-threads 8, 32
-bind 'tcp://0.0.0.0:8080'

+ 0 - 10
frameworks/Ruby/sinatra-sequel/config/thin.yml

@@ -1,10 +0,0 @@
---- 
-timeout: 30
-wait: 30
-max_conns: 1024
-max_persistent_conns: 512
-environment: production
-port: 8080
-servers: 8
-log: /tmp/thin.log
-quiet: true

+ 0 - 8
frameworks/Ruby/sinatra-sequel/config/trinidad.yml

@@ -1,8 +0,0 @@
----
-  port: 8080
-  threadsafe: true
-  environment: production
-  http:
-    address: '*'
-  logging:
-    level: SEVERE

+ 0 - 7
frameworks/Ruby/sinatra-sequel/config/unicorn.rb

@@ -1,7 +0,0 @@
-worker_processes 8
-listen "/tmp/.sock", :backlog => 256
-preload_app true
-GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true
-
-before_fork { |server, worker| }
-after_fork { |server, worker| }

+ 98 - 73
frameworks/Ruby/sinatra-sequel/hello_world.rb

@@ -1,91 +1,116 @@
-require 'active_record'
+MAX_PK = 10_000
+SEQUEL_NO_ASSOCIATIONS = true
+
 Bundler.require :default
 
-set :logging, false
-ActiveRecord::Base.logger = nil
-set :activerecord_logger, nil
-set :static, false
-set :template_engine, :slim
-Slim::Engine.set_default_options format: :html5, sort_attrs: false
-
-# Specify the encoder - otherwise, sinatra/json inefficiently
-# attempts to load one of several on each request
-set :json_encoder => :to_json
-
-# Don't prefix JSON results with { "world": {...} }
-ActiveRecord::Base.include_root_in_json = false
-
-db_config = { :database => 'hello_world', :username => 'benchmarkdbuser', :password => 'benchmarkdbpass', :pool => 256, :timeout => 5000 }
-adapter = RUBY_PLATFORM == 'java' ? 'jdbcmysql' : 'mysql2'
-set :database, db_config.merge(:adapter => adapter, :host => ENV['DB_HOST'])
-
-# The sinatra-activerecord gem registers before and after filters that
-# call expensive synchronized ActiveRecord methods on every request to
-# verify every connection in the pool, even for routes that don't use
-# the database. Clear those filters and handle connection management
-# ourselves, which is what applications seeking high throughput with
-# ActiveRecord need to do anyway.
-settings.filters[:before].clear
-settings.filters[:after].clear
-
-class World < ActiveRecord::Base
-  self.table_name = "World"
+# Configure Slim templating engine
+Slim::Engine.set_options \
+  :format => :html,
+  :sort_attrs => false
+
+# Configure Sequel ORM
+DB = Sequel.connect \
+  :adapter => RUBY_PLATFORM == 'java' ? 'jdbc:mysql' : 'mysql2',
+  :host => ENV['DB_HOST'],
+  :database => 'hello_world',
+  :user => 'benchmarkdbuser',
+  :password => 'benchmarkdbpass',
+  :max_connections => 32, # == max worker threads per process
+  :pool_timeout => 5
+
+# Allow #to_json on models and arrays of models
+Sequel::Model.plugin :json_serializer
+
+class World < Sequel::Model(:World); end
+
+class Fortune < Sequel::Model(:Fortune)
+  # Allow setting id to zero (0) per benchmark requirements
+  unrestrict_primary_key
 end
 
-class Fortune < ActiveRecord::Base
-  self.table_name = "Fortune"
-end
+# Our Rack application to be executed by rackup
+class HelloWorld < Sinatra::Base
+  configure do
+    disable :protection
 
-get '/json' do
-  json :message => 'Hello, World!'
-end
+    # Don't add ;charset= to any content types per the benchmark requirements
+    set :add_charset, []
 
-get '/plaintext' do
-  content_type 'text/plain'
-  'Hello, World!'
-end
-
-get '/db' do
-  worlds = ActiveRecord::Base.connection_pool.with_connection do
-    World.find(Random.rand(10000) + 1)
+    # Specify the encoder - otherwise, sinatra/json inefficiently
+    # attempts to load one of several on each request
+    set :json_encoder, :to_json
   end
-  json(worlds)
-end
 
-get '/queries' do
-  queries = (params[:queries] || 1).to_i
-  queries = 1 if queries < 1
-  queries = 500 if queries > 500
+  helpers do
+    # Return a random number between 1 and MAX_PK
+    def rand1
+      Random.rand(MAX_PK) + 1
+    end
 
-  worlds = ActiveRecord::Base.connection_pool.with_connection do
-    (1..queries).map do
-      World.find(Random.rand(10000) + 1)
+    # 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
   end
-  json(worlds)
-end
 
-get '/fortunes' do
-  @fortunes = Fortune.all
-  @fortunes << Fortune.new(:id => 0, :message => "Additional fortune added at request time.")
-  @fortunes = @fortunes.sort_by { |x| x.message }
+  after do
+    # Add mandatory HTTP headers to every response
+    response['Server'] = 'Puma'
+    response['Date'] = Time.now.to_s
+  end
 
-  slim :fortunes
-end
+  get '/json' do
+    json :message => 'Hello, World!'
+  end
 
-get '/updates' do
-  queries = (params[:queries] || 1).to_i
-  queries = 1 if queries < 1
-  queries = 500 if queries > 500
+  get '/plaintext' do
+    content_type 'text/plain'
+    'Hello, World!'
+  end
 
-  worlds = ActiveRecord::Base.connection_pool.with_connection do
-    worlds = (1..queries).map do
-      world = World.find(Random.rand(10000) + 1)
-      world.randomNumber = Random.rand(10000) + 1
-      world
+  get '/db' do
+    json World[rand1]
+  end
+
+  get '/queries' do
+    queries = (params[:queries] || 1).to_i
+    queries = 1 if queries < 1
+    queries = 500 if queries > 500
+
+    json World.where(:id => randn(queries))
+  end
+
+  get '/fortunes' do
+    @fortunes = Fortune.all
+    @fortunes << Fortune.new(
+      :id => 0,
+      :message => 'Additional fortune added at request time.'
+    )
+    @fortunes.sort_by!(&:message)
+
+    slim :fortunes
+  end
+
+  get '/updates' do
+    queries = (params[:queries] || 1).to_i
+    queries = 1 if queries < 1
+    queries = 500 if queries > 500
+
+    # Prepare our updates in advance so transaction retries are idempotent
+    updates = randn(queries).sort!.map! { |id| [id, rand1] }
+
+    worlds = nil
+
+    World.db.transaction do
+      worlds = World
+        .where(:id => updates.transpose.first)
+        .for_update
+
+      World.dataset
+        .on_duplicate_key_update(:randomNumber)
+        .import([:id, :randomNumber], updates)
     end
-    World.import worlds, :on_duplicate_key_update => [:randomNumber]
-    worlds
+
+    json worlds
   end
-  json(worlds)
 end

+ 0 - 9
frameworks/Ruby/sinatra-sequel/run_jruby_puma.sh

@@ -1,9 +0,0 @@
-#!/bin/bash
-
-fw_depends rvm java7 jruby-1.7.8
-
-rvm jruby-1.7.8 do bundle install --gemfile=$TROOT/Gemfile
-
-rvm jruby-1.7.8 do bundle --jobs 4
-
-DB_HOST=${DBHOST} rvm jruby-1.7.8 do bundle exec puma -C config/puma.rb &

+ 5 - 4
frameworks/Ruby/sinatra-sequel/run_mri_puma.sh

@@ -1,9 +1,10 @@
 #!/bin/bash
 
-fw_depends rvm ruby-2.0.0
+MRI_VERSION=ruby-2.2.1
 
-rvm jruby-1.7.8 do bundle install --gemfile=$TROOT/Gemfile
+fw_depends rvm $MRI_VERSION
 
-rvm ruby-2.0.0-p0 do bundle --jobs 4
+rvm $MRI_VERSION do bundle install --gemfile=$TROOT/Gemfile --path vendor/bundle
 
-DB_HOST=${DBHOST} rvm ruby-2.0.0-p0 do bundle exec puma -C config/puma.rb -w 8 --preload &
+DB_HOST=${DBHOST} \
+rvm $MRI_VERSION do bundle exec puma -w 8 -t 8:32 -b tcp://0.0.0.0:8080 -e production &

+ 0 - 7
frameworks/Ruby/sinatra-sequel/run_rbx_puma.sh

@@ -1,7 +0,0 @@
-#!/bin/bash
-
-fw_depends rvm rbx-2.2.10
-
-rvm rbx-2.2.10 do bundle install --gemfile=$TROOT/Gemfile
-
-DB_HOST=${DBHOST} rvm rbx-2.2.10 do bundle exec puma &

+ 0 - 9
frameworks/Ruby/sinatra-sequel/run_thin.sh

@@ -1,9 +0,0 @@
-#!/bin/bash
-
-fw_depends rvm ruby-2.0.0
-
-rvm ruby-2.0.0-p0 do bundle install --gemfile=$TROOT/Gemfile --path=vendor/bundle
-
-rvm ruby-2.0.0-p0 do bundle --jobs 4
-
-DB_HOST=${DBHOST} rvm ruby-2.0.0-p0 do bundle exec thin start -C config/thin.yml &

+ 0 - 9
frameworks/Ruby/sinatra-sequel/run_torqbox.sh

@@ -1,9 +0,0 @@
-#!/bin/bash
-
-fw_depends rvm java7 jruby-1.7.8
-
-rvm jruby-1.7.8 do bundle install --gemfile=$TROOT/Gemfile
-
-rvm jruby-1.7.8 do bundle --jobs 4
-
-DB_HOST=${DBHOST} rvm jruby-1.7.8 do bundle exec torqbox -b 0.0.0.0 -E production &

+ 0 - 9
frameworks/Ruby/sinatra-sequel/run_trinidad.sh

@@ -1,9 +0,0 @@
-#!/bin/bash
-
-fw_depends rvm java7 jruby-1.7.8
-
-rvm jruby-1.7.8 do bundle install --gemfile=$TROOT/Gemfile
-
-rvm jruby-1.7.8 do bundle --jobs 4
-
-DB_HOST=${DBHOST} rvm jruby-1.7.8 do bundle exec trinidad --config config/trinidad.yml &

+ 0 - 13
frameworks/Ruby/sinatra-sequel/run_unicorn.sh

@@ -1,13 +0,0 @@
-#!/bin/bash
-
-fw_depends rvm ruby-2.0.0 nginx
-
-sed -i 's|/usr/local/nginx/|'"${IROOT}"'/nginx/|g' config/nginx.conf
-
-rvm ruby-2.0.0-p0 do bundle install --gemfile=$TROOT/Gemfile --path=vendor/bundle
-
-rvm ruby-2.0.0-p0 do bundle --jobs 4
-
-nginx -c $TROOT/config/nginx.conf
-
-DB_HOST=${DBHOST} rvm ruby-2.0.0-p0 do bundle exec unicorn -E production -c config/unicorn.rb &

+ 28 - 0
toolset/setup/linux/languages/ruby-2.2.1.sh

@@ -0,0 +1,28 @@
+#!/bin/bash
+
+fw_depends rvm
+
+VERSION=2.2.1
+RETCODE=$(fw_exists ${IROOT}/ruby-${VERSION}.installed)
+[ ! "$RETCODE" == 0 ] || { \
+  # Load environment variables
+  source $IROOT/ruby-$VERSION.installed
+  return 0; }
+
+# We assume single-user installation as 
+# done in our rvm.sh script and 
+# in Travis-CI
+if [ "$TRAVIS" = "true" ]
+then
+  rvmsudo rvm install $VERSION
+  # Bundler is SOMETIMES missing... not sure why.
+  rvmsudo rvm $VERSION do gem install bundler
+else
+  rvm install $VERSION
+  # Bundler is SOMETIMES missing... not sure why.
+  rvm $VERSION do gem install bundler
+fi
+
+echo "" > $IROOT/ruby-$VERSION.installed
+
+source $IROOT/ruby-$VERSION.installed