フォールバック処理をどう設計するかという視点

障害対応の話になると、フォールバック(失敗時の代替動作)ってかなり早い段階で話題に上がります。外部APIが落ちた、DBが重い、ネットワークが不安定、そんなときに「どうする?」を決めるためです。

ただ、ここで一つ落とし穴があります。フォールバックを「技術的にできること」だけで決めると、後から業務や運用、コンプライアンスの観点で詰みます。

たとえば「キャッシュを返せばOK」「失敗したら後払いにしよう」みたいな案は、システムとしては動き続けます。でも業務領域によっては、規約違反や法令上の問題、監査で突っ込まれるポイントになり得ます。つまりフォールバックは、コードの話に見えて、実はプロダクトの意思決定でもあります。

この記事では、フォールバック処理を「どう書くか」ではなく、どう設計するかに軸を置いて解説します。さらに、指摘のあった「ビジネス側との合意」や「復旧後の整合性修復(戻し方)」を、使えるレベルまで掘り下げてみます。

目次

概要:フォールバック設計で最初に押さえるべき全体像

フォールバック設計を一言でいうとこうです。

失敗したときに、ユーザー体験と業務要件を壊さずに、サービスをどう動かし続けるかを決めること

ポイントは「動かし続ける」だけじゃありません。動かし続けた結果、業務が破綻したり、後で整合性が取れなくなったり、監査に耐えなくなったら意味がありません。

なので、設計の最初に次の三点を決めるのがおすすめです。

1つ目は 何を守るか(優先順位)

2つ目は 何なら妥協できるか(縮退の許容範囲)

3つ目は 誰と合意しておくべきか(ビジネス・法務・運用)

技術だけで完結しない、と最初に腹落ちしておくと、後の設計がスムーズになります。

詳細解説:フォールバックを「設計」として組み立てる

フォールバック方針はビジネス側と合意が必要になる理由

「キャッシュを返す」「後で処理する」「別経路で受け付ける」などは便利な手段です。でも、業務領域によってはリスクになります。

代表的に揉めやすい観点を挙げます(当たり前のことかもしれませんが、、)。

正確性が必須の情報をキャッシュで返してよいか

在庫、価格、ポイント残高、利用制限、認可情報などは、古い値が混ざると事故につながります。ユーザーの画面上の表示だけでなく、裏側の業務処理まで誤る可能性があります。

キャッシュは強い武器ですが、「古くても問題ない情報」と「古いとアウトな情報」を分けるのが前提です。ここは技術者だけで判断せず、業務側と合意しておくのが安全です。

後回し処理や代替フローが規約・法令・監査に触れないか

「後で支払い」「後で確定」「一旦受け付ける」は、場合によっては契約条件や規約、監査要件に絡みます。決済や本人確認、権限付与などは特に慎重さが必要です。

大事なのは、「技術的に可能」ではなく、やってよい設計なのかを確認することです。フォールバック方針は、仕様の一部として扱うのが正攻法です。

障害時の案内文やUIも合意対象になる

「いま使えません」「後で反映されます」「一時的に制限中です」などの文言は、ユーザーへの約束になります。文言ひとつでクレームや問い合わせが増減しますし、場合によっては法務確認が必要になることもあります。

フォールバックは裏側の挙動だけでなく、表のコミュニケーションまで含めて設計する、が現場で強いです。

失敗パターンを分類すると設計が速くなる

フォールバックは「失敗したら何かする」ですが、失敗の種類で最適解が変わります。実務でよくある分類は次の通りです。

タイムアウト(遅すぎて待てない)

多いです。落ちてはいないけど遅い状態。ここで重要なのは、待ち続けないこと。タイムアウトが長いと、サーバ側のリソースが詰まり、連鎖的に全体が重くなります。

一時的な失敗(リトライで回復しがち)

ネットワーク瞬断など。リトライは有効ですが、やりすぎると相手に負荷をかけて障害を悪化させます。回数よりも間隔設計が大事です。

恒久的な失敗(復旧に時間がかかる)

依存先が障害中のケース。リトライより、縮退運転や即フォールバックで「待たない設計」に切り替える方が被害が小さくなります。

データ不正(成功に見えるが中身が壊れている)

これが地味に厄介です。返ってきた値が想定外で、画面や処理が壊れます。フォールバックというより、バリデーションと安全なデフォルト設計が重要になります。

