Azure FunctionsのService Busトリガーでリトライ嵐を防ぐ:Exponential backoffとcircuit breakerの実装ポイント

Service BusトリガーのAzure Functionsで外部APIやDBが一時的に遅くなると、関数のスケールアウトと即時再試行が重なり、障害をさらに広げることがあります。2026年5月15日に確認されたMicrosoft公式情報の要点は、Exponential backoff and circuit breaker for Service Bus-triggered Azure Functionsを使い、リトライ嵐を抑え、依存先を守り、処理不能なメッセージを安全に退避させることです。これは「Azure Functionsの挙動が自動的に変わる」というより、Service Bus SDK bindingsを使って開発者が明示的に実装すべき耐障害性パターンの整理と捉えると分かりやすいでしょう。(Microsoft for Developers)

結論から言うと、管理者と開発者がまず確認すべきポイントは、autoCompleteMessages、明示的なメッセージ完了処理、リトライ回数を保持するapplicationProperties、DLQまたは隔離キュー、サーキットブレーカー状態の保存先、maxConcurrentCallsなどの同時実行制御です。特に、本番環境ではインメモリのサーキットブレーカーだけに頼らず、複数インスタンスで共有できる状態ストアを使う必要があります。(Microsoft for Developers)

目次

Azure Functionsの「Exponential backoff and circuit breaker」は何を示しているか

MicrosoftのAzure SDK Blogでは、Service BusトリガーのAzure Functionsで、以下の2つのパターンを組み合わせる設計が紹介されています。

パターン役割Azure Functionsでの意味
Exponential backoff次の再試行をいつ行うかを制御する失敗したメッセージをすぐ再実行せず、遅延を増やしながら再投入する
Circuit breaker今その依存先を呼び出してよいかを判断する外部APIやDBが不安定な間は呼び出しを止め、無駄な実行と障害拡大を防ぐ

Exponential backoffは「時間をずらす」仕組みです。たとえば、1回目は数秒後、2回目は十数秒後、3回目は数十秒後のように、失敗回数に応じて再試行間隔を広げます。これにより、同じタイミングで大量のメッセージが再実行される状態を避けやすくなります。

一方、Circuit breakerは「そもそも呼び出すべきか」を判断する仕組みです。依存先のエラー率やタイムアウトが一定以上になったら回路を開き、一定時間は呼び出しを止めます。回復期間後に少数の試行を許可し、正常化していれば通常処理へ戻します。公式情報でも、backoffはメッセージ単位の再試行制御、circuit breakerは依存先単位の保護として位置付けられています。(Microsoft for Developers)

これは破壊的変更ではなく、実装パターンの強化として考える

今回のポイントは、Azure FunctionsのService Busトリガーに対して、プラットフォーム側が一律に新しい再試行挙動を強制するという話ではありません。むしろ、Service Bus SDK bindingsを使い、メッセージの状態、再試行回数、完了、放棄、dead-letterなどをアプリケーション側で明示的に制御する設計指針です。

公式ブログでは、TypeScript、Python、.NETの例が示され、Service Busメッセージのメタデータからリトライ回数を読み取り、遅延再投入やdead-letterを行う流れが紹介されています。TypeScriptではAzure Functions v4 programming modelとService Bus SDK binding、PythonではPython v2 programming model、.NETではisolated worker modelを前提にした例が扱われています。(Microsoft for Developers)

開発チームが押さえるべき変更点は、次の3つです。

観点従来よくある実装今回推奨される考え方
リトライ例外を投げて即時再実行に任せるメッセージごとに再試行回数と次回実行時刻を管理する
障害時の呼び出し依存先が落ちていても毎回呼び出すサーキットブレーカーで呼び出し自体を止める
失敗メッセージ何度も処理され、原因が見えにくいDLQまたは隔離キューに移し、調査・再実行手順を分ける

特に重要なのは、「リトライするかどうか」と「依存先を呼び出すかどうか」を分けて考えることです。backoffだけでは、依存先が明らかに落ちている状態でも最初の呼び出しは発生します。circuit breakerだけでは、メッセージ単位の再試行タイミングを細かく制御できません。両方を組み合わせることで、Functionsアプリ、Service Bus、依存先システムの全体を守る設計になります。

対象になるシステムと影響範囲

影響を受けるのは、Service BusキューまたはトピックをトリガーにしてAzure Functionsを実行し、その中で外部API、データベース、SaaS、マイクロサービス、社内システムなどを呼び出しているワークロードです。

具体的には、次のようなシステムで優先度が高くなります。

