トレタ開発者ブログ

飲食店向け予約/顧客台帳サービス「トレタ」、モバイルオーダー「トレタO/X」などを運営するトレタの開発メンバーによるブログです。

FaradayのClientErrorに苦しんだ話

こんばんは。配達依存症がさっぱり治らないサーバサイドエンジニアの佐藤です。 この記事はトレタ Advent Calendar 2019の三日目です。

FaradayというHTTP クライアントライブラリ

みなさんRubyのFaradayというHTTP クライアントライブラリをご存知ですか? Faradayを使えば外部のAPIと接続するクライアントを楽に記述することができます。ミドルウェアを差し込むことでリクエストやレスポンスをいじったり覗いたりすることができ、JSONに変換したい、アクセストークンを乗せたい、細かいログを取りたい、と言ったことを一箇所にまとめて記述することができます。いろいろできて便利なのですが、今日はその中でも raise_error middleware という特定のステータスコードが返ってきた時例外を出してくれるミドルウェアに苦しんだ話を書きます。

raise_error middleware と retry middleware

raise_error middleware は400や404と言ったClientErrorや500のようなServerErrorのステータスコードが返ってきた時に、それに応じた例外を出してくれます。これによってステータスコードを確認して処理を抜けるようなコードを書いたりする必要もなくrubyの例外処理を使って様々なErrorに対応することができます。

これとよく組み合わせて利用されるのが retry middleware です。 retryはその名前の通り特定の例外や条件においてリクエストを自動でリトライさせることができます。例外がトリガーにできるため、上記raise_errorと組み合わせることでタイムアウトしてしまったらとりあえずもう一度やり直す、のような処理をmiddlewareの設定のみで行うことができるようになります。

raise_errorがraiseするもの

さてここまで揃えば話は単純で、raise_errorが上げてくれる諸々の例外に合わせて必要なretry設定をretry middlewareでしてあげれば良い。そう思っていました。 ところがこれ、実際にやってみると faraday (0.17.1) (執筆時最新バージョン)では期待通りに動作しません。なぜなら、404,407を除く400〜600は全てClientErrorが吐かれるからです。 (該当のソースコードはこちら

これ最初知らなくてハマりました。よく出会うであろう404はちゃんと専用の Faraday::ResourceNotFound が返ってくるんです。他の4xxが Faraday::ClientError なのはわかるとして、5xxまでClientErrorだったのは最初全くの想定外で、そうだと理解できるまで結構な時間を費やしました。

ちなみに Faraday:: ServerError ですが、ステータスコードが取得できないケース(ServerError)やタイムアウト時(TimeoutError)に出てきます。

5xxでリトライしてもらうには(4xxでリトライしないためには)

幸いなことにretry middlewareには retry_if という条件を指定してリトライさせる機能があります。こちらを利用してClientErrorの中からさらに4xx以外の時にのみretryさせることができるようになります。ちょっと本末転倒感はあるんですが、それでもfaradayとmiddlewareの設定で完結させることができるために、見通しはよくなる気がします。

サンプルコード

conn = Faraday.new(:url => 'http://example.com') do |faraday|
  faraday.request   :retry,
    methods: [], # retry_ifを無視してリトライするHTTP methods。無視して欲しく無いのでemptyにする
    exceptions: [Faraday::Error::TimeoutError, Faraday::Error::ConnectionFailed, Faraday::Error::ClientError],
    retry_if: ->(env, _exception) { !(400..499).include?(env.status) } # status codeの4xx以外ならリトライ
  faraday.response :raise_error
  faraday.adapter Faraday.default_adapter
end

補足:次のバージョンで直るらしい

faradayのmasterブランチを見にいくと、ServerErrorがちゃんとだし分けられています。faradayはv0.x、つまりまだ開発版なのでv1.0.0のリリースで書き換わるのでしょう。その際のバージョンアップでは注意が必要です。

余談ですが、0.16.2の時に一瞬このServerErrorへの変更がお漏らしして、必死こいて対応してました。ようやく修正が終わって、その苦労をblogにしてやろう・・・と思った矢先に0.17でロールバックされてしまったので、本記事はその供養も兼ねています。


今年のトレタのアドベントカレンダーはこちら!

qiita.com

© Toreta, Inc.

Powered by Hatena Blog