Getting Started with Unicorn

Tue Feb 09 00:00:00 -0800 2010

I’ve been doing rolling deploys with a Mongrel cluster for over half a year now, and it works. Most of the time. Unless a Mongrel takes too long to shut down or something. Eh.

But then we all saw GitHub’s switch to Unicorn. I thought it sounded very cool, but maybe too much work to bother switching.

Oops.

Have a couple minutes? Good. Here you go …

Getting Started

Hit up the gem repository and gem install unicorn. It needs to compile, but that’s no big deal.

Now, shut down your script/server. Just go ahead and close that. Ready? Okay, now run unicorn_rails -p 3000.

Done. Good stuff. Was it good for you too?

Is It Different in Production?

Well sure. Let’s play around a little bit. It’s super simple to run Unicorn with a bunch of workers and send it commands just like you would in production.

First, create a basic config/unicorn.rb.

worker_processes 2
preload_app true
timeout 30
listen 3000

after_fork do |server, worker|
  ActiveRecord::Base.establish_connection
end

Want to know what all of that does? Check the docs, or the official example, or the Github sample. Go ahead, I’ll be here when you get back.

Now start your Unicorn server, except this time in the background and with this config file:

$ unicorn_rails -c config/unicorn.rb -D

$ pgrep -lf unicorn_rails
12113 unicorn_rails master -c config/unicorn.rb -D
12118 unicorn_rails worker[0] -c config/unicorn.rb -D
12119 unicorn_rails worker[1] -c config/unicorn.rb -D

$ cat tmp/pids/unicorn.pid
12113

Next, we’re going to send it some commands. You do this by sending signals. You’ve used the kill command? Yeah, well, it doesn’t always kill things. Watch this:

$ kill -s TTOU 12113
$ pgrep -lf unicorn_rails
12113 unicorn_rails master -c config/unicorn.rb -D
12118 unicorn_rails worker[0] -c config/unicorn.rb -D

We just dynamically removed a worker from the pool. Neat, huh? Let’s add it back, plus one:

$ kill -s TTIN 12113
$ kill -s TTIN 12113
$ pgrep -lf unicorn_rails
12113 unicorn_rails master -c config/unicorn.rb -D
12118 unicorn_rails worker[0] -c config/unicorn.rb -D
12136 unicorn_rails worker[1] -c config/unicorn.rb -D
12137 unicorn_rails worker[2] -c config/unicorn.rb -D

Where’s My Seamless Deploy!

Nice. But I know what you’re really here for. You want to know what it’s like to hot-swap Unicorn for seamless deploys? First, add this to your config/unicorn.rb:

before_fork do |server, worker|
  old_pid = RAILS_ROOT + '/tmp/pids/unicorn.pid.oldbin'
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

And for the win. Notice that I pgrep`d fast enough and caught it in transition.

$ kill -s USR2 12113

$ pgrep -lf unicorn_rails
12113 unicorn_rails master (old) -c config/unicorn.rb -D
12118 unicorn_rails worker[0] -c config/unicorn.rb -D
12136 unicorn_rails worker[1] -c config/unicorn.rb -D
12137 unicorn_rails worker[2] -c config/unicorn.rb -D
12239 /usr/bin/ruby1.8 /usr/bin/unicorn_rails -c config/unicorn-dev.rb -D

$ pgrep -lf unicorn_rails
12239 unicorn_rails master -c config/unicorn-dev.rb -D
12245 unicorn_rails worker[0] -c config/unicorn-dev.rb -D
12246 unicorn_rails worker[1] -c config/unicorn-dev.rb -D

Done. Good stuff. Was it good for you, too?

International Mailing Addresses

Wed Jul 15 00:00:00 -0700 2009

Wow, is it complicated to format mailing address for international delivery!

Let’s do something about it.

I’ve started a library called Snail based on quality resources like Frank’s Compulsive Guide to Postal Addresses and the USPS International Mailing Manual. But it’s far from complete. So far it’s set up to format addresses from the United States to many other countries. That fills most of my needs at the moment. But what I’d love to see is:

  • Support for other origin countries
  • Support for more destination countries
  • Validation tools based on each destination country. Just knowing which fields are required in which countries would be a great start!
  • Form helpers to generate some of the best-practice address forms.

So! If this scratches an itch, then please consider helping out wherever you can.

Seamless Deploys with Ec2 on Rails 0.9.9.1

Mon Jun 01 00:00:00 -0700 2009
I've been using a simple Capistrano recipe to deploy fixes to a production environment, and I figured hey, maybe other people want to do the same. I know there are other scripts out there, but this is particular to Ec2 on Rails and is tested against version 0.9.9.1. Also, I've created a /stat page that responds with a 200 when the site is running. The recipe uses that url, but any other would work as well.
namespace :deploy do
  desc "Hot deploy, only usable w/o migrations"
  task :hot do
    update
    reroll
  end

  desc "Rolling restart for Mongrels"
  task :reroll, :roles => :app_admin do
    (8000..8005).each do |port|
      sudo "monit -g app unmonitor mongrel_#{port}"
      sudo "/usr/local/ec2onrails/bin/mongrel_stop --only #{port}"
      sudo "/usr/local/ec2onrails/bin/mongrel_start --only #{port}"
      sudo "wget --no-verbose localhost:#{port}/stat && rm -f stat" # this should block until the mongrel has booted
      sudo "monit -g app monitor mongrel_#{port}"
    end
  end
end