Azure Service Busのバッチ処理で「1件だけ失敗したのに、成功したメッセージまで再処理される」問題に悩んでいた開発者にとって、2026年4月28日のMicrosoft公式更新は重要です。今回のポイントは、Azure FunctionsのService Busトリガーで、バッチ内のメッセージを1件ずつ個別に完了・再試行・配信不能・延期できるよう整理されたことです。これにより、重複処理、無駄な再実行、毒メッセージによる足止めを減らしやすくなります。(Microsoft for Developers)
特に、Azure Service Busを使って注文処理、通知配信、データ連携、イベント駆動アプリケーションを構築している開発者や管理者は、従来の「バッチ全体を成功か失敗で扱う」設計を見直す価値があります。
Azure Service Busのバッチ処理で起きていた「全部やり直し」問題
Azure Service Busは、Azure上でキューやトピックを使ったメッセージングを実現するサービスです。Azure FunctionsのService Busトリガーをバッチモードで使うと、複数のメッセージをまとめて関数に渡せるため、高スループットの処理に向いています。
しかし、従来のバッチ処理では次のような問題が起きやすくなります。
たとえば、1回の関数実行で50件のメッセージを受け取ったとします。49件は正常に処理できたものの、1件だけ不正なデータで失敗した場合、バッチ全体が失敗扱いになると、成功済みの49件もキューに戻って再処理されます。Microsoftの公式記事でも、この「1件の失敗がバッチ全体を巻き戻す」パターンを、典型的なall-or-nothing batch failure problemとして説明しています。(Microsoft for Developers)
この設計では、実務上かなり厄介な副作用が出ます。
- 成功済みメッセージが再配信され、重複処理が発生する
- データベース更新、外部API呼び出し、メール送信などが二重実行される可能性がある
- 処理済みの作業を再実行するため、Functionsの実行時間や外部サービスの利用コストが増える
- 失敗し続ける1件の毒メッセージが、同じバッチ内の正常メッセージまで巻き込む
- すべての後続システムに強い冪等性設計を求める必要がある
冪等性は本来重要ですが、すべての処理で「何度実行されても完全に問題ない」状態を作るのは簡単ではありません。注文確定、請求、在庫引き当て、通知送信のように副作用がある処理では、重複処理を避ける設計のほうが現実的な場合もあります。
何が変わったか:バッチ内のメッセージを1件ずつsettlementできる
今回の更新で重要なのは、Azure FunctionsからService Busメッセージをバッチで受け取った場合でも、各メッセージに対して個別にsettlement、つまり処理結果の確定操作を行える点です。
Microsoft公式記事では、Azure FunctionsでService Bus message settlement actionsを使うことで、バッチ全体を一括で成功・失敗とみなすのではなく、メッセージごとの結果に応じて個別に扱えると説明されています。(Microsoft for Developers)
主な操作は次の4つです。
| 操作 | 何をするか | 実務での使いどころ |
|---|---|---|
| Complete | メッセージを正常処理済みとしてキューから削除する | 注文登録、ログ保存、通知送信などが成功した場合 |
| Abandon | ロックを解放し、メッセージを再試行可能な状態に戻す | DB接続エラー、外部APIの一時障害、タイムアウトなど |
| Dead-letter | メッセージを配信不能キューに移す | JSON形式不正、必須項目欠落、仕様外データなど |
| Defer | メッセージを通常受信から外し、シーケンス番号で後から取得できる状態にする | 前提データ待ち、手動確認待ち、順序制御が必要なケース |
たとえば、50件のメッセージを受け取った場合に、47件をComplete、2件をAbandon、1件をDead-letterにする、といった処理が1回の関数実行内で可能になります。公式記事でも、このように成功・一時エラー・毒メッセージを同じバッチ内で分けて扱える例が示されています。(Microsoft for Developers)
Complete、Abandon、Dead-letter、Deferをどう使い分けるか
個別settlementで失敗しやすいのは、「失敗したら全部Dead-letterに入れる」「迷ったらAbandonする」といった雑な判断です。メッセージごとの状態に応じて、どの操作を選ぶかを明確にしておく必要があります。
Completeは「副作用まで成功した後」に実行する
Completeは、メッセージをキューから削除する操作です。つまり、Complete後に同じメッセージは通常再処理されません。
注意点は、処理の途中でCompleteしないことです。たとえば、注文データをDBに保存し、その後に通知メールを送る処理で、DB保存直後にCompleteしてしまうと、メール送信に失敗してもService Bus側では処理済みになります。
Completeは、原則として「そのメッセージに対して必要な処理がすべて終わった後」に実行します。
Abandonは「再試行すれば成功する可能性がある失敗」に使う
Abandonは、メッセージのロックを解放し、再配信可能な状態に戻す操作です。Microsoft Learnでは、Service Busの受信処理において、処理成功後に削除できるか、失敗して再配信すべきかをクライアントとブローカーが確定する必要があると説明されています。(Microsoft Learn)
Abandonが向いているのは、次のような一時的な障害です。
- データベースへの接続が一時的に失敗した
- 外部APIが429や503を返した
- ネットワーク遅延でタイムアウトした
- 下流サービスが短時間だけ停止している
一方で、メッセージ本文の形式が壊れている、必須項目がない、存在しない顧客IDを参照しているなど、再試行しても成功しない可能性が高い場合はAbandonではなくDead-letterを検討します。
Dead-letterは「このままでは処理できないメッセージ」に使う
Dead-letterは、処理できないメッセージを配信不能キューに移す操作です。Microsoft Learnでは、配信不能キューの目的を、受信者に配信できないメッセージや処理できなかったメッセージを保持し、後から検査できるようにすることだと説明しています。(Microsoft Learn)
Dead-letterに入れるべき典型例は次のとおりです。
- JSONとしてパースできない
- 必須フィールドが欠けている
- スキーマバージョンが非対応
- 業務ルール上、処理対象にできない
- 再試行回数が上限に達した
Dead-letterを使う場合は、理由を残すことが重要です。メッセージID、エラー種別、対象の処理名、再送可否をログやアプリケーションプロパティに残しておくと、後でDLQから復旧する際に判断しやすくなります。
Deferは「今は処理しないが、後で明示的に取り出したい」場合に使う
Deferは、メッセージをキューに残したまま、通常の受信では取得されない状態にする操作です。後からシーケンス番号を使って取得します。
使いどころは限定的ですが、前提データがまだ到着していない場合や、別メッセージとの順序関係を保ちたい場合に有効です。
ただし、Deferしたメッセージは「後で取り出す仕組み」を自分たちで設計する必要があります。シーケンス番号を保存していないと、後から扱いにくくなります。安易にDeferを多用すると、見えにくい滞留メッセージが増えるため注意が必要です。
実務ではどんなシステムで効果が大きいか
個別settlementの効果が大きいのは、メッセージ処理の件数が多く、かつ失敗理由がメッセージごとに異なるシステムです。
| 活用シーン | よくある課題 | 個別settlementの効果 |
|---|---|---|
| 注文処理 | 1件の不正注文で正常注文まで再処理される | 正常注文だけCompleteし、不正注文をDead-letterに分離できる |
| メール・通知配信 | 一部ユーザーだけ宛先不正や配信失敗が起きる | 宛先不正はDead-letter、一時障害はAbandonで分けられる |
| データ連携 | 外部APIの一時障害とデータ不備が混在する | 障害種別に応じて再試行と隔離を切り分けられる |
| IoTデータ取り込み | センサーごとにデータ形式や遅延が異なる | 正常データは処理し、不正データだけ隔離できる |
| ログ・イベント処理 | 大量イベント中に一部だけ壊れたデータが混じる | バッチ全体の再処理を避け、スループットを維持しやすい |
特に、外部API呼び出しやDB更新などコストのかかる処理を含む場合、成功済みメッセージを再実行しないだけでも効果があります。公式記事でも、成功済み処理の再実行を避けられるため、高スループット環境ではコスト削減につながり得ると説明されています。(Microsoft for Developers)
実装時に押さえるべき設定ポイント
個別settlementを使うには、単にService Busトリガーを使うだけでは不十分です。自動完了を無効化し、関数側で明示的にメッセージごとの処理結果を確定する設計にします。
Microsoft Learnでは、ServiceBusMessageActionsを使用する場合、トリガー属性のAutoCompleteMessagesをfalseに設定する必要があると説明されています。これにより、関数の呼び出し成功後にランタイムが自動でメッセージ完了を試みなくなります。(Microsoft Learn)
設定の考え方
| 項目 | 推奨される考え方 |
|---|---|
| バッチ処理 | 複数メッセージをまとめて受け取り、処理効率を上げる |
| 自動完了 | 個別settlementを使う場合は無効化する |
| 成功時 | 各メッセージの処理完了後にCompleteする |
| 一時失敗時 | Abandonで再試行に回す |
| 恒久失敗時 | Dead-letterで隔離する |
| 後続条件待ち | 必要に応じてDeferする |
公式記事では、Node.js、Python、.NETの例が示されており、Node.jsではautoCompleteMessages: false、cardinality: 'many'、SDKバインディングを使った例が紹介されています。PythonではV2 programming model、.NETではC# Isolated Workerでの例が示されています。(Microsoft for Developers)
設計時の判断基準:再試行するか、隔離するか
個別settlementを導入すると、メッセージ単位で柔軟に扱える一方、判断基準が曖昧だと運用が複雑になります。実装前に、少なくとも次の分類を決めておくべきです。
| エラー種別 | 例 | 推奨アクション |
|---|---|---|
| 一時的な外部障害 | DB接続失敗、APIタイムアウト、429、503 | Abandon |
| データ形式エラー | JSON不正、必須項目不足、型不一致 | Dead-letter |
| 業務ルール違反 | 無効な注文状態、存在しない参照ID | Dead-letterまたは業務用の隔離キュー |
| 順序・依存関係待ち | 親データが未到着、前段イベント待ち | Defer |
| 正常処理 | 保存、通知、連携が完了 | Complete |
実務では、すべての例外をcatchしてDead-letterに送る実装は危険です。たとえば、一時的なDB障害までDead-letterに入れてしまうと、本来は再試行で復旧できたメッセージが手動復旧待ちになります。
逆に、すべてAbandonにすると、不正データが何度も再配信され、最大配信回数に達するまでリソースを消費します。最初にエラー分類を決め、ログやメトリックで可視化できるようにすることが重要です。
管理者が確認すべき運用ポイント
開発者だけでなく、Azure環境を管理する担当者も今回の変更を理解しておく必要があります。個別settlementは便利ですが、運用設計なしに導入すると、DLQの滞留や再試行ループを見逃しやすくなります。
DLQを定期的に監視する
Dead-letterを積極的に使う設計では、配信不能キューの監視が必須です。Microsoft Learnでは、DLQのメッセージは自動的にクリーンアップされず、明示的に取得して完了するまで残ると説明されています。(Microsoft Learn)
そのため、次のような運用を用意しておくと安全です。
- DLQ件数がしきい値を超えたら通知する
- Dead-letter理由を分類してダッシュボード化する
- 復旧可能なメッセージを再投入する手順を用意する
- 復旧不能なメッセージの削除判断を明文化する
再試行回数をアプリケーション側でも見えるようにする
Service Busには配信回数の概念がありますが、アプリケーション側でも再試行理由や試行回数を記録しておくと、運用判断がしやすくなります。
たとえば、Abandonする際にアプリケーションプロパティへ再試行回数や最後のエラー種別を残す設計にすると、「一時障害なのか、実は恒久エラーなのか」を後から判断しやすくなります。公式記事でも、Abandon時にアプリケーションプロパティを変更して再試行メタデータを扱える点に触れています。(Microsoft for Developers)
ロック時間と処理時間のバランスを見る
Service BusのPeek-Lockでは、受信したメッセージを処理中にロックします。処理が長引き、ロック期限を超えると、意図せず再配信される可能性があります。Microsoft Learnでも、ロックが失われた場合の例外や再配信に関する注意が説明されています。(Microsoft Learn)
バッチサイズを大きくしすぎると、後半のメッセージに到達するまで時間がかかります。結果として、Completeしようとした時点でロックが切れている、という失敗が起きることがあります。
バッチサイズは「大きいほど良い」ではありません。1件あたりの処理時間、外部APIの応答時間、ロック期間、再試行ポリシーを見て調整する必要があります。
開発者が実装で失敗しやすいポイント
autoCompleteMessagesをfalseにし忘れる
個別settlementでは、関数ランタイムが勝手にメッセージを完了しないようにする必要があります。autoCompleteMessagesやAutoCompleteMessagesを無効化していないと、手動settlementと自動完了が混在し、意図しない挙動につながります。
Completeのタイミングが早すぎる
外部API呼び出しやDB更新が残っているのにCompleteすると、後続処理に失敗しても再試行できません。Completeは「もう再配信されなくてよい」と判断できる最後の地点で実行します。
例外処理が粗すぎる
catch内で常にDead-letter、または常にAbandonにする実装は避けるべきです。例外の種類、HTTPステータス、入力データの検証結果を見て分岐させます。
DLQを作っただけで見ない
Dead-letterは「失敗をなかったことにする場所」ではありません。原因調査と再処理のための保管場所です。DLQを監視しない設計では、問題が表面化しにくくなります。
冪等性が不要になると誤解する
個別settlementにより重複処理のリスクは減りますが、完全になくなるとは限りません。ネットワーク障害、ロック切れ、Complete失敗などの可能性は残ります。重要な業務処理では、メッセージIDや業務キーを使った重複防止を引き続き設計すべきです。
既存システムで見直すべきチェックリスト
既存のAzure FunctionsとAzure Service Bus構成を使っている場合は、次の順番で確認すると効率的です。
| 確認項目 | 見るべきポイント |
|---|---|
| バッチ処理の有無 | cardinality="many"や配列受け取りを使っているか |
| 失敗時の挙動 | 1件失敗したときに成功済みメッセージまで再処理されていないか |
| 自動完了設定 | 手動settlementが必要な処理で自動完了が有効のままになっていないか |
| エラー分類 | 一時障害と恒久障害を分けているか |
| DLQ監視 | 件数、理由、滞留期間を監視しているか |
| 再試行設計 | 無限ループや過剰リトライを防ぐ仕組みがあるか |
| 処理時間 | バッチサイズとロック期間のバランスが取れているか |
| 冪等性 | 重複実行されても壊れない最低限の保護があるか |
このチェックで「失敗時に関数全体が例外をthrowして終わるだけ」という実装が見つかった場合は、個別settlementへの移行候補です。
今回の更新をどう捉えるべきか
今回の公式更新は、単なる小さな機能紹介ではなく、Azure Service Busを使ったバッチ処理の設計をより現実的にするための整理といえます。
従来は、バッチ処理で効率を上げようとすると、失敗時に全体が巻き戻るリスクがありました。一方、1件ずつ処理すれば失敗制御はしやすいものの、関数実行回数や接続オーバーヘッドが増えます。
個別settlementは、このトレードオフを緩和します。バッチ処理の効率を維持しながら、メッセージごとに成功・再試行・隔離・延期を判断できるためです。
開発者は、まず現在のService Busトリガーで「1件の失敗がどこまで影響するか」を確認してください。管理者は、DLQ監視、再試行回数、失敗理由の可視化を整えることが次の一手です。
Azure Service Busのバッチ処理は、単にまとめて速く処理するだけでは不十分です。これからは、バッチ内の1件ごとに正しく終わらせる設計が、安定運用とコスト最適化の鍵になります。

コメント