Capistrano is dead - use Mina

written by Raphael

Up until lately I was happy using Capistrano for my deployments. But then I noticed the following:

Capistrano was utilizing 70 % of a physical CPU core when deploying on the go.

I'm on a dual core system so this impacts my Mac Book Pros overall performance, battery life and it kept me waiting for a long long time. My iPhone was at edge speed so I figured Capistrano spent most of the time waiting for network I\O - busy waiting that is.

So I profiled Capistrano using perftools.rb. Capistrano spent about 50% on Compat.io_select over each deployment run. That's 50 % of a total of 40 seconds it took to deploy & restart my application server. The percentage spent waiting on Compact.io_select decreases to about 10% as the network speed increases to regular DSL levels.

The interesting part is that this is just CPU time. When profiling wall time Compact.io_select is at 9%, while Object#process_iteration is at 84%. Object#process_iteration is a wrapper Capistrano introduces around Kernel#select which 'should' not do busy waiting. But Capistrano kept my CPU busy anyways.

I converted my Capistrano configuration to Mina to see if it was any better.

Contrary to Capistrano Mina creates a Bash script based on your deployment configuration and executes the script on the server side using a single SSH connection. In theory this should be way faster than Capistrano which creates multiple SSH connections. It should have a far smaller CPU footprint as well given that Ruby is only used to assemble a Bash script.

Timing both setups led to the following results:

$ time mina deploy
real  0m23.999s
user  0m0.188s
sys  0m0.054s
$ time cap deploy
real  0m40.561s
user  0m0.897s
sys  0m0.191s

As you can see Mina deployments take about half the time it takes Capistrano to deploy and it didn't use much CPU.

For comparison only, here's a stripped-down version of my Capistrano configuration

require "rvm/capistrano"
require 'bundler/capistrano'
set :application, "my-app"

namespace :deploy do
  task :start, :roles => :app, :except => { :no_release => true } do
    run "sudo supervisorctl start #{application}:*"
  end

  task :stop, :roles => :app, :except => { :no_release => true } do
    run "sudo supervisorctl stop #{application}:*"
  end

  task :restart, :roles => :app, :except => { :no_release => true } do
    run "sudo supervisorctl restart #{application}:*"
  end
end

as well as Minas:

require 'mina/bundler'
require 'mina/rails'
require 'mina/git'

task :rvm do
  queue "PATH=$PATH:$HOME/.rvm/bin"
  queue "source $HOME/.rvm/scripts/rvm"
  queue "#{deploy_to}/.rvm/bin/rvm rvmrc load"
end

desc "Deploys the current version to the server."
task :deploy do
  invoke :rvm
  deploy do
    invoke :'git:clone'
    invoke :'bundle:install'

    to :launch do
      queue 'sudo supervisorctl restart my-app:*'
    end
  end
end

Conclusion

Mina is faster than capistrano, uses less CPU and less network bandwidth. Since it generates a bash script for deployment it's also much more transparent what's going on and how to change it to act the way you want it to. Verdict:

Use Mina already!


Do it yourself calendar helper for Ruby

written by Raphael

I needed the possibility to display any given month of a year, along with some additional informations in Rails. I wanted to place the additional informations within the calendar in a way that no available plugin allowed me to, so I rolled my own.

sample calendar

In this blog post I want to share my approach to build a simple calendar helper for Ruby. The following code does not use any functionality provided by Rails, so you can use it anywhere you want.

1. some thoughts on the first row to display

I need my calendar to always start at Monday. The first day of a given month rarely is a Monday so you'll need to display some days of the prior month as well.

first row

The days that need to be displayed prior to the first day in a month can be calculated if you enumerate each day of the week; e.g. Sunday equals 0, Monday 1, Tuesday 2, and so forth. That's just what Ruby does.

first row

Now, all you need to do is to determine which day of the week the first of a given month is. This will give you enough informations to calculate how many days need to be displayed prior:

year = 2012
month = 7

first_day_of_month = Date.new(year, month, 1)
date_of_first_monday = (first_day_of_month.sunday? ? -6 : (first_day_of_month.wday > 1 ? -first_day_of_month.wday : 1))

Now we can determine the date of the first monday in the first row

first_monday_in_calendar = Date.new(year, month - 1, date_of_first_monday)
# => yields 2012-06-25

and calculate the remaining row as well:

7.times.map { |wday| first_monday_in_calendar.next_day(wday) }

2. calculate how many rows to display

In most cases you'll need to display between four and six rows, depending on the number of days in a month as well as the weekday on which a given month starts. So let's calculate that:

last_day_in_month = first_day_of_month.next_month.prev_day
# => yields 2012-07-31

days_in_month = last_day_in_month.yday - first_day_of_month.yday
# => yields 30

days_to_display = days_in_month + days_to_display_prior.abs
# => yields 36

weeks_to_display = weeks_to_display = (days_to_display.to_f / 7).ceil
# => yields 6

3. calculate all dates to be displayed

weeks_to_display.times.map { |week|
  7.times.map { |wday|
    first_monday_in_calendar.next_day(week * 7 + wday)
  }
}

That's it. Now you can wrap this logic into a presenter or extended it.


A usecase for git commit hooks

written by Raphael

I've been working a lot with XCode lately, developing an iPad app for a customer at weluse. Sometimes I just dropped assets from my dropbox into XCode, which lead to XCode adding the files using my absolute file directory path, rather than the path relative to the project.

Now this wasn't a problem until our customer started building the project which obviously failed.

The solution which worked great for me was a git pre-commit hook to ensure that no files have been checked in containing absolute paths, in this case: any file which path starts with $HOME.

So I came up with this simple pre-commit hook script which causes git to stop commits for just this use case:


#!/bin/sh

# Redirect output to stderr.
exec 1>&2

search_for_absolute_paths() {
  git grep "$HOME"
}

if [ "" != "$(search_for_absolute_paths)" ]
then
  echo "commit contains paths relative to the current user:"
  echo ""
  echo $(search_for_absolute_paths)
  exit 1;
else
  exit 0;
fi

The installation is straight forward: place the content under


/path/to/your/project/.git/hooks

in a file named pre-commit, chmod it to 755 and voila: git will refuse any commit containing your $HOME environment variables value.

Now this wouldn't have been necessary if I were a little more cautious when working with my assets, but it feels good to be on the safe side anyways.