2014-02-08

In my previous post I covered troubleshooting unicorn processes. This time I'll share one more technique that can make your devops life easier.

Default unicorn worker process names suck

When you look at server process list using ps auxf or something similar, unicorn looks like this:

... unicorn master -c /etc/app/config.rb -E production -D
...  \_ unicorn worker[0] -c /etc/app/config.rb -E production -D
...  \_ unicorn worker[1] -c /etc/app/config.rb -E production -D
...  \_ unicorn worker[2] -c /etc/app/config.rb -E production -D

Not very useful, and quite redundant - your workers inherit the argument line from master process. There's no way to tell what they're doing at given moment.

Overriding unicorn worker process names in Ruby on Rails app

How about something like this?

... unicorn master -c /etc/app/config.rb -E production -D
...  \_ unicorn worker[0] 66.249.78.123: last status: 200. Waiting for req.
...  \_ unicorn worker[1] 91.9.93.234: GET /items/3134
...  \_ unicorn worker[2] 62.227.53.32: POST /auth/login

It shows the IP of your user along with current request method and path. And in case worker is slacking, last user IP and response status. Now, let's make ourselves a piece of Rack Middleware that will do the above.

Create /app/middlewares/set_unicorn_procline.rb:

class SetUnicornProcline
  def initialize(app)
    @app = app
  end

  def call(env)
    user_ip = env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR']
    request_info = "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']}"
    worker_line = $0.split(']')[0] + "] #{user_ip}"
    set_procline "#{worker_line}: #{request_info}"[0..200]
    status, headers, body = @app.call(env)
    set_procline "#{worker_line}: last status: #{status}. Waiting for req."
    [status, headers, body]
  end

  def set_procline(value)
    $0 = sanitize_utf8(value)
  end

  def sanitize_utf8(string)
    return string.force_encoding('utf-8') if string.valid_encoding?
    string.chars.select(&:valid_encoding?).join.force_encoding('utf-8')
  end
end

Then enabled it in config/application.rb:

...
module <YourApp>
  class Application < Rails::Application
    ...
    require File.join("#{Rails.root}/app/middlewares/set_unicorn_procline.rb")
    config.middleware.use SetUnicornProcline
  end
end

It should work with Ruby >= 1.9.3 and Rails >= 3.2.x. The middleware also takes care of broken encoding that would otherwise cause $0.split to crash on next request after malicious REQUEST_PATH was provided.

Now run watch 'ps auxf | grep unicorn' and enjoy the show.

Ruby on Rails