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 ;)