retry-handler
「失敗したら N 回リトライする」という処理を簡単に書けるようにする Ruby ライブラリです。Proc に retry メソッドを生やして、リトライしたい処理をブロックで包むだけで最大回数・待ち時間・対象例外を指定したリトライができます。
gem として公開していて、Ruby 1.8/1.9 で動きます。
gem install retry-handler
使い方
Proc のブロック内で例外が発生すると、指定回数まで自動でリトライします。
Proc.new{
# なんかエラーの起こるコード
}.retry(:max => 3, :accept_exception => StandardError)
:wait を指定すると失敗のたびに指定秒だけ待ってから次の試行に入ります。処理の流れを可視化したいときは :logger を付けます。Proc.new と書くのがダサいときは lambda でも使えます。
lambda{}.retry(:max => 3, :wait => 3)
lambda{}.retry(:max => 3, :wait => 3, :logger => Logger.new(STDERR))
Method オブジェクトにも retry が生えるので、既存メソッドをそのままリトライ対応にできます。File.read の例です。
File.method(:read).retry("./not_found.txt",
:accept_exception => StandardError, :logger => Logger.new(STDERR))
内部設計
実体は 1 ファイル(lib/retry-handler.rb)で、中核は RetryHandler.retry_handler の 1 メソッドです。Proc#retry・Method#retry・Kernel#retry_handler はどれもそこへ委譲するモンキーパッチになっています。
- オプションの既定値 —
:maxは 3 回、:waitは 1 秒、:timeoutはなし(無制限)、:accept_exceptionは同梱のRetryError、:loggerはLogger.new(nil)(何も出力しない)です - リトライ対象は
:accept_exceptionで指定した例外だけです。既定値のRetryErrorを自分で投げることは普通ないので、実用上は README の例のように:accept_exception => StandardErrorなどを渡す形になります。対象外の例外はそのまま素通しです :timeoutを指定するとtimeout()でブロックを包み、時間超過時に:accept_exceptionの例外を送出させます。タイムアウトも「捕捉対象の例外」に化けるので、リトライループ側は例外の一種として一元的に扱えますMethod#retryは引数リストの末尾がHashならそれをオプションとして取り出し、残りをメソッド本体の引数として渡します
リトライループ本体(RetryHandler._retry_handler)の流れです。begin/rescue/retry の素直な作りで、失敗のたびにカウントを進めて sleep してやり直します。
- リトライを使い切ると、最後の例外を添えて
RetryOverErrorを送出します。例外クラスはRetryError < StandardError、RetryOverError < RetryErrorの 2 つです :maxは「リトライ回数」なので、実行回数としては最初の 1 回 + 最大:max回 = 最大 4 回(既定値の場合)です
自作の vrowser がこの gem を使っていて、マスターサーバーからのサーバー一覧取得が失敗したときのリトライに充てています。