フォールバックの代表パターンと「使ってよい領域」

ここは誤解されやすいので、「どういうときに使いやすいか」をセットで整理します。

デフォルト値に置き換える

プロフィール画像のプレースホルダ、ランキングの空欄回避など、失敗しても業務的に意味が変わらないところで有効です。

注意点は、0や空配列のような「正常に見える値」を返すと、失敗と区別できなくなることです。運用や分析で困るので、内部的には「フォールバックした」フラグを残す設計がよく使われます。

キャッシュを返す

ニュース、天気、コンテンツ一覧など、「多少古くても許される」情報に強いです。一方で、在庫や価格、権限など「正確性が必須」の領域は危険です。ここはビジネス合意が必要になりやすいポイントです。

縮退運転する

「購入はできるが、おすすめは出さない」「検索は止めてカテゴリ導線だけにする」など、重要機能を守るために周辺機能を落とす設計です。障害時の生存率が上がります。

処理を後ろ倒しにして受け付ける

業務的に許される範囲なら強い手です。ただし、許されるかどうかが領域依存なので、合意と監査観点がセットになります。

ここからが本題:フォールバック後の整合性修復をどう設計するか

フォールバックでその場をしのげても、後で整合性が崩れていたら結局事故です。そこで重要になるのが「戻し方」です。

戻し方は大きく分けると 自動リカバリ再実行(イベント再送) です。ここを押さえると、実務の設計が一段上がります。

自動リカバリの基本は「再試行」ではなく「復旧に合わせた再開」

障害中にずっとリトライし続けると、相手が復旧した瞬間にリクエストが殺到して再炎上します。

よくある設計は次の組み合わせです。

  • 指数バックオフ:失敗したら間隔を広げる
  • ジッター:全員が同時に再試行しないように少し揺らす
  • サーキットブレーカー:失敗が続くなら一定時間呼び出しを止める

この3つは、「障害を悪化させない」という意味でセット運用されがちです。

イベント再送設計で大事なのは「失敗を消さない」こと

「失敗したら諦める」だと、データ欠損になります。なので、失敗をキューやログとして保持し、後で再処理できるようにします。

よく使われる考え方は次の通りです。

  • 失敗したイベントはDLQ(失敗用の置き場)に送る
  • 復旧後にDLQから再処理する
  • 再処理は冪等(何回やっても同じ結果)にする

この「冪等」は初心者にはとっつきにくいですが、例え話で言うと「同じボタンを2回押しても1回分だけ処理される」状態です。再送がある世界では必須になります。

二重送信・二重処理を防ぐには「冪等キー」が効く

イベント再送やリトライをやると、同じ処理が複数回走る可能性が出ます。そこで、処理単位に一意なキーを持たせて「このキーはもう処理済み」を判定できるようにします。

例えば「注文確定」なら注文ID、「メール送信」なら送信要求IDのような感じです。これがあると、再送しても安全になります。

コードはpython風に雰囲気だけ出すとこんなイメージです。(※あくまで風なので動きません。)

if already_processed(idempotency_key):
    return OK
process()
mark_processed(idempotency_key)

本質は、再試行しても壊れないようにしておくことです。

Outboxパターンで「DB更新とイベント送信のズレ」を防ぐ

実務でありがちな事故がこれです。

  • DB更新は成功した
  • でもイベント送信に失敗した
  • その結果、別サービスが更新を知らない

これを避けるために、DB更新と同じトランザクション内で「送信予定イベント」をOutboxテーブルに書き、後から確実に送る設計があります。厳密な説明を省くと、要は「送るべきものをまず台帳に書く」方式です。

例えるなら、荷物を送る前に発送伝票を控えに残す感じです。配送が失敗しても、控えがあるからやり直せます。

Sagaで「跨る処理」を段階的に整合させる

複数サービスにまたがる処理では、全部を一気に確定できないことが多いです。そこで「段階的に進め、失敗したら補償処理で戻す」設計が使われます。

フォールバックで縮退した結果、途中で止まった処理があるなら、どこまで進んだかを状態として管理し、補償(取り消し)か再開を選べるようにするのが現実的です。

ここまで来ると難しそうに見えますが、考え方はシンプルです。

  • 途中で止まるのは前提
  • 止まった状態を記録する
  • 再開か取り消しができるようにする

