Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】How to Fix Random OpenAI 500 Errors in Rails Background Jobs Using retry_on

2025年09月14日に「Dev.to」が公開したITニュース「How to Fix Random OpenAI 500 Errors in Rails Background Jobs Using retry_on」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

RailsアプリがOpenAI APIと連携する際、バックグラウンドジョブで発生する一時的な500エラーは、Railsの`retry_on`機能で解決できる。`Faraday::ServerError`を検知し、遅延を伴う自動リトライを複数回設定することで、ジョブの堅牢性を高める。エラー発生時のログ記録も併用し、システム安定化を図る。

ITニュース解説

システム開発の世界では、自分の作ったアプリケーションが、他の会社が提供している機能、一般にAPI(エーピーアイ)と呼ぶもの、を利用する場面がよくある。例えば、天気予報を表示するアプリケーションなら天気予報を提供する会社のAPIを、オンライン決済アプリケーションなら決済会社のAPIを利用する。今回紹介する記事も、自分のアプリケーションがOpenAIという会社のAPIを使ってAIとやり取りをするという、まさにこのようなケースの話だ。

外部のAPIを利用する上で避けられないのが、そのAPIが時々エラーを起こして正常に動作しないことだ。これは、インターネット回線の不具合かもしれないし、APIを提供しているサーバーの一時的な不調かもしれないし、短時間にたくさんのリクエストを送りすぎたことによる制限(レートリミット)かもしれない。どんな理由であれ、自分のアプリケーションが外部のAPIと連携している以上、これらのエラーはいつか必ず発生するものと考え、適切に対処できるように設計しておくことが、信頼性の高いアプリケーションを作る上で非常に重要となる。

記事の筆者も、自身のRailsというフレームワークで開発されたアプリケーションで、まさにこの問題に直面した。アプリケーションは「バックグラウンドジョブ」と呼ばれる仕組みを使って、AI(LLMと略される大規模言語モデル)の応答を生成していた。バックグラウンドジョブとは、ユーザーが直接待つ必要のない時間のかかる処理や、裏側で定期的に実行される処理などを、メインのアプリケーションとは別で動かす仕組みのことだ。これにより、ユーザーは快適にアプリケーションを操作でき、重たい処理が裏で実行されている間も待つ必要がなくなる。しかし、このバックグラウンドジョブが、OpenAIのAPIと通信する際に、「Faraday::ServerError: the server responded with status 500」というエラーで、ときどき失敗していた。

「Faraday::ServerError」というのは、アプリケーションが外部のサーバーに何かをリクエストした際に、その外部サーバーが「500番台」のエラーを返してきたことを示すものだ。「500 Internal Server Error」は、サーバー側で何か予期せぬ問題が発生して処理ができなかったことを意味する。つまり、このエラーは筆者のアプリケーションのコードに問題があるのではなく、OpenAI側のサーバーで一時的に問題が起きていたということだ。しかし、筆者のアプリケーションは、このエラーが発生したときにただ一度だけ処理を試みて、失敗したらそのまま諦めてしまっていた。その結果、失敗したジョブは「失敗キュー」に格納され、開発者が手動で再試行する必要があったため、非常に手間がかかっていた。

このような一時的なエラーに対する最も効果的な対処法は、少し時間を置いてからもう一度試してみることだ。幸いにも、Railsの「Active Job」という機能には、この自動再試行を簡単に行うための仕組みが備わっている。それが「retry_on(リトライオン)」だ。

「retry_on」を導入する最初のステップは、バックグラウンドジョブのクラスに特定の行を追加することだ。具体的には、「retry_on Faraday::ServerError, wait: :polynomially_longer, attempts: 3」という一行を追記した。この一行が、アプリケーションの動作を劇的に改善する。

この記述によって、ジョブは次のような挙動を示すようになる。まず、処理の実行中に「Faraday::ServerError」という種類のエラーが発生したことを検知する。次に、そのエラーをキャッチし、自動的にジョブの再実行を予約する。再実行する際にはすぐにではなく、少し待機してから試みる。この待機時間の指定に「:polynomially_longer」という戦略が使われている。これは、再試行を繰り返すたびに、待機時間が指数関数的に長くなる仕組みだ。例えば、最初の再試行までには約3秒、2回目の再試行までには約18秒、3回目までには約83秒といった具合に、徐々に間隔が空いていく。これは、エラーの原因となっている外部APIが回復するまでにより多くの時間を与えるためで、短い間隔で何度もエラーを起こしているサーバーに負荷をかけ続けるのを避ける目的もある。そして、この再試行は最大で3回まで行われる。「attempts: 3」という指定がその回数を示しており、3回試してもまだエラーが解決しない場合は、最終的にジョブは失敗と判断され、再び手動での介入が必要な「失敗キュー」へ移動される。

これにより、多くの場合、OpenAI側の一時的な問題が数秒から数分で解決し、自動的に再試行されたジョブが無事に成功するようになるため、アプリケーションははるかに堅牢になる。

ただし、自動再試行ができたからといって、エラーがまったく起きていないかのように振る舞うのは良くない。エラーが発生したという事実を開発者は把握しておく必要がある。そこで次のステップとして、エラーが発生した際にその情報を記録する「ロギング」の仕組みを改善する。

API呼び出しを行っているサービス層のコード(今回の場合はgenerate_responseメソッド)に修正を加える。具体的には、API呼び出しの箇所で「Faraday::ServerError」が発生した場合にそれを「rescue(レスキュー)」つまり「捕捉」するコードを追加する。エラーを捕捉したら、そのエラーに関する情報(エラーメッセージや、APIに送信したパラメータなど)を「log_error」という専用のメソッドを使って記録する。このログ情報は、例えばSentryのような監視サービスに送信したり、アプリケーションのログファイルに書き出したりすることで、開発者が後からエラーの発生状況や詳細を確認できるようにする。

ここで非常に重要なのは、エラーをログに記録した後に、再びそのエラーを「raise e(レイズ・イー)」(再スロー)している点だ。もしエラーを捕捉してログに記録するだけで再スローしなかった場合、サービス層はエラーが解決したかのように振る舞ってしまう。すると、上位のジョブはエラーが発生したことに気づかず、「retry_on」の機能が発動しないことになる。エラーをログに記録しつつ、さらに「raise e」でエラーを投げ直すことで、エラーを記録するという目的と、ジョブの「retry_on」機能を発動させて自動再試行させるという両方の目的を達成できる。

このように、RailsのActive Jobが提供する「retry_on」機能と、サービス層での具体的なエラーハンドリングおよびロギングを組み合わせることで、外部APIとの連携時に発生する一時的なエラーに対して、非常に堅牢なバックグラウンドジョブを構築できる。この仕組みは、信頼性の低いネットワークや外部サービスに依存するアプリケーションにとって不可欠だ。実装することで、ユーザーはよりスムーズな体験を得られ、開発者は失敗したジョブの手動再試行に費やす時間を大幅に削減できるようになるだろう。システムエンジニアとしてアプリケーションを開発する際には、常に「もしこの外部連携が失敗したらどうなるか?」という問いを自分に投げかけ、最初からこのような回復力のあるエラー処理戦略を組み込むことを意識すべきだ。

関連コンテンツ