1 分

安全なサードパーティAPI統合:リトライ、タイムアウト、サーキットブレーカー

障害時でもアプリを稼働させる安全なサードパーティAPI統合。タイムアウト、リトライ、サーキットブレーカー、簡単な確認事項を学びます。

安全なサードパーティAPI統合:リトライ、タイムアウト、サーキットブレーカー

サードパーティAPIがコアワークフローを詰まらせる理由

サードパーティAPIは「完全にダウンしている」ように見えない形で失敗することがあります。最も一般的なのは遅延です:リクエストがハングし、応答が遅れ、アプリが待ち続けます。そうした呼び出しがクリティカルパスにあると、外部の小さな問題が内部で積み重なります。

これがローカルな遅延が全体の障害に発展する仕組みです。スレッドやワーカーが待機で詰まり、キューが増え、データベーストランザクションが長時間開いたままになり、新しいリクエストがタイムアウトし始めます。やがて、外部APIを使っていないページでさえ、待機中の作業でシステムが過負荷になり壊れたように見えることがあります。

影響は具体的です。不安定な認証プロバイダはサインアップやログインを止めます。決済ゲートウェイのタイムアウトはチェックアウトを凍結させ、ユーザーは課金されたかどうか不安になります。メッセージ遅延はパスワードリセットや注文確認を止め、再度のリトライやサポートチケットを生みます。

目標は単純です:外部の失敗を分離して、コアワークフローを動かし続けること。たとえば、支払いを後で確定する間に注文を受け付ける、ウェルカムメールが失敗してもサインアップを許可する、などです。

実用的な成功指標:プロバイダが遅い・ダウンしているときでも、アプリは素早く明確に応答し、被害範囲(blast radius)が小さく保たれること。具体例として、ほとんどのコアリクエストが通常のレイテンシ予算内で完了し、失敗はそのAPIに依存する機能に限定され、ユーザーには明確な状態(キュー済み、保留、後で再試行してください)が表示され、プロバイダ復旧時に自動で回復する、などが挙げられます。

想定すべき失敗モード

ほとんどの失敗はタイミングは予測できなくても種類は予測可能です。先に名前を挙げておけば、何をリトライし、何を止め、ユーザーに何を見せるかを決められます。

一般的なカテゴリ:

  • レイテンシの急上昇(突然10倍遅くなるリクエスト)
  • 一時的なサーバまたはネットワークエラー(タイムアウト、502/503、接続リセット)
  • レート制限やクォータの枯渇(429、日次上限)
  • 認証や権限の問題(キーの期限切れ、アクセス取り消し)
  • 不正または意外なデータ(フィールド欠落、フォーマット不正、部分的な応答)

すべてのエラーが同じ意味を持つわけではありません。一時的な問題はリトライする価値があります(ネットワークのノイズ、タイムアウト、502/503、一部の429など)。永続的な問題は自然には直りません(無効な認証情報、間違ったエンドポイント、権限拒否、リクエストの不備)。

すべてのエラーを同じ扱いにすると、小さなインシデントがダウンタイムになってしまいます。永続的な失敗をリトライし続けると時間を無駄にし、レート制限に早く達し、バックログができて他が遅くなります。逆に一時的な失敗をまったくリトライしないと、ユーザーは同じ操作を繰り返す必要があり、数秒後に完了したかもしれない作業を失います。

特に注意すべきワークフロー:チェックアウト、ログイン、パスワードリセット、通知(メール/SMS/プッシュ)。マーケティングAPIでの2秒のスパイクは煩わしいですが、決済承認での2秒は収益を止めます。

役立つテストは:「この呼び出しは今この瞬間、ユーザーの主要タスクを完了するために必須か?」です。もし必須なら、厳しいタイムアウト、慎重なリトライ、明確な失敗経路が必要です。必須でないならキューに移してアプリを応答性の高いまま保ちましょう。

タイムアウト:上限を決めて守る

タイムアウトは「これ以上待たない」と決める時間です。明確な上限がないと、ひとつの遅いプロバイダが待機中のリクエストを積み上げ、重要な作業をブロックしてしまいます。

待機には大きく二種類があります:

  • Connect timeout:接続確立にどれだけ待つか。
  • Read timeout:接続後に応答をどれだけ待つか。

