ウェブサービスを作っています。

perform の引数を破壊的に変更していると resque-retry の挙動がおかしい

Resque のプラグインで、Delayed::Job のように自動リトライをしてくれる resque-retry というものがあります。で、

Resque::Failure::MultipleWithRetrySuppression.classes = [Resque::Failure::Redis]
Resque::Failure.backend = Resque::Failure::MultipleWithRetrySuppression

のようにしておけば、リトライ可能な間は、例外が発生しても failed queue に入らないはずなのですが、なぜか入っていたので調べました。

前提

2012/5/16 現在の resque 最新バージョン 1.20.0 は、resque-retry 0.2.2 と相性が悪いので、resque-retry のバージョンを 1.0.0.a にしておきます。
参考: Issue #39: Job is retried infinitely · lantins/resque-retry

# Gemfile
gem 'resque', '1.20.0'
gem 'resque-retry', '1.0.0.a'

問題が起きるパターン

class HogeJob
  extend Resque::Plugins::ExponentialBackoff

  @queue = :hoge

  def self.perform(options)
    options.symbolize_keys!  # <= 破壊的変更

    ...
  end
end

上の例のように、perform の引数を破壊的に変更していると、エラー発生時、resque-retry の挙動がおかしいようです。


ざっと resque-retry のソースを見たところ、引数を文字列化して redis のキーにしている関係で、引数がメソッド実行中に破壊的に変わるとキーが見つからなくなり、挙動がおかしくなるっぽいです。
上の例ならば、例えば、perform 呼び出し時にはキーが

{"a"=>"b"}

だったのに、options.symbolize_keys! 実行後の例外発生時にはキーが

{:a=>"b"}

になっているようなイメージです。


ですので、破壊的変更をしないようにすれば直ります。

class HogeJob
  extend Resque::Plugins::ExponentialBackoff

  @queue = :hoge

  def self.perform(options)
    options = options.symbolize_keys

    ...
  end
end