対象システム起きやすい問題対策の優先度
注文・決済・在庫連携外部APIのタイムアウト時に同じ処理が集中する高い
データ連携バッチ大量メッセージが同時に再試行される高い
通知・メール・Webhook配信送信先のレート制限にぶつかる高い
AI APIや外部SaaS呼び出し429や一時的な遅延で処理が詰まる高い
単純な内部キュー処理依存先が軽く、失敗頻度が低い中程度

Azure Functionsはスケールアウトしやすい反面、弱っている依存先へ複数インスタンスから同時にアクセスしてしまうことがあります。公式ブログでも、Functionインスタンスが即時かつ無制限に再試行すると、一時的な障害が広範囲の障害へ変わるリスクがあると説明されています。(Microsoft for Developers)

一方、HTTPトリガーだけのFunctions、Service Busを使っていないFunctions、依存先を呼ばない単純な変換処理では、今回の設計をそのまま適用する必要はありません。ただし、リトライ嵐、同時実行、DLQ監視という考え方は、他のイベント駆動システムにも応用できます。

Service Bus SDK bindingsで何を制御するのか

Service Bus SDK bindingsを使うと、単なる文字列やJSONとしてメッセージを受け取るだけでなく、ServiceBusReceivedMessageServiceBusMessageActionsなどを使って、メッセージの完了、放棄、dead-letter、メタデータ参照といった処理をより細かく扱えます。Microsoft Learnでも、Service Busトリガーの高度なシナリオとして、メッセージsettlement、セッション、トランザクションなどの用途にSDK型が使えることが示されています。(Microsoft Learn)

今回の設計で特に使うのは、次の要素です。

要素用途
applicationPropertiesretryCountoriginalMessageIdscheduledAtなどの再試行メタデータを保持する
ServiceBusMessageActionscompleteabandondeadletterなどを明示的に実行する
scheduled message次回処理時刻を指定してメッセージを再投入する
DLQまたは隔離キュー最大リトライ後のメッセージを通常処理から切り離す
共有状態ストアサーキットブレーカーの状態を複数インスタンスで共有する

ここで注意したいのは、Service Busクライアント自体のリトライ設定と、業務処理の再試行は別物という点です。host.jsonclientRetryOptionsはService Busサービスとのやり取りに対するリトライであり、関数実行や外部API呼び出しのリトライを直接制御するものではありません。(Microsoft Learn)

Exponential backoffの実装ポイント

Exponential backoffを実装する基本の流れは、次のようになります。

手順処理内容実装上の注意点
メッセージ受信Service Busトリガーでメッセージを受け取るapplicationPropertiesから現在のリトライ回数を読む
依存先呼び出しAPIやDBなどを呼び出すタイムアウト、429、5xxなどを一時障害として分類する
失敗判定一時障害か恒久障害かを判断するバリデーションエラーは再試行しない
遅延計算再試行間隔を指数的に増やす本番ではjitterを加えて再試行タイミングを分散する
再投入scheduled messageとしてService Busへ送る元メッセージの相関IDや重要なメタデータを引き継ぐ
元メッセージ完了現在のメッセージをcompleteする二重処理に備えて冪等性を確保する
上限超過最大リトライ回数を超えたらDLQまたは隔離キューへ送る原因調査と再実行フローを用意する

遅延時間は、たとえば次のような考え方で決めます。

次回遅延 = min(最大遅延, 基準遅延 × 2 ^ リトライ回数) + jitter

たとえば、基準遅延を5秒、最大遅延を5分に設定すると、再試行間隔は5秒、10秒、20秒、40秒のように広がります。ただし、すべてのメッセージが同じ計算式だけで動くと再試行タイミングがそろうため、数百ミリ秒から数秒程度のランダムなjitterを入れるのが実務上は有効です。公式ブログでも、本番環境では同期的な再試行を避けるためにjitterを追加することが推奨されています。(Microsoft for Developers)

固定間隔リトライとの違い

固定間隔リトライは実装が簡単ですが、障害時に全メッセージが同じ周期で再実行されやすくなります。たとえば、外部APIが30秒間不安定になり、全メッセージを10秒ごとに再試行すると、10秒ごとに同じ依存先へアクセスが集中します。

Exponential backoffでは、失敗が続くほど次回実行を後ろへずらします。これにより、依存先の回復時間を確保しつつ、Functions側の無駄な実行回数も抑えやすくなります。

Circuit breakerの実装ポイント

Circuit breakerは、依存先が不安定なときに「呼び出さない」という判断を入れるための仕組みです。一般的には、次の3状態で考えます。