数値設定は完璧さの問題ではなく、人間の我慢とワークフローに合わせることが重要です。

  • ユーザーがスピナーを見ている場合、早い応答と次の明確な手順が必要です。
  • バックグラウンドジョブ(夜間の請求同期など)なら長めに許容できますが、それでも永遠にハングしない上限は必要です。

タイムアウトの現実的な選び方は、体験から逆算することです:

  • ユーザーはどれくらい待てば「明確なメッセージ」を出す必要があるか?
  • この呼び出しが今失敗したら、後でリトライできるか、フォールバックがあるか?
  • ピーク時にこの呼び出しはどれだけ走るか?

トレードオフは明白です。長すぎるとスレッド、ワーカー、DB接続を占有します。短すぎると偽陽性の失敗を生み、不必要なリトライを誘発します。

障害を悪化させないリトライ

リトライは一時的な失敗(短いネットワーク障害、DNSのノイズ、一時的な500/502/503)に有効です。その場合、2回目の呼び出しで成功し、ユーザーは気づきません。

リスクはリトライ嵐です。多くのクライアントが同時に失敗して同時にリトライすると、プロバイダ(および自分のワーカー)を圧倒します。バックオフとジッターでこれを防ぎます。

リトライ予算を設けることで過剰な試行を防げます。試行回数は少なく、合計時間を制限してコアワークフローが他人に待たされないようにします。

安全なデフォルトのリトライ設定

  • リトライはごく少数に(一般的には合計1〜3回、フローにより異なる)。
  • 指数バックオフ(例:200ms、500ms、1s)にランダムジッターを追加。
  • リトライに費やす合計時間に上限を設ける(ユーザー向けフローでは数秒)。
  • 全試行を通した長いタイムアウトではなく、試行ごとのタイムアウトを使う。

400/422のような予測可能なクライアントエラー、401/403の認証関連、404はリトライしないでください。ほとんどの場合再試行しても失敗します。

もう一つのガードレール:POST/PUTなどの書き込みは冪等性が確保されている場合にのみリトライしてください。さもないと二重請求や重複レコードのリスクがあります。

冪等性:実運用でリトライを安全にする

冪等性とは、同じリクエストを2回実行しても最終結果が同じであることです。これは重要です。ネットワーク切断、サーバ再起動、クライアントのタイムアウトでリトライは普通に発生します。冪等性がなければ、親切なリトライが重複を生み、金銭的な問題を引き起こします。

チェックアウトの例を想像してください:支払いAPIが遅くてアプリがタイムアウトし、再試行したとします。最初の呼び出しが実際には成功していた場合、再試行は二重課金を引き起こすかもしれません。同じリスクは注文作成、サブスクリプション開始、メール/SMS送信、返金発行、サポートチケット作成などでも発生します。

対策は、各「何かを実行する」呼び出しに冪等性キー(またはリクエストID)を添付することです。キーは試行ごとではなくユーザーの操作ごとに一意にします。プロバイダ(または自分のサービス)はそのキーで重複を検出し、同じ結果を返して再実行を防ぎます。

冪等性キーはヘッダだから忘れられても良いものではなく、データモデルの一部として扱ってください。

本番で通用するパターン

ユーザーがアクションを開始したとき(例:支払いボタンを押したとき)に1つのキーを生成し、ローカルのレコードに保存します。

各試行時:

  • 同じキーを送信する。
  • 返ってきた最終結果を保存する(成功レスポンス、失敗コード、チャージIDなど)。
  • 既に記録された結果があれば、その結果を返して再実行を避ける。

もしあなたが内部呼び出しの「プロバイダ」であれば、サーバー側で同じ振る舞いを強制してください。

サーキットブレーカー:失敗している時に呼び出しを止める

生成されたコードを自分で管理する
統合を生成した後、ソースコードをエクスポートして確認・カスタマイズできます。

サーキットブレーカーは安全スイッチです。外部サービスが失敗し始めたら、追加のタイムアウトが発生する前に短時間その呼び出しを止めます。

サーキットブレーカーには通常、三つの状態があります:

  • Closed(閉):通常どおりリクエストを流す。
  • Open(開):一定のクールダウン期間、呼び出しをブロックする。
  • Half-open(半開):クールダウン後に少数のテスト呼び出しで復旧を確認する。

