1. Jun
    28

    NGinx Useful Tips

    Posted in : nginx and sysadmin

    During an epic debugging session with an NGinx configuration for a project, I discovered some useful, but not so common (at least to me) configuration.

    Debug

    NGinx do not provide so much help (by default) when it comes to debugging internal redirect, proxying and other rewrite rules. But it comes with a handy debug module which allows you to get a lot more info.

    You have to enable it at compile time:

    ./configure --with-debug

    And then in your configuration you can set:

    http {
      # At the http level activate debug log for eveything
      error_log /path/to/my/detailed_error_log debug;
    
      server {
        # At the server level activate debug log only for this server
        error_log /path/to/my/detailed_error_log debug;
      }
    
      server {
        # At the server level without the debug keywords it disable debug for this server
        error_log /path/to/my/error_log;
      }
    }

    You can even debug only some connections:

    error_log /path/to/my/detailed_error_log debug;
    events {
        debug_connection   10.0.0.1;
        debug_connection   10.0.1.0/24;
    }

    Source: NGinx Debugging Log

    Proxying

    NGinx is well known for its proxy/reverse-proxy/caching-proxy capabilities, but you’d better know how some things works to not waste your time on some odd behaviors.

    When proxying to remote host by URLs, be aware that NGinx use its own internal resolver for DNS name. This means in some cases it can’t resolve domains unless you specify which DNS to use.

    Let’s take an example:

    server {
      # Let's match everything which starts with /remote_download/
      location ~* ^/remote_download/(.*) {
        # but only when coming from internal request (proxy, rewrite, ...)
        internal;
    
        # Set the URI using the matched location
        set $remote_download_uri $1;
    
        # Set the host to use in request proxied (useful if remote is using vhost
        # but you're using its IP address to reach it in the proxy_pass config)
        set $remote_download_host download.mydomain.tld;
    
        # Set the url we want to proxy to
        # Using IP address of server, be sure to set the $remote_download_host
        set $remote_download_url https://10.0.0.1/$remote_download_uri;
        # Or using the full domain
        # set $remote_download_url https://$remote_download_host/$remote_download_uri;
    
        # Set Host header for vhost to work
        proxy_set_header Host $download_host;
    
        # This clears the Authorization
        proxy_set_header Authorization '';
        # If your remote server needs some auth you can set it there
        # Basic auth would be something like
        # proxy_set_header Authorization 'Basic kjslkjsalkdjaslasdoiejldfkj=';
    
        # Disable local file caching, when serving file
        proxy_max_temp_file_size 0;
    
        # Finally send query to remote and response back to client
        proxy_pass $download_url;
      }
    
      try_files $uri @fallback;
    
      location @fallback {
        proxy_pass http://my_backend;
      }
    }

    Example adapted from Nginx-Fu: X-Accel-Redirect From Remote Servers

    This example is fully working because we used an IP address for $remote_download_url, but if we were using the domain (eg: download.mydomain.tld), any request would fail with a 502 Bad Gateway error.

    This is due to the way NGinx default resolver works. It’s smart enough to resolve the domains in proxy_pass directives as long as they are statics (it can get them at boot time) and they are in /etc/hosts. But as we are constructing the URL here, it does not try to resolve it. Fortunately you can specify which DNS server it should use in such cases by setting:

      http {
        # Globally
        resolver 127.0.0.1; # Local DNS
    
        server {
          # By server
          resolver 8.8.8.8; # Google DNS
    
          location /demo {
            # Or even at location level
            resolver 208.67.222.222; # OpenDNS
          }
        }
      }

    Comments

  2. Jun
    14

    Benchmark ruby versus node.js on Heroku

    Posted in : ruby, node, heroku, sinatra, mongo_mapper, unicorn, express, mongoose, and cluster

    I’ve been playing a lot with node lately thanks to this PeepCode screencast and since Heroku released their new Celadon Cedar stack I’ve been wanting to benchmark ruby versus node.js for a restful API which I need to build.

    At first I tried to compare bare node.js against eventmachine_httpserver. It did quickly became obvious that this kind of micro-benchmark wasn’t going to be very helpful in deciding which one to choose.

    Building the API was going to be messy unless I did start using something like sinatra or express.

    Methodology

    I did setup 2 similar repositories on github:

    https://github.com/JosephHalter/heroku_ruby_bench

    This repository is using sinatra, mongo_mapper, yajl and unicorn.

    https://github.com/JosephHalter/heroku_node_bench

    This repository is using express, mongoose and cluster.

    I deployed both on Heroku, did setup a free mongohq account for both and run a few tests with various number of workers. All tests have been done from a datacenter in Paris using the following command:

    ab -k -t 10 -c 1000 http://evening-robot-961.herokuapp.com/restaurant/4df550c5c3aaaa0100000001
    ab -k -t 10 -c 1000 http://cold-mist-128.herokuapp.com/restaurant/4df55a0fab9e270007000001

    evening-robot-961 is running on node 0.4.7 and cold-mist-128 is running on ruby 1.9.2

    Checklist

    For those asking:

    • yes, I’ve waited enough time between each heroku scale
    • yes, I’ve retried each test a crazy number of times

    Heating

    Here are the raw results for a concurrency of 100, just to ensure everything works as expected before starting the real test:

                             | completed | failed |
    express/node             | 3358      | 0      |
    express/cluser/3 workers | 7473      | 0      |
    sinatra/thin             | 1649      | 0      |
    unicorn/4 workers        | 6080      | 0      |

    Results

    Here are the raw results for a concurrency of 1000:

                               | completed | failed |
    node/1 dyno                | 40524     | 27865  |
    node/5 dynos               | 20755     | 11575  |
    node/10 dynos              | 36953     | 9866   |
    node/20 dynos              | 34724     | 7486   |
    node/40 dynos              | 33919     | 8863   |
    node/60 dynos              | 32218     | 8984   |
    cluster/1 worker           | 21307     | 13193  |
    cluster/2 workers          | 41679     | 18904  |
    cluster/3 workers          | 40700     | 12864  |
    cluster/3 workers/5 dynos  | 37292     |  8360  |
    cluster/3 workers/10 dynos | 19787     | 10870  |
    cluster/3 workers/15 dynos | 35894     |  7119  |
    cluster/3 workers/20 dynos | 35871     |  9807  |
    cluster/3 workers/40 dynos | 32727     |  8371  |
    cluster/4 workers          | 22262     | 14738  |
    thin/1 dyno                | 38813     | 36769  |
    thin/5 dynos               | 40885     | 35178  |
    thin/10 dynos              | 42141     | 29082  |
    thin/40 dynos              | 33014     |  9732  |
    thin/60 dynos              | 31392     |  8747  |
    unicorn/1 worker           | 41032     | 39498  |
    unicorn/2 workers          | 24991     | 22152  |
    unicorn/3 workers          | 22601     | 16319  |
    unicorn/3 workers/5 dynos  | 20386     | 11012  |
    unicorn/4 workers          | 44127     | 37607  |
    unicorn/4 workers/5 dynos  | 39591     | 13426  |
    unicorn/4 workers/10 dynos | 35672     |  9511  |
    unicorn/4 workers/15 dynos | 35925     |  7997  |
    unicorn/4 workers/20 dynos | 34611     |  8131  |
    unicorn/4 workers/40 dynos | 18873     | 11125  |
    unicorn/8 workers          | 45904     | 39819  |

    A few charts to make it easier to read:

    express

    express with cluster

    express with cluster/3 workers

    sinatra with thin

    sinatra with unicorn

    sinatra with unicorn/4 workers

    Conclusions

    Using either ruby or node I could easily get more than 2000req/s. I think both are viable alternatives. With only 1 dyno however, you’ll start to have failed connection when faced with massive concurrency because the backlog is full. Increasing the number dynos doesn’t magically allow you to handle more requests per second, however it can decrease the number of failed connections. Heroku pricing is linear to the number of dynos you scale to, however your throughput does only only improve marginally. It could be that I’m benchmarking from only 1 server but I’ve seen almost no difference between having 40 dynos or 60. You’ll see one when you receive the bill so be cautious, especially if you use an auto-scale tool.

    Apart from that, we’re seeing a huge improvement when using unicorn with 4 workers instead of thin which was the only possibility before Celadon Cedar so a big thank you to Heroku who did make this possible. The same applies to node with cluster, you can do more with 15 dynos running cluster with 3 workers than with 60 dynos of node alone (for a quarter of the price!). Special thanks to TJ Holowaychuk who did help us to fixing a stupid issue which prevented us from using cluster on Heroku.

    Comments

  3. May
    23

    Link to static assets on Rails 3.1

    Posted in : rails, sass, assets, and sprockets

    Having just migrated an application to Rails 3.1, we discovered that the following line is not valid anymore:

    = javascript_include_tag "rightjs", "application", :cache => "all"

    The easiest solution is moving all your javascripts in app/assets/javascripts/ then create a file all.js

    // require "rightjs"
    // require "application"

    Or we can also just add the require in application.js itself. Soon we had to do the same for our stylesheets: move them to app/assets/stylesheets.

    Also, the default path where image_tag will look for our images is now app/assets/images and no more public/images. The public folder does now shrink to only a handful of files.

    The only remaining issue should now be with images (and fonts) referenced in the stylesheets as ../images doesn’t work anymore. The first thing we tried was changing:

    url(../images/myimage.png)

    To:

    url(/assets/myimage.png)

    Ok now it works, but is only suitable for dev because it does hit our application to serve the static files. Hopefully, Rails has a new rake task to precompile all assets to the public/assets folder:

    bundle exec rake assets:precompile RAILS_ENV=production

    Don’t forget to specify the environment otherwise the css won’t be minified properly. Also our files will be renamed from:

    application.css

    to:

    application-ab5f...(more letters and digits).css

    It combines the original filename with a hash, a MD5 digest of the file content after evalutation. This is to ensure proper cache reloading - the old way with query string didn’t work with some proxies. In order to avoid serving static assets in production, we have to reference these images directly, with the hash in filename. So intead of:

    url(/assets/myimage.png)

    What we want is:

    url(/assets/myimage-df02(more letters and digits).png)

    Of course, doing it manually like suggested here would be tedious. The solution is to rename our stylesheet to application.css.erb (or application.css.scss.erb) and type:

    url(<%= asset_path "myimage.png" %>)

    Don’t bother trying to write:

    url(<%= asset_path "images/myimage.png" %>)

    Even if the image is actually in app/assets/images because it won’t generate the right link. We did also moved our fonts to app/assets/fonts, it works no differently that for images. Reminder: if you forgot to specify the environment (RAILS_ENV=production) when running the assets:precompile tasks the links won’t include the necessary hash in filename.

    Comments

  4. Apr
    29

    Generate checksum of file with openssl

    Posted in : openssl, chef, and checksum

    Here’s how to generate checksums of file using OpenSSL:

    # SHA256, used in chef cookbooks
    openssl dgst -sha256 path/to/myfile
    
    # MD5
    openssl dgst -md5 path/to/myfile

    Comments

  5. Apr
    29

    Compile Psych support in Ruby 1.9.2p180

    Posted in : rvm, ruby, 1.9.2, homebrew, and yaml

    Ruby 1.9.2 comes with a new YAML parser: Psych from Aaron Patterson (aka Tenderlove). But it’s compiled only if you have installed libyaml in some of ruby’s mkmf default search location.

    Here’s how to make sure ruby will compile it:

    You have homebrew installed in /usr/local:

    brew install libyaml
    rvm install ruby-1.9.2-p180

    You have homebrew installed in ~/.homebrew:

    brew install libyaml
    rvm install ruby-1.9.2-p180 -C --with-libyaml-include=$HOME/.homebrew/include,--with-libyaml-lib=$HOME/.homebrew/lib

    I tried to use the --with-libyaml-dir=$HOME/.homebrew/ shortcut, but it don’t work. So don’t loose your time.

    Comments

  6. Sep
    26

    Install Mongrel2 on Mac

    Posted in : mac, mongrel2, server, homebrew, and zeromq

    Want to play with Zed Shaw’s Mongrel2 on your mac? Using Homebrew in your home and not in /usr/local ?

    Here’s how to get the ØMQ Homebrew’s install picked up by Mongrel2’s Makefile:

    # install zeromq via homebrew
    > brew install zeromq
    
    # download and install mongrel2
    wget http://mongrel2.org/static/downloads/mongrel2-1.2.tar.bz2
    tar xvjf mongrel2-1.2.tar.bz2
    cd mongrel2-1.2
    
    # to install in your home
    PREFIX=$HOME OPTFLAGS="-I`brew --prefix`/include -L`brew --prefix`/lib" make all install
    
    # to install in the default location (/usr/local)
    sudo OPTFLAGS="-I`brew --prefix`/include -L`brew --prefix`/lib" make all install

    Comments

  7. Aug
    15

    Mongomatic

    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.

    Mongomatic versus Mongoid

    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.

    Comments

  8. Aug
    02

    Compile 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.0

    Use the provided script to find rbconfig.rb file:

    ruby find_rbconfig.rb

    And 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

    Comments

  9. Jul
    24

    Benchmark 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:

    Mongo versus Sqlite

    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:

    MongoMapper versus Mongoid

    And here’s more ODM bench:

    MongoModel versus Candy versus MongoRecord versus MongoDoc

    The source for all these benchmarks is freely available on github.

    Comments

  10. Jul
    20

    Add 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
    end

    Please note that using Rails::Application is deprecated and that you should replace it with MyApplicationName::Application everywhere instead. Happy hacking ;)

    Comments

  11. Jul
    15

    Rack::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 string

    This 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=nil

    somewhere 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
    end

    Problem 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!

    Comments

  12. Apr
    24

    Cucumber 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 0m1s

    This means our cucumber startup time dropped from nearly 2 minutes to … instant.

    Now, what’s your results ?

    Comments

  13. Apr
    09

    Mirror 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.

    Comments

  14. Mar
    05

    CSS3 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 nokogiri

    Try it in irb

    Open a terminal and start irb

    irb

    Then 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.

    Comments

  15. Mar
    02

    Easy 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 redis

    You should then be able to start the redis server with something like this:

    redis-server `brew --prefix`/etc/redis.conf

    You 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 redis

    If 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 git

    Then clone Sikwamic git repository:

    git clone git://github.com/dorkalev/Sikwamic.git
    cd Sikwamic

    Sikwamic requires mongrel so you’ll have to install the gem:

    gem install mongrel --source http://gems.rubyinstaller.org

    Then run Sikwamic:

    ruby sikwamic.rb

    Open your browser.

    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/john

    Simple: 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/john

    If 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/john

    This 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/john

    This 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/john

    Unluckily, 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/john

    Not 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 libevent

    Then 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.c

    Compile 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.c

    To show usage, run:

    ./floodtest --help

    Good, so let’s try it against Sikwamic:

    ulimit -n 10100 && ./floodtest 10000 127.0.0.1 9291 /get_if_modified/user_55_name/john

    ulimit 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.rb

    Running the floodtest again, this time we were able to complete all 10k queries with a maximum concurrency of 494:

    Chart

    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_sinatra

    Let’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 start

    Using 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/john

    This 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=32768

    Running the floodtest again,

    Comments

  16. Mar
    01

    Installing 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-install

    Follow 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 list

    It 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 --all

    Ruby 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-head

    After 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-head

    It 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 --default

    Caveat

    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.1

    Install 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 sinatra

    Conclusion

    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.

    Comments