Search code examples
ruby-on-rails-3unicorn

Why unicorn.pid is updated but the processes does not restart?


I just switched to deploy my Rails project on unicorn. In my capistrano recipe deploy:restart is

task :restart, :except => { :no_release => true } do
    run "cd #{shared_path}/pids && kill -s USR2 `cat unicorn.pid`"
end

But from time to time I found that, sometimes the deploy procedure finished with success, but although the unicorn.pid is updated, the unicorn processes remains still.

For example, I deployed at Dec.15th, and I monitored that unicorn.pid is updated, but if I run ps -ef | grep unicorn, I could see that the unicorn process is still started at the time of my last deployment, so it refers to the last release folder which causes trouble.

Why is that?

The following is my unicorn.rb file:

env = ENV["RAILS_ENV"]

case env
when 'pre', 'production'
  @app_path = '/home/deployer/deploy/myproject'
end

if env == 'pre' || env == 'production'
  user 'deployer', 'staff'
  shared_path = "#{@app_path}/shared"
  stderr_path "#{shared_path}/log/unicorn.stderr.log"
  stdout_path "#{shared_path}/log/unicorn.stdout.log"

  if env == 'production'
    worker_processes 6
  else
    worker_processes 2
  end

  working_directory "#{@app_path}/current" # available in 0.94.0+

  listen "/tmp/myproject.sock", :backlog => 64


  timeout 30

  pid "#{shared_path}/pids/unicorn.pid"


  preload_app true
  GC.respond_to?(:copy_on_write_friendly=) and
  GC.copy_on_write_friendly = true

  before_fork do |server, worker|

    defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!

    old_pid = "#{shared_path}/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

  after_fork do |server, worker|
    defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
  end
end

Solution

  • I'm not sure why this is the case, but personally I don't kill the Unicorn PID when deploying via Capistrano.

    Rather, I start unicorn via the god gem, and configure the god file so it restarts unicorn whenever a file has been touched.

    When I deploy via Capistrano, I then touch the file after every deploy, forcing the unicorn process to restart

    namespace :deploy do
      desc "Touch restart.txt, which will restart all God processes"
      task :restart do
        run "touch #{ current_path }/tmp/restart.txt"
    end
    

    You need to add this module to the .god file too:

    module God
      module Conditions
        class RestartFileTouched < PollCondition
          attr_accessor :restart_file
          def initialize
            super
          end
    
          def process_start_time
            Time.parse(`ps -o lstart  -p #{self.watch.pid}`)
          end
    
          def restart_file_modification_time
            File.mtime(self.restart_file)
          end
    
          def valid?
            valid = true
            valid &= complain("Attribute 'restart_file' must be specified", self) if self.restart_file.nil?
            valid
          end
    
          def test
            process_start_time < restart_file_modification_time
          end
        end
      end
    end