-
Aug
15Mongomatic
Posted in : ruby, mongo, and benchmark
An addition to our series of MongoDB ORM benchmarks, a brand new ruby orm for MongoDB called Mongomatic which claim to wash whiter than washing competitors.

Our benchmark highlight the fact that Mongomatic let MongoID in the dust in raw speed. I don’t know if Mongomatic is the best orms for MongoDB, but it may actually be the fastest.
The source for all these benchmarks is freely available on github.
-
Aug
02Compile WSO2/WSF-C and WSO2/WSF-ruby on Snow Leopard
Posted in : ruby, wso2, wsf, macosx, and snow leopard
If you tried to compile compile WSO2/WSF-C and WSO2/WSF-Ruby extension on Snow Leopard, you know how painful it is.
Here’s the configure options for WSO2/WSF-C
./configure --prefix=/opt/wso2/wsf_c CC="gcc-4.0" CXX="g++-4.0" CPPFLAGS="-DHAVE_GETIFADDRS"Then for WSO2/WSF-Ruby, you will have to replace all occurences of:
axis2-1.4.0 with axis2-1.6.0 rampart-1.2.0 with rampart-1.3.0Use the provided script to find rbconfig.rb file:
ruby find_rbconfig.rbAnd add following configs:
#--------------------------------------------------------------------------------- CONFIG["WSFC_HOME"] = "/opt/wso2/wsf_c" CONFIG["WSF_LOG_DIR"] = "/var/log/" CONFIG["WSF_LOG_LEVEL"] = "3" CONFIG["WSF_RUBY_HOME"] = "/opt/wso2/wsf_ruby" #---------------------------------------------------------------------------------And run the build script:
# if you're using rvm sh build.sh # else sudo sh build.sh -
Jul
24Benchmark various MongoDB ORM for Ruby
Posted in : ruby, mongo_mapper, mongoid, and benchmark
Inspired by a benchmark of MongoDB versus SQLite by Jeremy Evans, the famous author of Sequel, here is a benchmark of MongoDB via the mongo ruby driver versus Sqlite via Sequel:

The faster “select alls” can be easily explained: SQLite ids are simple integers whereas mongo ids look like this:
BSON::ObjectID('4c4b20a41a221842e3000002')So there’re simply more data the gather each time. Sqlite is quite fast in fact, but don’t forget that it offers neither concurrency nor scalability.
More interesting is the following chart featuring MongoMapper versus Mongoid versus plain mongo ruby driver:

And here’s more ODM bench:

The source for all these benchmarks is freely available on github.
-
Jul
20Add routes at runtime on Rails 3
Posted in : rails and rails 3
Sometimes you just want to take the forbidden path. We had the case quite recently, while testing a Paypal integration. We did want to add a route to a fake Paypal service with a link back to our website after succesful payment, but at the same time we didn’t want to polute our routes.rb just for test purpose. Hopefully, it’s still possible to add routes at runtime even on Rails 3. The issue is calling Rails::Application.routes.draw clears all existing routes and after that no further route can be defined. The solution is inspired by how Rails is doing route reloading:
begin _routes = Rails::Application.routes _routes.disable_clear_and_finalize = true _routes.clear! Rails::Application.routes_reloader.paths.each{ |path| load(path) } _routes.draw do # here you can add any route you want get "/test#{rand(1000000)}", :to => "sessions#new" end ActiveSupport.on_load(:action_controller) { _routes.finalize! } ensure _routes.disable_clear_and_finalize = false endPlease note that using Rails::Application is deprecated and that you should replace it with MyApplicationName::Application everywhere instead. Happy hacking ;)
-
Jul
15Rack::Test warning on Ruby 1.9.x
Posted in : ruby and rack
If you’ve tried to use Rack::Test on Ruby 1.9.x, which is the default when using capybara for exemple, you’ve probably been bugged by nasty warning like this one:
~/.rvm/gems/ruby-1.9.2-head/gems/rack-1.2.1/lib/rack/utils.rb:16: warning: regexp match /.../n against to UTF-8 stringThis problem comes when trying to send any UTF-8 character in params. It may even happen without you trying to send any now the rails does automatically add a snowman in all your forms (not a joke). One solution to silence these warnings is to add:
$VERBOSE=nilsomewhere in your initialization. Another solution is to patch rack itself.
I propose an even better solution, the great escape_utils gem. Not only does it removes the warning, escaping will also be faster, I mean like 45x faster!
It’s nearly too easy to be true, just add this to your Gemfile:
gem "escape_utils"Then somewhere in your initialization (like in config/initializer in your using rails):
module Rack module Utils def escape(s) EscapeUtils.escape_url(s) end end endProblem solved! Now that you’re at it, why not add these lines too:
require "escape_utils/html/rack" require "escape_utils/html/haml"If you’re using erb instead of haml, replace last line by:
require "escape_utils/html/erb"Now all your html escaping will be faster too, this couldn’t be easier!
-
Apr
24Cucumber with Gherkin Parser
Posted in : cucumber, ruby, and bdd
The Cucumber team released some days ago a beta of the 0.7.0 version. With this release comes one thing we were waiting for a long time : Gherkin integration.
Gherkin is a new parser for the Gherkin language which features are written in. It’s using Ragel and thus generate fast parsers in pure Ruby, C (MRI extension) and Java.
A thread about the performance this brings was started in the Cukes Google Group for the v0.7.0.beta.2 release.
Here’s what we got when switching from Cucumber v0.6.4 to v0.7.0.beta.2:
162 Features 902 Scenario 12993 Steps Cucumber 0.6.4 : Parsing feature files took 1m44s Cucumber v0.7.0.beta.2 Parsing feature files took 0m1sThis means our cucumber startup time dropped from nearly 2 minutes to … instant.
Now, what’s your results ?
-
Apr
09Mirror a GIT Repository for Backup
Posted in : git
Here’s a quick way to easily maintain a complete mirror of a git repository. This is especially useful for backup.
-
Mar
05CSS3 Expression to XPath with Nokogiri
Posted in : ruby, nokogiri, and xpath
Nokogiri is such a wonderful library. Today we were trying to get the equivalent xpath query from a css expression and we found a fun way to do it automatically using Nokogiri.
Install nokogiri
First step is to have nokogiri installed
gem install nokogiriTry it in irb
Open a terminal and start irb
irbThen try some fun translations
As you could see the XPath queries generated are a little bit verbose, but at least you can quickly get a working query if you’re more familiar with CSS3 expressions.
-
Mar
02Easy comet with ruby and redis
Posted in : comet, ruby, redis, sinatra, async, thin, and benchmark
Today we want to try something new and funny. Let’s do some Comet “Comet at Wikipedia”) aka HTTP server push using Ajax and long pulling requests as used in Flash-less chat services such as Campfire or Talker.
Sikwamic was the closest we could find from what we wish to do. Moreover when Redis is in the project, speed is usually there.
Why not take a few minute and install ruby 1.9.2 and sinatra 1.0 unless it’s already what you use.
Install redis
If you’re on Mac OS X, chances are that you’ll want to install redis using homebrew and we cannot blame you because that’s exactly what we did:
brew install redisYou should then be able to start the redis server with something like this:
redis-server `brew --prefix`/etc/redis.confYou can stop the redis server with:
PREFIX=`brew --prefix` && kill `cat $PREFIX/var/run/redis.pid`Another solution is to set “daemonize no” in redis.conf so that you can simply stop it with CTRL-C.
Now you also need to install the redis gem to allow ruby to talk to your redis server:
gem install redisIf you’re not on Mac OS X or don’t use homebrew, another solution is to install redis through redis-rb.
Try redis
You can test if it did work by opening an irb console and trying these commands taken from Programmer’s Paradox guide:
If the last command returns “hello world” then you have a working redis server, if you have Errno::ECONNREFUSED errors then the redis server is probably not running.
Try Sikwamic
In the unlickely event where you haven’t installed git yet, you can install it with homebrew using the command:
brew install gitThen clone Sikwamic git repository:
git clone git://github.com/dorkalev/Sikwamic.git cd SikwamicSikwamic requires mongrel so you’ll have to install the gem:
gem install mongrel --source http://gems.rubyinstaller.orgThen run Sikwamic:
ruby sikwamic.rbOpen your browser.
- http://localhost:9291/set/user_55_name/john to set the current value in redis
- http://localhost:9291/get_if_modified/user_55_name/jeff should show john immediately
- http://localhost:9291/get_if_modified/user_55_name/john should wait 5 seconds for a change before giving up and finally showing john again
It’s easy to see that this behaviour can be used to return the latest messages in a chatroom for example, so it’s exactly what we wanted. But how does it scale?
Benchmark Sikwamic using ab
We used ab aka Apache Benchmark because it’s included in Xcode, so we have it anyway. If you’re not using Mac OS X, you’ll have to install it.
ab -n 2 -c 1 http://127.0.0.1:9291/get_if_modified/user_55_name/johnSimple: 2 connections, no concurrency. It logically takes 10 seconds. Now:
ab -n 2 -c 2 http://127.0.0.1:9291/get_if_modified/user_55_name/johnIf you look at where Sikwamic is running, you’ll see the console is littered with errors. That’s really bad. At this point, the application can only handle one client at a time, not exactly what you would have expected. Following the author comment at the bottom of the article, we changed the redis connection to be thread-safe. Restart the server, set user_55_name to john again, now try again:
ab -n 2 -c 2 http://127.0.0.1:9291/get_if_modified/user_55_name/johnThis time we have 2 completed requests in 5014ms, so it did actually work. The server was able to take both requests at the same time and keep them open (long pulling) while waiting for the key to change. Let’s try something bigger:
ab -n 100 -c 100 http://127.0.0.1:9291/get_if_modified/user_55_name/johnThis time we’re doing 100 concurrent waiting queries. Time taken for tests: 5.146 seconds. It scales pretty good actually. Let’s continue:
ab -n 500 -c 500 http://127.0.0.1:9291/get_if_modified/user_55_name/johnUnluckily, all we get as a result is error “socket: Too many open files”. We must change the command to:
ulimit -n 10100 && ab -n 500 -c 500 http://127.0.0.1:9291/get_if_modified/user_55_name/johnNot much better, this time it tells: “apr_socket_connect(): Connection reset by peer”. The error seems to show a limitation of ab under Mac OS X more than a limitation of our Ruby application itself.
Benchmark Sikwamic using a custom libevent-based http client
We briefly tried httperf but hit the same limitations as when using ab. This post describes how they were able to open 10k concurrent connections with a Mac OS C client to test their application, thanks to a custom libevent-based http client written in C. Let’s try it out.
First, we’ll need to install libevent. Using homebrew, it couldn’t be easier:
brew install libeventThen download the C source file using your browser or in terminal:
curl http://gist.github.com/raw/201450/a831b040f446fa5a4baea8a703956b75e74b0f07/c10k-test-client.c -o c10k-test-client.cCompile instructions are at the end of the file. As we use homebrew, the compile command turns to:
gcc -o floodtest -levent -I`brew --prefix`/include -L`brew --prefix`/lib c10k-test-client.cTo show usage, run:
./floodtest --helpGood, so let’s try it against Sikwamic:
ulimit -n 10100 && ./floodtest 10000 127.0.0.1 9291 /get_if_modified/user_55_name/johnulimit is required to avoid a dreaded “too many open files” error, and for the same reason we must kill the server and start it again with:
ulimit -n 10100 && ruby sikwamic.rbRunning the floodtest again, this time we were able to complete all 10k queries with a maximum concurrency of 494:

Not bad, but maximum concurrency isn’t achieved because the server starts responding after 5 seconds and it takes some time to create those 10k connections. So let’s ramp it to 2 minutes. At this time, sikwamic.rb looks like this:
We restart the server and run the same floodtest again. However, mongrel creates a thread per waiting connection and cannot handle more than 950 thread, thus the following error message:
Reaping 950 threads for slow workers because of 'max processors' Server overloaded with 950 processors (950 max). Dropping connection.That’s why the floodtest crashes near 1000 connections, just when it starts to be funny.
Time to use sinatra, thin and async
At this point, we want to go beyond Sikwamic limits. Sinatra default backend is thin which can handle asynchronous responses. Thanks to the async_sinatra gem it’s even easy. Let’s install it:
gem install async_sinatraLet’s modify our app.rb to use async_sinatra:
We’ve added the following config.ru:
With both files in current directory, we start thin this way:
ulimit -n 10100 && thin -p 9291 -e production -R config.ru startUsing your browser (same urls), you can check that the application is behaving like Sikwamic. Let’s flood this one:
ulimit -n 10000 && ./floodtest 10000 127.0.0.1 9291 /get_if_modified/user_55_name/johnThis time our ruby server doesn’t throw out errors, however the floodtest itself stops with a segmentation fault. We decided to separate the client running floodtest from the server running thin. On both, we allowed more ports and files to be opened at the same time:
sudo sysctl -w net.inet.ip.portrange.first=32768 sudo sysctl -w kern.maxfiles=65536 sudo sysctl -w kern.maxfilesperproc=32768Running the floodtest again,
-
Mar
01Installing ruby 1.9.2 and sinatra 1.0a
Posted in : ruby and sinatra
It would be sacriledge to use the old and dusty ruby 1.8.7 and stable as a dead cow sinatra version 0.9 for our experiments. Jump the wagon for cutting edge ruby 1.9.2 and sinatra 1.0a, and enjoy the speed improvements they both provide.
Install rvm
If you don’t use it yet, it’s probably time to install rvm. On Mac OS X, ruby is installed as part of the system, but you will need a compiler. The Developer Tools are on installation dvd provided with each Apple computer (usually the first thing to install on a fresh Mac OS X if you’re a developer), just remember the last version is always the prefered choice (free registration mandatory to download it from Apple). To install rvm using gem, open your terminal and type:
gem install rvm --source=http://rubygems.org/ rvm-installFollow the instructions, check the given lines have been added successfully in your ~/.profile (or ~/.zshrc if you’re using zsh) or copy/paste them manually. Then open a new tab.
To check that you’ve rvm type:
rvm listIt should show you the system ruby provided by Xcode and no other version yet.
Install ruby 1.9.2
Once you have rvm it’s easy. However, there’s a catch. The ruby version we want doesn’t appear in the list (at least at time of writing) if you do:
rvm list --allRuby 1.9.2 preview 1 has some serious issues, we expected at least preview 2. The fun part is that it’s still possible despite the fact that it’s not in the list, just do:
rvm install 1.9.2-headAfter a few minutes, you’ll have it installed. You still need to tell rvm that you want to switch to this version, just type:
rvm 1.9.2-headIt will only change used ruby version inside the current tab. It’s perfect if like us you just want to try it out. If however you want it to replace the system ruby as your default ruby, just type:
rvm 1.9.2-head --defaultCaveat
When running on Mac OS X 10.6 (Snow Leopard) on a 64bit architecture Ruby 1.9.2-head still generate a segfault intermittently (see: Non systematic segmentation fault with autoload rubyspec). If you have the problem, you can revert to ruby 1.9.1:
rvm install 1.9.1 rvm 1.9.1Install sinatra 1.0a
Sinatra 1.0 is coming and has numerous benefits such as template caching. Until final release, you can install version 1.0 alpha with:
gem install --pre sinatraConclusion
You should now be up and running with ruby 1.9.2 and sinatra 1.0 - and in my tests they proved to be pretty stable while giving a real speed boost. There’s no reason not to try them yourself unless you depend on a load of not-yet-compatible gems or plugins.