状態意味Functionsでの動き
Closed通常状態依存先を呼び出す
Open障害状態依存先を呼び出さず、メッセージを遅延再投入または退避する
Half-open回復確認状態少数の試行だけ許可し、成功すればClosedへ戻す

Service BusトリガーのAzure Functionsでは、サーキットブレーカーの状態をどこに保存するかが重要です。ローカル開発や単一インスタンスのデモではインメモリでも動きますが、本番環境ではFunction Appが複数インスタンスにスケールアウトするため、プロセス内メモリでは全体の状態を共有できません。公式ブログでは、本番ではAzure Managed Redis、Cosmos DB、その他の低遅延な共有ストアを使うことが推奨されています。(Microsoft for Developers)

実務では、次のようなキーで状態を分けると運用しやすくなります。

キーの単位向いているケース
依存先API単位特定の外部API全体が不安定になる場合
テナント単位一部顧客・一部接続先だけが制限される場合
エンドポイント単位APIの一部機能だけが落ちる場合
リージョン単位特定リージョンの依存先だけが不安定になる場合

たとえば、在庫APIだけが不安定な場合に、決済APIやメール送信まで止める必要はありません。サーキットブレーカーは広すぎると正常処理まで止め、狭すぎると管理が複雑になります。まずは「依存先サービス単位」で始め、障害パターンに応じて細分化するのが現実的です。

autoCompleteMessagesは必ず確認する

Service Busメッセージをコード側で明示的にcompleteabandondeadletterする場合、autoCompleteMessagesを無効にする必要があります。Microsoft Learnでも、ServiceBusMessageActionsを使う場合は、トリガー属性のAutoCompleteMessagesfalseに設定し、ランタイムが成功時に自動完了しようとするのを防ぐ必要があると説明されています。(Microsoft Learn)

設定例は次のような形です。

{
  "version": "2.0",
  "extensions": {
    "serviceBus": {
      "autoCompleteMessages": false,
      "maxConcurrentCalls": 8,
      "prefetchCount": 0,
      "clientRetryOptions": {
        "mode": "exponential",
        "delay": "00:00:01",
        "maxDelay": "00:00:30",
        "maxRetries": 3
      }
    }
  }
}

この例で注意すべき点は、clientRetryOptionsが外部APIのリトライ設定ではないことです。これはAzure Functions拡張機能がService Busサービスと通信する際のリトライです。外部APIやDBに対するExponential backoffは、関数コード側でメッセージの再投入や遅延を制御して実装します。(Microsoft Learn)

また、maxConcurrentCallsは1インスタンスあたりの同時コール数を制限する設定です。依存先がレート制限を持つ場合は、backoffやcircuit breakerだけでなく、同時実行数そのものを下げる必要があります。Microsoft Learnでは、既定で複数メッセージを同時処理し、maxConcurrentCallsでスケール済みインスタンスごとのコールバック同時数を制限できることが説明されています。(Microsoft Learn)

管理者と開発者が確認すべき設定

本番適用前に、次の項目を確認してください。

確認項目推奨される確認内容失敗しやすいポイント
autoCompleteMessages手動settlementを行う場合はfalseにする自動完了と手動完了が混在し、想定外の完了・再配信が起きる
applicationPropertiesretryCountoriginalMessageIdscheduledAtを記録するリトライ履歴が追えず、DLQ調査が困難になる
最大リトライ回数業務影響に応じて上限を決める無制限リトライで障害調査が難しくなる
jitter再試行時刻にランダム性を加える全メッセージが同じ時刻に再実行される
maxConcurrentCalls依存先の処理能力に合わせて下げるFunctionsが速すぎて依存先を圧迫する
prefetchCount大量先読みが必要か確認するロック時間や処理時間と合わず、再配信が増える
maxAutoLockRenewalDuration長時間処理に耐えられるか確認する処理中にロックが失われ、重複処理が発生する
DLQ設定調査・再投入の運用手順を決めるDLQに入ったまま放置される
サーキットブレーカー状態RedisやCosmos DBなど共有ストアを使うインスタンスごとに状態が分裂する
監視キュー長、DLQ数、リトライ回数、依存先レイテンシを見るFunction成功率だけを見て障害を見逃す

Service Busでは、メッセージが処理されずに戻り続けると、既定では一定回数後にdead-letter queueへ移動します。Microsoft Learnでは、既定のMaxDeliveryCountは10で、この値がdead-letterへの移動に関係することが説明されています。(Microsoft Learn)

DLQと隔離キューはどう使い分けるか

