ActiveRecord vs Sequel

Written by Walter on 20/1/2011

< >

Started on writing a lightweight Sinatra application and did some benchmarks.

First fill a user table with some records:

(1..9999).each{|n|User.create( :phone=>"+111111"+n.to_s, :password=>"anysecret" )}

Then tested the exact same sinatra app first with sinatra-activerecord and then with the excellent sequel gem: Basic sinatra app and then we can do for active record: Active record version for testing:


require 'sinatra'
require 'mysql2'
require 'sinatra/activerecord'    
require 'yaml'
require 'json'

... connect db ...

class User < ActiveRecord::Base
  set_table_name :user
end


Sequel version for testing:

require 'sinatra'
require 'mysql2'
require 'sequel'                  #alternative orm as activercord
require 'yaml'
require 'json'


#sequel model
class User < Sequel::Model(:user)
end


Then we query all users in a page and return json:


#json request active record
get '/active/user/:id' do
  u = User.find( params[:id] )
  content_type :json
  { :name => u.phone, :login => u.login }.to_json

end

#json request sequel
get '/sequel/user/:id' do
  u = User[params[:id]]
  content_type :json
  { :name => u.phone, :email => u.email }.to_json
end

So we have '/sequal/users' and '/active/users'

When running ruby 1.9.2 and rails 3.0.7 on my laptop I get different benchmark results. I'm not even using passenger just runnning ruby app.rb just to see some results quickly. Basically just firing ruby app.rb takes 5 times longer with activerecord than with sequel gem.

But now for actually testing the loading of the page with apache benchmark tool I get more accurate results:

ab -n 2000 -c 10 'http://localhost:4567/active/users'

... just hangs basically, to be honest we should try it with passenger on my webserver instead of locally on laptop, still it's not looking good for just concurrency 10:


ab -n 2000 -c 10 'http://localhost:4567/sequel/users' 
Total transferred: 3453700 bytes 
HTML transferred: 3435900 bytes Requests per second: 39.05 [#/sec] (mean) 
Time per request: 25.606 [ms] (mean) 
Time per request: 25.606 [ms] (mean, across all concurrent requests) 
Transfer rate: 1317.15 [Kbytes/sec] received 

With sequel however it runs just fine. So either something is wrong with my sinatra-activerecord gem or it does not like the low startup times here and we need passenger to get it running properly... In short sequel+sinatra seems to be a winner here ;).

Can't wait to see how fast it will be with passenger since I get speeds up to 800req/sec easily when running a rails app in production on the webserver this little sequel/sinatra app should be going at laser speeds ;) UPDATE: Not so good news, when running this sinatra/sequel app and actually benchmark it with passenger/apache2 running it just locks up before ab can finish. I posted a question on the sequel gem author, maybe they can help me out.

For now sticking with activerecord. I switched back to sinatra/activerecord. Ran my benchmarks on my webserver and bam 4000+ req/second !


  1 require 'sinatra'
  2 require 'mysql2'
  3 require 'sinatra/activerecord'
  4 require 'yaml'
  5 require 'json'
  6 #require './data/init.rb'
  7 
  8 #this is solely for benchmarking, cleaning up and restructuring code now ;)
  9 set :database_extras, { :password=>'thepass', :encoding=>'utf8', :max_connections=>5 }
 10 set :database, URI.encode( "mysql2://root:nowayM@localhost/ScaleChampion_development" )
 11 
 12 class User < ActiveRecord::Base
 13   #set_table_name :users
 14 end
 15 
 16 
 17 get '/' do
 18   erb :getAuthToken
 19 end
 20 
 21 get '/getAuthToken' do
 22   erb :getAuthToken 
 23 end
 24 
 25 
 26 post '/getAuthToken' do
 27   erb :getAuthToken  
 28 end
 29 
 30 
 31 get '/users' do
 32   content_type :json
 33   User.all.map{|u|
 34     { :id=>u.id, :name => u.name, :email => u[:email] }.to_json
 35   } 
 36 end
 37 
 38 #json request sequel
 39 get '/user/:id' do
 40   #u = User[params[:id]]
 41   u = User.find( params[:id] )
 42   #u = { :name=>"walter", :email=>"somethingfast@bla.com" }
 43   content_type :json
 44   { :name => u.name, :email => u.email }.to_json
 45 end 
 

Following test 6000 requests with 200 concurrency:


ab -n 6000 -c 200 'http://viu.sitweb.eu/user/100'

HTML transferred:       264000 bytes
Requests per second:    4095.47 [#/sec] (mean)
Time per request:       48.834 [ms] (mean)
Time per request:       0.244 [ms] (mean, across all concurrent requests)
Transfer rate:          1103.86 [Kbytes/sec] received

Fast enough for me. And the good news is that this sinatra json app can reuse all my models allready written for the rails app of viu2, hurrai ;) After restructuring and cleaning I have following sinatra app (cool is that it now uses the same database.yml format as a rails app and has views+layout.rb):


root@Debian-60-squeeze-64-LAMP:/var/www/viu2_json# tree .
.
├── app.rb
├── config.ru
├── data
│   ├── database.yml
│   ├── database.yml.option
│   ├── init.rb
│   └── models.rb
├── public
├── Rakefile
├── README.install
├── tmp
│   └── restart.txt
└── views
    ├── getAuthToken.erb
    ├── layout.erb
    └── showUser.erb

Performance is still equal as before. And for regular pages without mysql queries I get 6000 req/sec. Awesome! Now it's a matter of using some smart require's to include the models from the rails app so we have 0 code duplication ;)

Back to archive