delayed_jobのworker数を変更してrestartすると前に起動していたプロセスが消えない
[現象]Capistranoでdeployする時にdelayed_jobのワーカー数を変更すると古いワーカーが残ってしまう
最初にワーカーを1個にしてdeployしておいて、処理数が足りなくなってワーカーを2個に増やした場合、
delayed_job.0 (新)
delayed_job.1 (新)
delayed_job (古)
という状態になる。
ちなみにワーカーを3個から2個に減らすと
delayed_job.0 (新)
delayed_job.1 (新)
delayed_job.2 (古)
となる。
[原因]number_of_workersオプションの挙動
number_of_workersオプション無しで実行する
delayed_jobというプロセスが起動する
$ bundle exec bin/delayed_job start
delayed_job: process with pid 7665 started.
$ ps aux | grep [d]elayed_job
ubuntu    7665 32.9  7.9 731312 322824 ?       Sl   15:42  11:41 delayed_job
number_of_workersオプションありで実行する
suffixが付く
$ bundle exec bin/delayed_job --number_of_workers 2 start
Use "rmagick" instead
delayed_job.0: process with pid 7881 started.
delayed_job.1: process with pid 7887 started.
$ ps aux | grep [d]elayed_job
ubuntu    7881 33.9  5.4 564336 220212 ?       Sl   16:19   0:04 delayed_job.0                                   
ubuntu    7887 31.4  5.4 564692 220324 ?       Sl   16:19   0:04 delayed_job.1
オプションありで実行したあとに、オプション無しでrestartしてみる
suffixが付いているプロセスは生きたままで、新たにdelayed_jobというワーカーが生まれる。
$ bundle exec bin/delayed_job restart
delayed_job: warning: no instances running. Starting...
delayed_job: process with pid 7939 started.
$ ps aux | grep [d]elayed_job
ubuntu    7881 42.2  7.6 653708 309632 ?       Sl   16:19   0:46 delayed_job.0                                   
ubuntu    7887 41.9  7.6 653896 309740 ?       Sl   16:19   0:45 delayed_job.1                                   
ubuntu    7939 41.4  5.5 568964 224836 ?       Sl   16:21   0:12 delayed_job
ソース
ここ。
1個以上の場合にはプロセス数にsuffixを付ける。
stopする時には、stopの時に渡されたnumber_of_workersからプロセスを探すので、stopから漏れるワーカーが生まれる。
https://github.com/collectiveidea/delayed_job/blob/master/lib/delayed/command.rb#L97
Capistranoでの対策
capistranoでdeployする時の対応
config/deploy.rb内で、今起動しているワーカーの全pidを探して来てTERMを送るようにする。
これでうまく動いた。
ソースの一部は、daemonsというgemでワーカーをstopする時のものを拝借。
(snip)
  task :stop_all_delayed_job do
    on roles(:worker) do
      within release_path do
        begin
          pids = capture(:cat, "tmp/pids/delayed_job*")
        rescue
          info "there is no delayed_job process."
          next
        end
        pids.split("\n").each do |pid_s|
          pid = pid_s.to_i
          begin
            execute(:kill, '-s', 'TERM', pid)
            Timeout.timeout(5, TimeoutError) do
              while (execute(:kill, '-s', 'EXIT', pid) rescue false)
                 sleep(0.1)
              end
            end
          rescue ::Exception => e
            puts "exception while trying to stop monitor process #{pid}: #{e}"
          end
        end
        begin; File.delete("tmp/pids/delayed_job*"); rescue ::Exception; end
      end
    end
  end
  
  after 'restart', 'stop_all_delayed_job'
  after 'stop_all_delayed_job', 'delayed_job:start'
(/snip)
まとめ
みんな困ってるんじゃないかと思ったので、公式に解決策があるんじゃないかと思いつつもこれで解決。