最大リトライ回数を超えたメッセージは、DLQまたは隔離キューに移します。どちらも有効ですが、運用の目的が少し違います。

選択肢向いているケース注意点
DLQService Bus標準の失敗メッセージ管理を使いたい再投入や修正の手順を別途整備する必要がある
隔離キューアプリ独自の調査、修正、再実行フローを作りたい通常キューとは別に監視・権限・TTLを設計する必要がある

公式ブログでも、DLQはブローカー側に失敗メッセージの扱いを任せたい場合に自然な選択であり、隔離キューはアプリケーション側で独自の検査・再実行ワークフローを持ちたい場合に有効とされています。(Microsoft for Developers)

たとえば、入力データの一部を人が修正して再処理する業務では、隔離キューのほうが扱いやすい場合があります。一方、単純に処理不能メッセージを切り離して後から確認するだけなら、DLQのほうがシンプルです。

実装時の安全な処理順序

Exponential backoffで再投入する場合、失敗したメッセージをそのまま放置するのではなく、新しいscheduled messageを作成し、現在のメッセージを完了する流れになります。

基本フローは次のとおりです。

1. メッセージを受信する
2. retryCountを読む
3. サーキットブレーカーの状態を確認する
4. 呼び出し可能なら依存先を呼び出す
5. 一時障害ならretryCountを増やして遅延再投入する
6. 再投入後、現在のメッセージをcompleteする
7. 上限超過または恒久障害ならDLQまたは隔離キューへ移す

ここで最も注意すべきなのは、二重処理とメッセージ喪失です。再投入に成功したが元メッセージのcompleteに失敗した場合、同じ処理が重複する可能性があります。逆に、元メッセージを先にcompleteしてから再投入に失敗すると、処理対象が失われる可能性があります。

このリスクをゼロに近づけるには、次の対策が必要です。

対策内容
冪等性キーを持つoriginalMessageIdや業務IDで同じ処理を重複実行しない
状態更新を分ける外部API成功後のDB更新などをトランザクション単位で整理する
ログに相関IDを残すcorrelationIdoriginalMessageIdretryCountを必ず出す
例外分類を明確にする一時障害、恒久障害、データ不備を分ける
ステージングで障害注入するAPIタイムアウト、429、Service Bus送信失敗を意図的に試す

Azure Functionsのエラー処理ガイダンスでも、メッセージ処理では重複処理を避けるために冪等性を設計する重要性が示されています。(Microsoft Learn)

移行・展開時の注意点

既存のService Busトリガー関数にこの設計を入れる場合、いきなり全本番トラフィックへ適用しないでください。まずは対象関数、依存先、キュー設定を棚卸しし、失敗時の現状を把握することが重要です。

フェーズ実施内容判断基準
棚卸しService Busトリガー関数、依存先、host.json、DLQ数を確認するどの関数がリトライ嵐を起こし得るか分かる
観測追加retryCount、依存先レイテンシ、DLQ数をログ・メトリック化する変更前後を比較できる
小規模実装1つのキューまたは低リスク処理にbackoffを入れる重複処理や喪失がない
サーキットブレーカー追加共有状態ストアを使って依存先単位で制御する複数インスタンスで同じ判断ができる
カナリア展開一部メッセージまたは一部環境で有効化するキュー長と依存先負荷が悪化しない
本番展開同時実行数、リトライ上限、監視アラートを調整するDLQ増加時の運用手順がある

host.jsonの設定は環境ごとに変えたい場合があります。Azure Functionsでは、AzureFunctionsJobHost__path__to__setting形式のアプリ設定でhost.json値を上書きできます。これにより、ソースコード上のhost.jsonを変更せず、本番だけ同時実行数や監視設定を調整できます。(Microsoft Learn)

また、古いService Bus SDKを使っている場合は、リトライ設計とは別に移行計画も確認してください。Microsoft Learnでは、WindowsAzure.ServiceBusMicrosoft.Azure.ServiceBuscom.microsoft.azure.servicebusなどの古いAzure Service Bus SDKライブラリとSBMPプロトコルが2026年9月30日にサポート終了となることが案内されています。(Microsoft Learn)

RBACと接続設定も見直す

Service BusトリガーでマネージドIDを使う場合、Azureリソースの所有者権限だけでは実行時アクセスとして不十分な場合があります。Microsoft Learnでは、Service Bus拡張機能の通常運用で、トリガーにはAzure Service Bus Data ReceiverまたはAzure Service Bus Data Owner、出力バインディングにはAzure Service Bus Data Senderが例として示されています。(Microsoft Learn)