「戻し方」を設計するときのチェック観点

戻し方は抽象的になりやすいので、設計時に見ておくと事故りにくい観点をまとめます。

  • フォールバックで受け付けた処理はどこに残るか(台帳、キュー、DLQなど)
  • 再処理はいつ走るか(定期、手動、復旧検知など)
  • 再処理が二重実行されても壊れないか(冪等性)
  • 再処理が失敗し続けたときの置き場はあるか(DLQ)
  • ユーザーへの表示はどう変わるか(保留中、反映待ちなど)
  • 監視はどうするか(フォールバック発生、DLQ滞留、再処理失敗率など)

このあたりを事前に決めておくと、障害時に「戻せない」「調べられない」「誰も気づかない」の三重苦を避けられます。

実務でのポイント:フォールバック設計を現場で回すコツ

優先順位を明文化して、例外対応を減らす

機能を次のように分類しておくと、設計判断が速くなります。

  • 絶対に落とせない(決済、認証、重要な更新)
  • 落ちると不便だが致命傷ではない(検索、通知)
  • 落ちても体験を大きく壊さない(おすすめ、装飾)

この分類があると、「この機能は縮退OK」「ここは明示エラー」と迷いにくくなります。

フォールバックは「静かに成功する」ので監視が必須

フォールバックが動くと、ユーザーからは正常に見えることがあります。だからこそ、運用側が気づく仕組みが必要です。

  • フォールバック発生回数
  • 依存先のタイムアウト率
  • DLQ滞留件数
  • 再処理失敗率

このあたりをメトリクスとして見られるようにすると、障害が「長引く」問題を減らせます。

UI文言は「正確さ」と「安心感」を両立させる

障害時の文言は、説明不足だと不安が増えますし、言い切りすぎると約束になってしまいます。

よく使われる落とし所は、「状況」「影響」「次の行動」を短く揃えることです。

  • 現在、一部機能が利用しづらい状態です
  • 影響:更新の反映に遅れが出ることがあります
  • 対応:時間をおいて再度お試しください

このレベルでも問い合わせは減ります。

テストは「成功ケース」より「失敗ケース」を意図的に作る

フォールバックは普段動かないので、本番で初めて動くと壊れがちです。

  • タイムアウトさせる
  • 依存先を落とす
  • キャッシュを空にする
  • DLQを詰まらせる

こういう失敗を意図的に作って、画面・ログ・監視が想定通りかを確認するのが、実務では効きます。

一問一答:設計で迷いやすいところだけ短く

キャッシュフォールバックは基本的に良い手?

良い手ですが領域次第です。古くても許される情報には強い一方、在庫や価格、権限などは古い値が事故になるので、ビジネス側と合意して線引きするのが安全です。

後回し処理のフォールバックはいつ検討すべき?

ユーザー体験を守りつつ業務的に許されるなら有効です。ただし規約・法務・監査の観点が絡むことが多いので、設計段階で合意を取り、ユーザーへの案内と整合性修復(戻し方)までセットにします。

イベント再送って、結局リトライと同じ?

似ていますが狙いが違います。リトライは「その場で成功させたい」、再送は「失敗を記録して後で確実に処理する」です。再送があると、欠損を減らせます。

冪等性はなぜ必須?

再送やリトライがあると、同じ処理が複数回走る可能性が出ます。冪等性がないと二重課金や二重更新などの事故につながるので、現場では必須級の考え方です。

まとめ:フォールバックは「合意」と「戻し方」まで含めて完成する

フォールバック処理は、単に例外を拾って別の値を返すだけではありません。実務で本当に効くのは次の二点です。

1つ目は フォールバック方針をビジネス側と合意しておくこと

キャッシュや後回し処理は便利ですが、業務領域によってはリスクを伴います。技術的にできることと、やってよいことは別です。

2つ目は フォールバック後の整合性修復を設計しておくこと

自動リカバリ、サーキットブレーカー、DLQ、イベント再送、冪等性、Outbox、Sagaといった要素は、「戻せる」システムを作るための部品です。障害時に耐えるだけでなく、復旧後にきちんと正しい状態へ戻せることが、実務では強さになります。

フォールバックを「保険」ではなく「仕様」として扱う。さらに「その後どう戻すか」まで描く。この視点があるだけで、障害対応の質が一段上がります。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次