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としてメッセージを受け取るだけでなく、ServiceBusReceivedMessageやServiceBusMessageActionsなどを使って、メッセージの完了、放棄、dead-letter、メタデータ参照といった処理をより細かく扱えます。Microsoft Learnでも、Service Busトリガーの高度なシナリオとして、メッセージsettlement、セッション、トランザクションなどの用途にSDK型が使えることが示されています。(Microsoft Learn)
今回の設計で特に使うのは、次の要素です。
| 要素 | 用途 |
|---|---|
applicationProperties | retryCount、originalMessageId、scheduledAtなどの再試行メタデータを保持する |
ServiceBusMessageActions | complete、abandon、deadletterなどを明示的に実行する |
| scheduled message | 次回処理時刻を指定してメッセージを再投入する |
| DLQまたは隔離キュー | 最大リトライ後のメッセージを通常処理から切り離す |
| 共有状態ストア | サーキットブレーカーの状態を複数インスタンスで共有する |
ここで注意したいのは、Service Busクライアント自体のリトライ設定と、業務処理の再試行は別物という点です。host.jsonのclientRetryOptionsは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メッセージをコード側で明示的にcomplete、abandon、deadletterする場合、autoCompleteMessagesを無効にする必要があります。Microsoft Learnでも、ServiceBusMessageActionsを使う場合は、トリガー属性のAutoCompleteMessagesをfalseに設定し、ランタイムが成功時に自動完了しようとするのを防ぐ必要があると説明されています。(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にする | 自動完了と手動完了が混在し、想定外の完了・再配信が起きる |
applicationProperties | retryCount、originalMessageId、scheduledAtを記録する | リトライ履歴が追えず、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または隔離キューに移します。どちらも有効ですが、運用の目的が少し違います。
| 選択肢 | 向いているケース | 注意点 |
|---|---|---|
| DLQ | Service 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を残す | correlationId、originalMessageId、retryCountを必ず出す |
| 例外分類を明確にする | 一時障害、恒久障害、データ不備を分ける |
| ステージングで障害注入する | 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.ServiceBus、Microsoft.Azure.ServiceBus、com.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を無効にしていない
コードでcompleteやdeadletterを呼んでいるのに、ランタイム側の自動完了も有効なままだと、メッセージsettlementの責任範囲が曖昧になります。明示的に制御する設計に切り替えるなら、autoCompleteMessagesの設定を最初に確認してください。
すべての例外をリトライ対象にする
一時的なネットワーク障害や429はリトライ対象になり得ますが、入力データ不備、存在しない顧客ID、権限不足、仕様違反のJSONなどは、何度再試行しても成功しない可能性が高いです。こうした恒久障害は早めにDLQまたは隔離キューへ移します。
サーキットブレーカーをインメモリで本番運用する
Azure Functionsは複数インスタンスで動くため、インメモリ状態ではインスタンスごとに判断が分かれます。あるインスタンスではOpen、別のインスタンスではClosedのままになり、依存先保護として不十分です。
maxConcurrentCallsを見直さない
backoffを入れても、同時実行数が高すぎると、通常処理や回復直後の試行で依存先に強い負荷がかかります。依存先のレート制限やDB接続上限に合わせて、同時実行数を調整する必要があります。
DLQを作って終わりにする
DLQは失敗メッセージの保管場所であり、解決策そのものではありません。誰が確認するのか、どの条件で再投入するのか、再投入前にデータ修正が必要か、再投入時に同じ障害を繰り返さないかを決めておく必要があります。
まず何から始めるべきか
最初に取り組むべきなのは、Service BusトリガーのAzure Functionsのうち、外部API、DB、SaaS、他システムを呼び出している関数を洗い出すことです。そのうえで、autoCompleteMessages、maxConcurrentCalls、MaxDeliveryCount、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連携の標準設計に組み込む価値があります。

コメント