Archive for February, 2011

Erik DeBill has put two interesting benchmarks on his blog. The first one compares the performance of different Ruby implementations in Rails development mode while the second compares their performance in Rails boot time. If you haven’t read them yet, please do it now.

Benchmarking code is an important practice, but it can be misleading if you fail to understand the root causes that lead to the different results.

Performance in development mode

In the first blog post, it is guessed that the root case for having slow requests in development is because Rails eager loads all models and controllers for each request:

Now, what I’d really like is a way to avoid recompiling everything every time. If I could have Rails recompile just the model or controller I’m working on and skip all the others, that’d be grand. I’ve taken a couple stabs at it, but I haven’t succeeded yet.

This is wrong! Rails, in development, only loads the model and the controller you are using in that specific request. This is very easy to verify if you create a new application, scaffold two resources and add a puts self.name in their class definition. If you access one controller, it will only load the model explicitly referenced in that controller. Even the model associations try to be lazy in that aspect, always loading the minimum it can.

So you may ask, why Rails is getting so slow after adding more scaffolds?

It happens because Rails 3.0 includes all helpers by default in your ApplicationController. So at the beginning of each request, Rails needs to load all helpers. Loading a helper in development mode is slow because ActiveSupport::Dependencies needs to track which dependencies were added when a file is loaded. This tracking basically happens by checking which constants were added invoking Object.constants before and after the file was loaded. Tracking these constants take more than 50% of the time in the request, mainly because invoking Object.constants is slow.

In other words, the main reason for an implementation to perform better in the benchmarks showed in the blog post is if it can calculate Object.constants faster. Those results do not mean at all that an implementation is more suitable than other for Rails development. In order to have real results, we would need a real application that is not made of 1000 scaffold (or, in this case, 1000 helpers).

In any case, if the root cause is in loading all helpers, how can we make it better? There are a few things:

1) Obviously, the problem can be fixed by having less helper files. Since Rails scaffold automatically generates helper files, it is common that applications have a bunch of empty helpers. Get rid of them. If you prefer you can even turn off the automatic generation of helpers in scaffold by adding the following to your application configuration:

config.generators.helper = false

2) If you simply don’t want to include all helpers, there is a method called clear_helpers that you could invoke in your ApplicationController. This method won’t fix the problem because it is invoked too late, after all the helpers were already loaded. So you get the feature, but not the performance improvement.

3) Rails master (upcoming Rails 3.1) has a configuration option that allows you to effectively turn these helpers off getting both the feature and the performance improvement:

config.action_controller.include_all_helpers = false

Boot performance

The second blog post shows how Rails boot time performs in different implementations. Since it was not made explicit in which environment those benchmarks were executed, I will assume it happened on development.

At the end of the second blog post, it tries to associate the performance of booting Rails in development with the amount of code inside the app. However, when you boot an application in development, no model, controller or helper is loaded at all unless you explicitly access them in an initializer or in your routes file. Once again, you can check that by adding some puts to your classes.

So, you may ask one more time, what makes booting up so slow?

Rails 3 has a new router that can match paths very fast, but in order to do so, it needs to compile each route into a regular expression and that takes some time (although it could probably be made faster). And it is exactly the routes compilation that is slow on boot time. We can easily reproduce it by adding the following to our router:

Foo::Application.routes.draw do
  1000.times do |index|
    resources :"posts#{index}"
  end
end

This took 55 seconds on my machine using REE which is quite close to the value that he showed on his benchmark.

Again, benchmarking code is important, but more important is to correctly interpret the results. In his example, it is likely that most of Rails booting time is spent on compiling the routes and the benchmark just shows how good different Ruby implementations are in handling all these regular expressions.

Wrapping up

Much more interesting benchmarks for Rails boot time would actually be performed in production environment, which actually has to load all the code inside the app folder and compile the routes file. Regardless, developers starting new applications should always be skeptical about choosing a Ruby implementation based on other application’s benchmarks.

When starting out a new application, any Ruby implementation should suit you just fine unless you have a stronger constraint (like Java integration). Once your application starts to grow and you want to evaluate how well it performs in different implementations, you should do your own benchmarks and see how it goes. In any case, don’t jump into conclusions. If you need to investigate deeper, each implementation has its own sets of benchmarking and profiling tools that may help you really understand what is actually slow and how to improve it.

I also want to thank ruby-prof authors and maintainers, for such an amazing tool, and Yehuda Katz, who helped me profile a demo Rails application in order to write this detailed response.

