2016-09-03

JRuby + Poltergeist で並列化すると落ちる問題

CapybaraとPoltergeistを使ってクローリングしたい時、せっかくだから並列化して高速化を図りたい。ところが適当にスレッドを分けて並列化するとRubyが止まってしまう。そしてPhantomjsだけはバックグラウンドで動き続けていると言う謎の挙動があり、二週間くらい悩んでいた。おそらくはJRuby特有の問題で、普通のMRIでは再現しなかった。

どうやらJRuby側でPhantomjsからのコンソール出力をうまく受け取れていないことが問題の根元らしい。とりあえずPoltergeistのclient.rbにあるstartstopをモンキーパッチしてJRuby側で受け取らないようにしてみた。

module Capybara::Poltergeist
  class Client
    def start
      @pid = Process.spawn(*command.map(&:to_s), pgroup: true)
      ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
    end

    def stop
      if pid
        kill_phantomjs
        ObjectSpace.undefine_finalizer(self)
      end
    end
  end
end

問題は解決した。

ちなみにstartのコードはもともとこんな風になっている。

def start
  @read_io, @write_io = IO.pipe
  @out_thread = Thread.new {
    while [email protected]_io.eof? && data = @read_io.readpartial(1024)
      @phantomjs_logger.write(data)
    end
  }

  process_options = {}
  process_options[:pgroup] = true unless Capybara::Poltergeist.windows?

  redirect_stdout do
    @pid = Process.spawn(*command.map(&:to_s), process_options)
    ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
  end
end

なんとなく怪しさを感じる。STDOUT redirectionはJRubyと相性よくなさそうだ。