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

サードパーティAPIは「完全にダウンしている」ように見えない形で失敗することがあります。最も一般的なのは遅延です:リクエストがハングし、応答が遅れ、アプリが待ち続けます。そうした呼び出しがクリティカルパスにあると、外部の小さな問題が内部で積み重なります。
これがローカルな遅延が全体の障害に発展する仕組みです。スレッドやワーカーが待機で詰まり、キューが増え、データベーストランザクションが長時間開いたままになり、新しいリクエストがタイムアウトし始めます。やがて、外部APIを使っていないページでさえ、待機中の作業でシステムが過負荷になり壊れたように見えることがあります。
影響は具体的です。不安定な認証プロバイダはサインアップやログインを止めます。決済ゲートウェイのタイムアウトはチェックアウトを凍結させ、ユーザーは課金されたかどうか不安になります。メッセージ遅延はパスワードリセットや注文確認を止め、再度のリトライやサポートチケットを生みます。
目標は単純です:外部の失敗を分離して、コアワークフローを動かし続けること。たとえば、支払いを後で確定する間に注文を受け付ける、ウェルカムメールが失敗してもサインアップを許可する、などです。
実用的な成功指標:プロバイダが遅い・ダウンしているときでも、アプリは素早く明確に応答し、被害範囲(blast radius)が小さく保たれること。具体例として、ほとんどのコアリクエストが通常のレイテンシ予算内で完了し、失敗はそのAPIに依存する機能に限定され、ユーザーには明確な状態(キュー済み、保留、後で再試行してください)が表示され、プロバイダ復旧時に自動で回復する、などが挙げられます。
ほとんどの失敗はタイミングは予測できなくても種類は予測可能です。先に名前を挙げておけば、何をリトライし、何を止め、ユーザーに何を見せるかを決められます。
一般的なカテゴリ:
すべてのエラーが同じ意味を持つわけではありません。一時的な問題はリトライする価値があります(ネットワークのノイズ、タイムアウト、502/503、一部の429など)。永続的な問題は自然には直りません(無効な認証情報、間違ったエンドポイント、権限拒否、リクエストの不備)。
すべてのエラーを同じ扱いにすると、小さなインシデントがダウンタイムになってしまいます。永続的な失敗をリトライし続けると時間を無駄にし、レート制限に早く達し、バックログができて他が遅くなります。逆に一時的な失敗をまったくリトライしないと、ユーザーは同じ操作を繰り返す必要があり、数秒後に完了したかもしれない作業を失います。
特に注意すべきワークフロー:チェックアウト、ログイン、パスワードリセット、通知(メール/SMS/プッシュ)。マーケティングAPIでの2秒のスパイクは煩わしいですが、決済承認での2秒は収益を止めます。
役立つテストは:「この呼び出しは今この瞬間、ユーザーの主要タスクを完了するために必須か?」です。もし必須なら、厳しいタイムアウト、慎重なリトライ、明確な失敗経路が必要です。必須でないならキューに移してアプリを応答性の高いまま保ちましょう。
タイムアウトは「これ以上待たない」と決める時間です。明確な上限がないと、ひとつの遅いプロバイダが待機中のリクエストを積み上げ、重要な作業をブロックしてしまいます。
待機には大きく二種類があります:
数値設定は完璧さの問題ではなく、人間の我慢とワークフローに合わせることが重要です。
タイムアウトの現実的な選び方は、体験から逆算することです:
トレードオフは明白です。長すぎるとスレッド、ワーカー、DB接続を占有します。短すぎると偽陽性の失敗を生み、不必要なリトライを誘発します。
リトライは一時的な失敗(短いネットワーク障害、DNSのノイズ、一時的な500/502/503)に有効です。その場合、2回目の呼び出しで成功し、ユーザーは気づきません。
リスクはリトライ嵐です。多くのクライアントが同時に失敗して同時にリトライすると、プロバイダ(および自分のワーカー)を圧倒します。バックオフとジッターでこれを防ぎます。
リトライ予算を設けることで過剰な試行を防げます。試行回数は少なく、合計時間を制限してコアワークフローが他人に待たされないようにします。
400/422のような予測可能なクライアントエラー、401/403の認証関連、404はリトライしないでください。ほとんどの場合再試行しても失敗します。
もう一つのガードレール:POST/PUTなどの書き込みは冪等性が確保されている場合にのみリトライしてください。さもないと二重請求や重複レコードのリスクがあります。
冪等性とは、同じリクエストを2回実行しても最終結果が同じであることです。これは重要です。ネットワーク切断、サーバ再起動、クライアントのタイムアウトでリトライは普通に発生します。冪等性がなければ、親切なリトライが重複を生み、金銭的な問題を引き起こします。
チェックアウトの例を想像してください:支払いAPIが遅くてアプリがタイムアウトし、再試行したとします。最初の呼び出しが実際には成功していた場合、再試行は二重課金を引き起こすかもしれません。同じリスクは注文作成、サブスクリプション開始、メール/SMS送信、返金発行、サポートチケット作成などでも発生します。
対策は、各「何かを実行する」呼び出しに冪等性キー(またはリクエストID)を添付することです。キーは試行ごとではなくユーザーの操作ごとに一意にします。プロバイダ(または自分のサービス)はそのキーで重複を検出し、同じ結果を返して再実行を防ぎます。
冪等性キーはヘッダだから忘れられても良いものではなく、データモデルの一部として扱ってください。
ユーザーがアクションを開始したとき(例:支払いボタンを押したとき)に1つのキーを生成し、ローカルのレコードに保存します。
各試行時:
もしあなたが内部呼び出しの「プロバイダ」であれば、サーバー側で同じ振る舞いを強制してください。
サーキットブレーカーは安全スイッチです。外部サービスが失敗し始めたら、追加のタイムアウトが発生する前に短時間その呼び出しを止めます。
サーキットブレーカーには通常、三つの状態があります:
ブレーカーが開いているとき、アプリは予測可能な対応をするべきです。サインアップ時に住所検証APIがダウンしているなら、住所を受け付けて後で確認するようマークする。決済のリスクチェックがダウンしているなら、その注文を手動レビューのキューに入れるか、一時的にそのオプションを無効にして説明する。
閾値はユーザーへの影響に合わせて選びます:
クールダウンは短めに(数秒〜数十秒)し、半開のプローブは限定的にします。目的はまずコアワークフローを守り、素早く回復することです。
外部APIが遅いかダウンしているとき、目標はユーザーを動かし続けることです。そのためには正直なプランBが必要です。
APIが応答しないときにアプリが行う代替策をフォールバックと呼びます。キャッシュデータの利用、縮小モード(重要でないウィジェットを隠す、任意の操作を無効にする)、API呼び出しの代わりにユーザー入力を求める(手動で住所入力させる)、または次の手順を明示したメッセージを表示する、などがあります。
正直に扱ってください:実際に完了していないのに「完了」と表示してはいけません。
処理がユーザーリクエスト内で完了する必要がないなら、それをキューに押し込み素早く応答してください。一般的な候補はメール送信、CRMへの同期、レポート生成、アナリティクスイベントの投稿などです。
コアな操作については早く失敗させる(fail fast)。もしAPIがチェックアウトやアカウント作成の完了に不要なら、リクエストをブロックしないで注文を受け付け、その外部呼び出しはキューに入れて後で整理してください。APIが必須(例:支払い承認)の場合は、ユーザーを無駄に待たせずに素早く失敗を返し、明確なメッセージを表示してください。
ユーザーに見せる情報は内部で起きていることと一致するべきです:明確な状態(完了、保留、失敗)、守れる約束(領収書は今、確認は後で)、再試行の方法、UI上で見える記録(アクティビティログ、保留バッジ)など。
レート制限はプロバイダの「呼んでいいが頻度は控えてね」という合図です。思ったより早く到達します:トラフィックのスパイク、バックグラウンドジョブの同時実行、エラーでのループなどが原因です。
まず作成するリクエスト数を制御しましょう。可能ならバッチ処理、30〜60秒のキャッシュ(安全なら)を使い、クライアント側でスロットルしてプロバイダの許容より急にバーストしないようにします。
429 Too Many Requestsを受け取ったら、減速の合図として扱ってください。
Retry-After が提供されていれば従う。同時に、並列度を制限してください。単一のワークフロー(連絡先同期など)がすべてのワーカースロットを消費してログインやチェックアウトを枯渇させてはなりません。別プールや機能ごとのキャップが有用です。
すべてのサードパーティ呼び出しには障害時の計画が必要です。完璧は不要で、プロバイダが調子を崩した日に予測可能に振る舞うことが重要です。
その呼び出しが今失敗したらどうなるかを決めます。チェックアウト中の税計算は必須かもしれません。マーケティングのコンタクト同期は通常後回し可能です。この判断が以降の設定を決めます。
呼び出しタイプごとにタイムアウトを決め、一貫性を保ちます。その後リトライ予算を設けて遅いAPIを叩き続けないようにします。
何かを作成したり課金したりするリクエストには冪等性キーを付け、リクエスト記録を保存します。支払いリクエストがタイムアウトしても、リトライで二重課金してはいけません。追跡はサポートが「通ったか?」に答えるのにも役立ちます。
エラーが急増したら短期間そのプロバイダへの呼び出しを止めます。必須呼び出しでは明確な「再試行」経路を示し、後回し可能な呼び出しはキューに入れて後で処理します。
レイテンシ、エラー率、ブレーカーの開閉イベントを追跡します。単発のノイズではなく持続的な変化でアラートするように設定してください。
多くのAPI障害は最初は小さいです。アプリが最悪の反応をすると大きくなります:待ちすぎ、過剰なリトライ、同じワーカーを占有する、などです。
これらのパターンがカスケードを引き起こします:
小さな修正が大きな障害を防ぎます:一時的なエラー(タイムアウト、一部の429、一部の5xx)のみリトライし、バックオフとジッターで試行を上限まで制限する。タイムアウトは短く意図的に保ち、作成や課金を伴う操作には冪等性を必須にし、部分的障害を許容する設計にすること。
統合を本番に投入する前に、障害を想定した観点で素早く確認してください。以下に「はい」と答えられない項目があれば、サインアップ、チェックアウト、メッセージ送信などのコアワークフローに関してはリリースブロッカーとして扱ってください。
決済プロバイダがタイムアウトし始めたら、正しい振る舞いは「チェックアウトは読み込まれ、ユーザーに明確なメッセージを表示し、永遠に待たせない」ことであり、「すべてがタイムアウトするまで固まる」ことではありません。
チェックアウトが三つのサービスを呼ぶと想像してください:カードを課金する決済API、税額を計算する税API、領収書を送るメールAPI。
決済呼び出しだけが同期で必須です。税やメールの問題で購入が詰まってはいけません。
税APIが時々8〜15秒かかるとします。もしチェックアウトが待つと、ユーザーはカートを放棄し、ワーカーが詰まります。
安全な流れの例:
結果:税プロバイダが遅くてもカート放棄や詰まった注文が減ります。
領収書メールは重要ですが、支払い確定をブロックしてはいけません。メールAPIが失敗しているなら、数回の短い失敗後にサーキットブレーカーは開き、短いクールダウン期間その呼び出しを止めるべきです。
メールをインラインで送る代わりに、send receipt ジョブをキューに入れ、冪等性キー(例:order_id + email_type)を付けます。プロバイダがダウンしている場合はキューがバックグラウンドでリトライし、顧客には購入成功が表示されます。
結果:確認メールが届かないことでのサポート件数が減り、非決済要因でチェックアウトが失敗して収益を失うことがなくなります。
最も壊れたときに痛いワークフロー(チェックアウト、サインアップ、請求)を一つ選び、それを参照用の統合にします。次に同じデフォルトを他の箇所にコピーしてください。
簡単な導入順:
デフォルトを文書化して平凡に保ってください:1つの接続タイムアウト、1つのリクエストタイムアウト、最大リトライ回数、バックオフ範囲、ブレーカーのクールダウン、そして何がリトライ可能かのルール。
次のワークフローに拡張する前にフェイルドリルを実施してください。テスト環境でタイムアウトを強制(またはプロバイダをブロック)し、ユーザーが有用なメッセージを見るか、フォールバックが機能するか、キューのリトライが無限に溜まらないかを確認します。
新しいプロダクトを素早く作る場合、これらの信頼性デフォルトを再利用可能なテンプレートにする価値があります。Koder.ai (koder.ai) を使うチームでは、タイムアウト、リトライ、冪等性、ブレーカーのルールを一度定義しておき、新しいサービスに対して同じパターンを適用することが多いです。