And you? Have you done benchmarks in your applications and found any interesting data you would like to share?

Note: Devise 1.1.6 broke compatibility with Rails versions prior to 3.0.4, this has been fixed on Devise 1.1.7.

Devise 1.1.6 has just been released and it follows Rails 3.0.4 release. Rails 3.0.4 changes how CSRF works and adds a new method called handle_unverified_request that should be properly overridden by authentication frameworks. Devise 1.1.6 implements this method and others small security fixes.

If you have updated to Rails 3.0.4, you must update to Devise 1.1.6. Those using Devise 1.2.rc should use master for awhile, another RC is coming soon. For more information, check out the CHANGELOG.

Devise 1.0.10 was also released with the same fixes for Rails 2.3.11.

"You" by Hugo Barauna

Ei você! Que gosta de codar em Ruby e Rails e não fica sujando o código com remendos... (by @hugobarauna)

 

Estamos procurando por desenvolvedores Ruby/Rails que:

  • … querem trabalhar com um time ao invés de colegas;
  • … gostam de trabalhar com pessoas que praticam a melhoria contínua ao invés de pessoas conformadas com o status quo;
  • … são pragmáticos ao invés de xiitas cegos e teimosos;
  • … entendem que fazer código bem feito e testado gera impacto positivo para os clientes;
  • … têm os skills necessários para contribuir com o crescimento da PlataformaTec.

Além das características listadas acima, nós valorizamos muito os profissionais que tem prazer em realizar o seu trabalho. E para ajudar os candidatos a entenderem um pouco mais sobre o que fazemos aqui, vou listar algumas coisas que o nosso time de devs fez durante os últimos meses. O nosso time de devs…

  • … desenvolveu uma API com protocolo de segurança utlizando o conceito de chave pública;
  • … aperfeiçoou o algoritmo do Readability em Ruby para parsing de páginas web;
  • … trabalhou na integração do Git como repositório de templates utilizando Rails 3;
  • … aplicou coaching em Ruby e Rails;
  • … estudou sobre arquitetura RESTFul;
  • … discutiu implementações de código e testes com o José Valim;
  • … executou uma série de stress testings utilizando httperf e JMeter;
  • … participou de reuniões com clientes para entendimento de escopo;
  • … fez programação pareada durante o desenvolvimento de um aplicativo para Facebook com Rails 3;
  • … desenvolveu projetos open source (Devise, SimpleFormCapybara Zombie e Outpost);
  • … participou de reuniões com os gerentes de projetos e desenvolvedores de negócios para decidir os rumos dos projetos;
  • … escreveu no blog da PlataformaTec e recebeu mais de 1.000 pageviews num único dia;
  • … revisou, colaborou e leu o Crafting Rails Applications antes que todo mundo;
  • … participou do churrasco e reunião de “Kick-start 2011 PlataformaTec”;
  • … e por fim, jogou duas mesas de poker e perdeu uma para o @vinibaggio e outra para @hugobarauna ;)

Requisitos da vaga:

  1. Inglês intermediário
  2. Sólido background em programação orientada a objetos
  3. Experiência com desenvolvimento de aplicativos web em qualquer linguagem (pelo menos 1 ano)
  4. Experiência com Ruby e Rails (pelo menos 6 meses)
  5. OS Linux ou Mac
  6. Disponibilidade integral
  7. Local: São Paulo (nosso escritório)

 

Update (abr/2011):  este processo seletivo foi concluído. Obrigado a todos que participaram.

Here at PlataformaTec we like to use Capybara for acceptance tests. Recently we have discovered the custom selectors feature in Capybara and we would like to share with you how that feature helped us to improve our tests.

Sometimes we need to implement features that involves showing some ordered items to the user, like a ranking feature. The HTML for a feature like that could be:

<ol id="overall-ranking">
  <% @top_users.each do |user| %>
    <li><%= user.name %></li>
  <% end %>
</ol>

The acceptance tests for this ranking could be written as follows:

scenario "The user can see an overall ranking" do
  Factory(:user, :name => "Hugo",  :score => 5000)
  Factory(:user, :name => "Ozaki", :score => 3000)
  Factory(:user, :name => "João",  :score => 4000)
 
  visit overall_ranking_path
 
  within("#overall-ranking") do
    find(:xpath, './/li[1]').text.should match("Hugo")
    find(:xpath, './/li[2]').text.should match("João")
    find(:xpath, './/li[3]').text.should match("Ozaki")
  end