ブレーカーが開いているとき、アプリは予測可能な対応をするべきです。サインアップ時に住所検証APIがダウンしているなら、住所を受け付けて後で確認するようマークする。決済のリスクチェックがダウンしているなら、その注文を手動レビューのキューに入れるか、一時的にそのオプションを無効にして説明する。

閾値はユーザーへの影響に合わせて選びます:

  • 連続エラー(例:5回連続失敗)
  • 短いウィンドウでの高い失敗率
  • 多数の遅い応答(タイムアウト)
  • 特定のステータスコード(繰り返される503など)

クールダウンは短めに(数秒〜数十秒)し、半開のプローブは限定的にします。目的はまずコアワークフローを守り、素早く回復することです。

フォールバックとキュー:アプリを使える状態に保つ

外部APIが遅いかダウンしているとき、目標はユーザーを動かし続けることです。そのためには正直なプランBが必要です。

フォールバック:"十分に良い" 代替体験を選ぶ

APIが応答しないときにアプリが行う代替策をフォールバックと呼びます。キャッシュデータの利用、縮小モード(重要でないウィジェットを隠す、任意の操作を無効にする)、API呼び出しの代わりにユーザー入力を求める(手動で住所入力させる)、または次の手順を明示したメッセージを表示する、などがあります。

正直に扱ってください:実際に完了していないのに「完了」と表示してはいけません。

キュー:今やる必要がないなら後でやる

処理がユーザーリクエスト内で完了する必要がないなら、それをキューに押し込み素早く応答してください。一般的な候補はメール送信、CRMへの同期、レポート生成、アナリティクスイベントの投稿などです。

コアな操作については早く失敗させる(fail fast)。もしAPIがチェックアウトやアカウント作成の完了に不要なら、リクエストをブロックしないで注文を受け付け、その外部呼び出しはキューに入れて後で整理してください。APIが必須(例:支払い承認)の場合は、ユーザーを無駄に待たせずに素早く失敗を返し、明確なメッセージを表示してください。

ユーザーに見せる情報は内部で起きていることと一致するべきです:明確な状態(完了、保留、失敗)、守れる約束(領収書は今、確認は後で)、再試行の方法、UI上で見える記録(アクティビティログ、保留バッジ)など。

レート制限と負荷:自分で招く障害を避ける

レート制限はプロバイダの「呼んでいいが頻度は控えてね」という合図です。思ったより早く到達します:トラフィックのスパイク、バックグラウンドジョブの同時実行、エラーでのループなどが原因です。

まず作成するリクエスト数を制御しましょう。可能ならバッチ処理、30〜60秒のキャッシュ(安全なら)を使い、クライアント側でスロットルしてプロバイダの許容より急にバーストしないようにします。

429 Too Many Requestsを受け取ったら、減速の合図として扱ってください。

  • Retry-After が提供されていれば従う。
  • 多数のワーカーが同時にリトライしないようジッターを加える。
  • 429にはリトライ回数を上限にする。
  • 繰り返す429にはより積極的にバックオフする。
  • メトリクスとして記録し、ユーザーより先に問題に気づけるようにする。

同時に、並列度を制限してください。単一のワークフロー(連絡先同期など)がすべてのワーカースロットを消費してログインやチェックアウトを枯渇させてはなりません。別プールや機能ごとのキャップが有用です。

ステップバイステップ:安全な統合のデフォルト手順

正直な障害状態を設計する
障害時に何が起こったか分かるよう、明確な保留・リトライのUXを作ります。

すべてのサードパーティ呼び出しには障害時の計画が必要です。完璧は不要で、プロバイダが調子を崩した日に予測可能に振る舞うことが重要です。

1) 呼び出しを分類する(必須 vs 後回し)

その呼び出しが今失敗したらどうなるかを決めます。チェックアウト中の税計算は必須かもしれません。マーケティングのコンタクト同期は通常後回し可能です。この判断が以降の設定を決めます。

2) タイムアウトとリトライ予算を設定する

呼び出しタイプごとにタイムアウトを決め、一貫性を保ちます。その後リトライ予算を設けて遅いAPIを叩き続けないようにします。

  • ユーザー待ち:短いタイムアウト、リトライ0〜1回。
  • 後回し可能、バックグラウンド:長めのタイムアウト、バックオフ付きで数回リトライ。
  • 永遠にリトライしない:タスクごとの合計時間に上限を設ける。

3) 冪等性と追跡でリトライを安全にする

