pidファイルの管理をする

pgrep -F pidfileオプションを使うと、pidfileを開いて、その中にあるプロセスIDが存在するか調べ、存在している場合には、0で終了してくれるようだ。

なので、起動スクリプトとかで、pidfileの存在チェックとかをするときには、

   if test -e $PIDFILE && pgrep -F $PIDFILE > /dev/null; then

       restart

   fi

とか書いてやれば、マシンがハングアップとかして、正常に終了しなくて、pidfileが残っているがファイル内にあるプロセスIDはもういない場合にrestartをしても悲しいことにならずにすむ。

dalliはfork safeか?

ふと、dalliがunicornなどのmulti processで動作するアプリケーションで動かしたときにコネクションを張りなおす処理をしないといけないか気になったので、調べてみた。


gist8284358

これを実行してみると、

true
1
I, [2014-01-07T00:27:41.396287 #9290]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.396622 #9291]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.396905 #9292]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.397327 #9293]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.397726 #9294]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.398108 #9295]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.398495 #9296]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.399108 #9297]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.399440 #9298]  INFO -- : localhost:11211 failed (count: 0)
I, [2014-01-07T00:27:41.399961 #9299]  INFO -- : localhost:11211 failed (count: 0)
[9290, 0, 1]   
[9292, 0, 1]   
[9291, 0, 1]   
[9290, 1, 1]   
[9293, 0, 1] 


こんな感じになり、forkした直後のログで失敗した、というエラーメッセージが出るが、その後、再接続し、通信できているようだ。

dalliのソースコード(acb1ff3afd4d)をpidでgrepすると、

# lib/dalli/server.rb
    def verify_state
      failure!(RuntimeError.new('Already writing to socket')) if @inprogress
      failure!(RuntimeError.new('Cannot share client between multiple processes')) if @pid && @pid != Process.pid
    end


という処理が出てきて、newしたときと、pidが変わっているかをチェックしていることがわかる。

このverify_stateはクライアントがリクエストしようとしたときには必ず通過するようになっているので、これでpidの変更を検出しているようだ。

failureの中では、

    def failure!(exception)
      message = "#{hostname}:#{port} failed (count: #{@fail_count}) #{exception.class}: #{exception.message}"
      Dalli.logger.info { message }

      @fail_count += 1
      if @fail_count >= options[:socket_max_failures]
        down!
      else
        close
        sleep(options[:socket_failure_delay]) if options[:socket_failure_delay]
        raise Dalli::NetworkError, "Socket operation failed, retrying..."
      end
    end

のようになっていて、失敗回数を保持するカウンタを1あげつつ、例外を吐いている。

ここで発生させた例外は、

# lib/dalli/client.rb
    # Chokepoint method for instrumentation
    def perform(*all_args, &blk)
      return blk.call if blk
      op, key, *args = *all_args

      key = key.to_s
      key = validate_key(key)
      begin
        server = ring.server_for_key(key)
        ret = server.request(op, key, *args)
        ret
      rescue NetworkError => e
        Dalli.logger.debug { e.inspect }
        Dalli.logger.debug { "retrying request with new server" }
        retry
      end
    end

serverを呼びだしている元の、performメソッドで、rescueされて、retryするようだ。