end

Generally, I don’t like to see those XPath selectors inside my acceptance tests. And sometimes it can get really ugly! So, in order to improve our tests, we can create a custom selector with Capybara as follows:

# spec/spec_helper.rb
 
RSpec.configure do |config|
 
  Capybara.add_selector(:li) do
    xpath { |num| ".//li[#{num}]" }
  end
 
end

After that, we can refactor our test as shown below:

scenario "The user can see an overall ranking" do
  Factory(:user, :name => "Hugo",  :score => 5000)
  Factory(:user, :name => "Ozaki", :score => 3000)
  Factory(:user, :name => "João",  :score => 4000)
 
  visit overall_ranking_path
 
  within("#overall-ranking") do
    find(:li, 1).text.should match("Hugo")
    find(:li, 2).text.should match("João")
    find(:li, 3).text.should match("Ozaki")
  end
end

If you wanna know more about Capybara’s custom selectors, check its README.

And you? Any tips about using Capybara or improving your acceptance/integration tests?

Outpost is me, scratching my own itch. For a while now, freelancing or working
for others, I work with pretty much all the stack in web development: from front-end
development with HTML and CSS up to the system administration.

And sometimes, I screw up. Sometimes, stuff go wrong. Maybe that Sphinx
isn’t getting indexed or even running after an unsuccessful deploy. Maybe that
background job isn’t being run, and I forget to verify it.

I know there are excellent tools that already solve this problem. But I wanted an
easy way I would be able to code my own monitoring rules, using one of the
languages I like the most. And also, the fantastic Aaron Patterson (or
tenderlove), one of the most prominent faces of the Ruby community once told in
a Q&A at RMU: it should be fun!

And thus the Outpost idea was born. Outpost is a framework so I can easily
implement Ruby code that query the current state of any service I want. I can
also write code that can go into the database and do a SELECT on a table (a
jobs table, for example) to check if everything’s fine.

What is Outpost?

Outpost is basically a DSL where you can describe rules to monitor your
service, application, server, whatever. Below is a very simple example:

require 'outpost'
require 'outpost/scouts'
 
class MyWebPageOutpost < Outpost::DSL
  using Outpost::Scouts::Http => "web page" do
    options :host => 'localhost', :port => 3000
    report :up, :response_code => 200
    report :down, :response_body => {:match => /Ops/}
  end
end

In this example, we are monitoring (using what I’ve called ‘Scouts’) HTTP
communication to localhost:3000. It will report that the system is
up if the response code is 200 (HTTP OK) and report that it is down if the
response body contains the word “Ops”, by matching a Regular Expression to it.

There is still a lot of work to be done, but I feel it is ready for a very first
release. There are only two Scouts today: HTTP and Ping, but it’s so
easy to write new ones that I will be releasing a few more in the next
days.

Also, there are only three expectation matchers: response time, response body
and response code. I believe they are able to cover most of the cases, but it is also
very easy to write new expectations.

Below is another example of an Outpost, based on the integration tests:

require 'outpost'
require 'outpost/scouts'
 
class ExamplePingAndHttp < Outpost::DSL
 using Outpost::Scouts::Http => 'master http server' do
   options :host => 'localhost', :port => 9595, :path => '/'
   report :up, :response_body => {:match => /Up/}
 end
 
 using Outpost::Scouts::Ping => 'load balancer' do
   options :host => 'localhost'
   report :up, :response_time => {:less_than => 500}
 end
end

For more details, please check the project’s README, on GitHub.

The future

My plans for Outpost are: SSH support, so you can connect to servers that do
not expose their services to the outer world, a very simple web dashboard to show
the results, better error reporting (for now you can only know which Scout
failed, but not why), a script that automatically generates Outposts for Rails
apps, and the list goes on!

Crazy idea: wouldn’t it be nice to have a script that reads Outposts and
tells if a machine is ready to be in production? TDD for machines? Woot!

Thanks

This project was my personal project for RMU (Ruby Mendicant University) and
so I would like to thank all the people involved for the amazing experience and
reviews on the project. Also thanks to my friends for listening me nagging
about this project for a while. Finally, thanks to Tomás D’Stefano, who
did some work on my previous attempt to write Outpost, your work won’t be
thrown away.

Closing

Please share your thoughts about this project. I would love to know your opinion!