何かを作成したり課金したりするリクエストには冪等性キーを付け、リクエスト記録を保存します。支払いリクエストがタイムアウトしても、リトライで二重課金してはいけません。追跡はサポートが「通ったか?」に答えるのにも役立ちます。

4) サーキットブレーカーとフォールバックを追加する

エラーが急増したら短期間そのプロバイダへの呼び出しを止めます。必須呼び出しでは明確な「再試行」経路を示し、後回し可能な呼び出しはキューに入れて後で処理します。

5) 基本を監視する

レイテンシ、エラー率、ブレーカーの開閉イベントを追跡します。単発のノイズではなく持続的な変化でアラートするように設定してください。

小さな問題を大きな障害にするよくあるミス

多くのAPI障害は最初は小さいです。アプリが最悪の反応をすると大きくなります:待ちすぎ、過剰なリトライ、同じワーカーを占有する、などです。

これらのパターンがカスケードを引き起こします:

  • すべての失敗をリトライ(無効なリクエストや期限切れ認証、権限不足の4xxも含む)。
  • 「安全のために」と非常に長いタイムアウトを設定し、スレッドやDB接続、ジョブランナーを静かに消費してしまう。
  • 冪等性キーなしで作成操作をリトライし、二重請求や重複出荷を招く。
  • 復旧しない、または激しく開閉するような誤設定のサーキットブレーカー。
  • 部分的な障害を全体の失敗として扱い、影響範囲を広げる。

小さな修正が大きな障害を防ぎます:一時的なエラー(タイムアウト、一部の429、一部の5xx)のみリトライし、バックオフとジッターで試行を上限まで制限する。タイムアウトは短く意図的に保ち、作成や課金を伴う操作には冪等性を必須にし、部分的障害を許容する設計にすること。

出荷前のクイックチェックリスト

コアワークフローを保護する
プロバイダが遅くなっても応答性を保つチェックアウトやサインアップのフローを作成します。

統合を本番に投入する前に、障害を想定した観点で素早く確認してください。以下に「はい」と答えられない項目があれば、サインアップ、チェックアウト、メッセージ送信などのコアワークフローに関してはリリースブロッカーとして扱ってください。

  • タイムリミットが明示されている(接続タイムアウトと読み取り/応答タイムアウト)。
  • リトライが制限されている(小さなリトライ予算、バックオフ、ジッター、総時間上限)。
  • リトライは実際の操作でも安全(冪等性キーや明確な重複排除チェック)。
  • ブレーカーとプランBがある(フォールバック、縮小モード、またはキュー)。
  • 問題を早く検知できる(レイテンシ、エラー率、プロバイダ・エンドポイント別の依存性ヘルス)。

決済プロバイダがタイムアウトし始めたら、正しい振る舞いは「チェックアウトは読み込まれ、ユーザーに明確なメッセージを表示し、永遠に待たせない」ことであり、「すべてがタイムアウトするまで固まる」ことではありません。

例:プロバイダが不安定なときにチェックアウトを守る

チェックアウトが三つのサービスを呼ぶと想像してください:カードを課金する決済API、税額を計算する税API、領収書を送るメールAPI。

決済呼び出しだけが同期で必須です。税やメールの問題で購入が詰まってはいけません。

税APIが遅い場合

税APIが時々8〜15秒かかるとします。もしチェックアウトが待つと、ユーザーはカートを放棄し、ワーカーが詰まります。

安全な流れの例:

  • ハードタイムアウトを設定する(例:800ms〜2s)して速やかに失敗させる。
  • 安全ならリトライは最大1回、ジッター付きで。
  • タイムアウトが発生したらキャッシュ済みの税率や購入者地域の最終既知テーブルを使う。
  • 法的にキャッシュが使えないなら、注文を「税保留」としてキューに入れて再計算する。

結果:税プロバイダが遅くてもカート放棄や詰まった注文が減ります。

メールAPIが落ちている場合

領収書メールは重要ですが、支払い確定をブロックしてはいけません。メールAPIが失敗しているなら、数回の短い失敗後にサーキットブレーカーは開き、短いクールダウン期間その呼び出しを止めるべきです。

メールをインラインで送る代わりに、send receipt ジョブをキューに入れ、冪等性キー(例:order_id + email_type)を付けます。プロバイダがダウンしている場合はキューがバックグラウンドでリトライし、顧客には購入成功が表示されます。