今回の設計では、関数がメッセージを受信するだけでなく、再試行用メッセージを送信したり、DLQまたは隔離キューを扱ったりする可能性があります。そのため、最小権限を守りつつ、実際に必要な操作権限があるかを確認してください。

処理必要になりやすい権限
Service Busメッセージの受信Data Receiver
再試行メッセージの送信Data Sender
DLQや隔離キューへの移動実装方式に応じた受信・送信・管理権限
トピックサブスクリプションのトリガーサブスクリプションリソースに対する有効なスコープ

特にトピックを使う場合、ロール割り当てのスコープがトピックだけでは不十分で、Service Busサブスクリプションリソースに対して有効である必要があります。権限不足はリトライ設計以前に、関数が起動しない、メッセージが処理されない、再投入できないといった障害につながります。(Microsoft Learn)

監視すべきメトリック

Exponential backoffとcircuit breakerを導入した後は、Functionの成功率だけを見ても十分ではありません。むしろ、遅延再投入や保護動作が正しく働いているかを見る必要があります。

メトリック見るべき理由
アクティブメッセージ数処理が追いついているかを確認する
scheduled message数backoffによる遅延再投入が増えすぎていないかを見る
DLQ数恒久障害やリトライ上限超過を検知する
retryCount分布何回目のリトライで成功しているかを把握する
依存先レイテンシサーキットブレーカーのしきい値設計に使う
429、5xx、タイムアウト数一時障害の種類を分類する
breaker状態Openが長時間続いていないかを確認する
Function実行時間ロック更新時間やタイムアウト設定と合っているかを見る

公式ブログでも、breaker状態、retry count、queue depth、dead-letter count、downstream latencyをApplication Insightsで追跡することが推奨されています。(Microsoft for Developers)

アラートは「Functionが失敗した」だけでなく、「DLQが増えた」「Open状態が一定時間続いた」「リトライ3回以上のメッセージが急増した」のように、運用者が次の行動を判断できる条件にします。

よくある失敗パターン

autoCompleteMessagesを無効にしていない

コードでcompletedeadletterを呼んでいるのに、ランタイム側の自動完了も有効なままだと、メッセージsettlementの責任範囲が曖昧になります。明示的に制御する設計に切り替えるなら、autoCompleteMessagesの設定を最初に確認してください。

すべての例外をリトライ対象にする

一時的なネットワーク障害や429はリトライ対象になり得ますが、入力データ不備、存在しない顧客ID、権限不足、仕様違反のJSONなどは、何度再試行しても成功しない可能性が高いです。こうした恒久障害は早めにDLQまたは隔離キューへ移します。

サーキットブレーカーをインメモリで本番運用する

Azure Functionsは複数インスタンスで動くため、インメモリ状態ではインスタンスごとに判断が分かれます。あるインスタンスではOpen、別のインスタンスではClosedのままになり、依存先保護として不十分です。

maxConcurrentCallsを見直さない

backoffを入れても、同時実行数が高すぎると、通常処理や回復直後の試行で依存先に強い負荷がかかります。依存先のレート制限やDB接続上限に合わせて、同時実行数を調整する必要があります。

DLQを作って終わりにする

DLQは失敗メッセージの保管場所であり、解決策そのものではありません。誰が確認するのか、どの条件で再投入するのか、再投入前にデータ修正が必要か、再投入時に同じ障害を繰り返さないかを決めておく必要があります。

まず何から始めるべきか

最初に取り組むべきなのは、Service BusトリガーのAzure Functionsのうち、外部API、DB、SaaS、他システムを呼び出している関数を洗い出すことです。そのうえで、autoCompleteMessagesmaxConcurrentCallsMaxDeliveryCount、DLQの運用、Application Insightsのログ項目を確認してください。

次に、失敗時の処理を「一時障害」「恒久障害」「依存先停止」の3つに分けます。一時障害にはExponential backoff、依存先停止にはCircuit breaker、恒久障害にはDLQまたは隔離キューを使います。この分類ができていないと、リトライ回数や待機時間をどれだけ調整しても、障害時の挙動は安定しません。

本番導入では、小さなキューまたは低リスクな処理から始め、リトライメタデータ、DLQ数、依存先レイテンシを見ながら調整するのが安全です。Azure Functionsの強みであるスケール性能は、障害時には依存先を圧迫する力にもなります。Exponential backoff and circuit breaker for Service Bus-triggered Azure Functionsは、そのスケール性能を安全に使うための実装パターンとして、Service Bus連携の標準設計に組み込む価値があります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次