結果:確認メールが届かないことでのサポート件数が減り、非決済要因でチェックアウトが失敗して収益を失うことがなくなります。

次のステップ:アプリ全体に安全策を展開する

最も壊れたときに痛いワークフロー(チェックアウト、サインアップ、請求)を一つ選び、それを参照用の統合にします。次に同じデフォルトを他の箇所にコピーしてください。

簡単な導入順:

  • タイムアウトを設定し、速やかに失敗を返す。
  • リトライをバックオフ付きで追加するが、リトライ可能なエラーに限定する。
  • 冪等性を追加してリトライで二重課金や二重作成が起きないようにする。
  • サーキットブレーカーを追加して、悪いプロバイダがコアワークフローを詰まらせないようにする。

デフォルトを文書化して平凡に保ってください:1つの接続タイムアウト、1つのリクエストタイムアウト、最大リトライ回数、バックオフ範囲、ブレーカーのクールダウン、そして何がリトライ可能かのルール。

次のワークフローに拡張する前にフェイルドリルを実施してください。テスト環境でタイムアウトを強制(またはプロバイダをブロック)し、ユーザーが有用なメッセージを見るか、フォールバックが機能するか、キューのリトライが無限に溜まらないかを確認します。

新しいプロダクトを素早く作る場合、これらの信頼性デフォルトを再利用可能なテンプレートにする価値があります。Koder.ai (koder.ai) を使うチームでは、タイムアウト、リトライ、冪等性、ブレーカーのルールを一度定義しておき、新しいサービスに対して同じパターンを適用することが多いです。

よくある質問

低速なサードパーティAPIが、なぜアプリを停止させることがあるのですか?

すべての呼び出しで、接続タイムアウトと読み取りタイムアウトを明示的に設定してください。ユーザー向けのフローには短い上限を選び、明確な結果を返すか、必須ではない処理をキューに移します。

どのAPIエラーを再試行すべきですか?

タイムアウト、接続リセット、一部の5xxエラーはすぐに解消する可能性があるため、再試行してください。無効なリクエスト、誤った認証情報、権限エラー、存在しないエンドポイントは再試行しないでください。

APIリクエストは何回再試行すべきですか?

再試行回数は少なく保ち、通常は合計1〜3回にします。ランダムなジッターを含む指数バックオフを使い、再試行に費やす合計時間に上限を設けてください。

支払いリクエストがタイムアウトした場合、重複請求を防ぐにはどうすればよいですか?

レコードの作成、メッセージ送信、返金、課金を行う操作には、冪等性キーを使用してください。1回のユーザー操作から生じるすべての試行で、同じキーを再利用します。

サーキットブレーカーは何をしますか?

サーキットブレーカーは、失敗やタイムアウトが繰り返された後、呼び出しを一時的に停止します。ワーカーを保護し、限定的なテストリクエストを再び送る前にプロバイダーが回復する時間を確保します。

どの処理をバックグラウンドキューに移すべきですか?

任意の呼び出しを分離して、チェックアウトとログインの応答性を保ってください。ユーザーリクエスト中に待機する代わりに、領収書メール、CRM更新、分析処理をキューに入れます。

アプリは429レート制限レスポンスをどのように処理すべきですか?

プロバイダーのRetry-After値がある場合はそれに従い、並行処理数を減らし、再試行前にジッターを加えてください。アプリの呼び出し回数を減らすため、安全な読み取りはキャッシュまたはバッチ処理にします。

外部サービスが失敗した場合、ユーザーには何を表示すべきですか?

実際の状態を表示します。完了、保留中、または失敗です。支払いを再試行する、保留中の注文を後で確認するなど、ユーザーが取れる実用的な次の手順を示してください。

サードパーティAPIについて何を監視すべきですか?

プロバイダーとエンドポイントごとに、レイテンシ、エラー率、タイムアウト率、レート制限レスポンス、サーキットブレーカーのイベントを追跡してください。問題を早期に検知できるよう、変化が継続した場合にアラートを出します。

リリース前にAPI連携をどのようにテストすべきですか?

タイムアウト、部分的なレスポンス、429、5xxエラー、繰り返される書き込みリクエストをテストしてください。フォールバックが機能すること、キューに入れたジョブが際限なく増えないこと、再試行で作業が重複しないことを確認します。

Related posts