SQS・Pub/Sub・Azure Service Bus比較:配送保証・順序・再処理で選ぶ
AWSのSQSとSNS、Google Cloud Pub/Sub、Azure Service Busを、仕事の分配とイベント配信、重複排除、順序、滞留、DLQからの復旧で比較。exactly-onceの範囲と業務の二重処理を分けて設計します。
SQS・Pub/Sub・Service Bus:仕事の分配とイベントの配信を分ける
注文が確定したら発送と分析へ知らせたいとします。発送担当のworkerが10台あっても、発送という仕事はそのうちの1台が担えば十分です。一方、分析にも同じ注文確定を渡すなら、発送と分析は別々に受け取る必要があります。この二つを、競合するconsumerによる分担と、fanoutによる配信として区別します。
| 目的 | AWS | Google Cloud | Azure |
|---|---|---|---|
| 発送worker同士で分担 | 1つのSQSキューを複数consumerで受信 | 1つのサブスクリプションを複数subscriberで受信 | Service Busの1つのキューを複数receiverで受信 |
| 発送と分析へそれぞれ配信 | SNS topicから業務ごとに分けたSQSキューへ配信 | Pub/Sub topicに業務ごとのサブスクリプション | Service Bus topicに業務ごとのサブスクリプション |
発送と分析のworkerを同じ分担用キューやサブスクリプションへ混ぜると、両方にすべてのイベントが渡る構成になりません。業務ごとに滞留と再試行を分け、分析が遅れても発送を止めない構成にします。追加の購読先が増えれば、配送・保持・consumerの費用も増えます。
重複排除とexactly-onceは、何を一度にするのか
| 機能・方式 | 確認できる範囲 | 別に設計すること |
|---|---|---|
| SQS Standard | 少なくとも1回の配送。重複や順序の入れ替わりがあり得る | 再配送されても同じ業務操作を二重に実行しない |
| SQS FIFOの送信重複排除 | 同じ重複排除ID等による5分間の送信再試行の重複を抑える | 期限後の再送と、処理後・削除前の停止による再処理 |
| Pub/Subのexactly-once delivery | 有効化したpull方式で、リージョン内の保証。成功確認済みのack後に再配送しない | push・exportへの適用は不可。ack期限切れ等の有効な再配送と外部副作用 |
| Service Busのduplicate detection | 有効化したStandard・Premiumで、設定窓内の送信IDを重複排除 | 受信処理の再実行と外部副作用。partition利用時はPartitionKeyも確認 |
Service Busの重複検出窓は20秒〜7日で設定でき、既定は10分です。Pub/Subの保証も、同じ業務イベントをpublisherが別メッセージとして二度発行したことまで同一視するものではありません。どの場合も、発送APIの成功直後にconsumerが停止すれば、配送サービスだけでは発送済みかを判断できません。
- メッセージ基盤: ship-123を渡す
- 配送業者API: 発送依頼が成功
- worker: 結果保存・受領確定の前に停止
- メッセージ基盤: 期限切れ等で再配送
- 配送業者API: 同じship-123で結果照合・重複対策
注文保存と配信の間はOutbox、受信側は業務操作IDで守る
DBへ注文を保存した後、メッセージ送信前に落ちると、発送へ何も届きません。注文とoutboxのイベントを同じDBトランザクションへ保存し、リレーやCDCで送信する方式を検討します。未送信イベントの保持、リレーの再起動、再試行、最古の未送信時刻を管理します。送信成功後の記録に失敗すれば再送されるので、受信側の重複対策も必要です。
- 自分のDB内で完結する更新は、処理済みイベントIDの一意な記録と業務更新を同じトランザクションに含める。同時に受けても一方だけが更新するようにする。
- 外部の発送APIには、元の発送操作IDを冪等キーとして渡す等、相手側の保証を使う。結果不明なら同じIDで照合し、単なるローカルの処理済みフラグで成功を決めない。
- 業務操作IDを配送用message IDとは別に保持する。保持期間は通常の再送だけでなく、DLQからの再処理期間と外部APIのキー保持条件にも合わせる。
順序は注文単位か、顧客単位かを先に決める
| 候補 | 順序の単位・条件 | 並列性への影響 |
|---|---|---|
| SQS FIFO | MessageGroupId内の順序 | 異なるグループは並列化し、同じグループの処理順をconsumer側でも守る |
| Pub/Subのordering | 有効化した購読で同じordering key。発行は同一リージョンへ | キーをまたぐ全体順序ではない。遅いキーを分離して測る |
| Service Bus sessions | Standard・Premiumで同じSessionId | セッション単位で受信し、ロック更新・期限切れを扱う |
すべてを1つのキーへ寄せると、遅い注文が全体の進行を止める原因になります。注文の作成→変更→取消の順序が必要なら注文IDでまとめ、必要な並列性を残します。ただし到着順が業務上の正しい順序とは限らないため、イベントの版や現在の状態も照合します。DLQへ隔離して後で戻すときの順序も別に決めます。
滞留と費用は、再試行・購読先・下流上限まで含める
仮に到着が毎秒900件、実際に成功する処理能力が毎秒700件の状態が10分続くと、滞留は(900 − 700) × 600 = 120,000件です。その後の到着が毎秒500件なら余力は毎秒200件なので、一定流量の仮定では解消に120,000 ÷ 200 = 600秒、つまり10分かかります。workerを増やしても配送業者APIの上限が毎秒700件なら、それ以上の成功処理能力にはなりません。
- キューの件数だけでなく、最古の未処理メッセージの年齢、業務の完了時間、失敗率を見る。保持期限までに回収できるか確認する。
- 再試行、受信・確認・削除、空のポーリング、配送バイト数、購読先数、保持を実際の料金体系へ対応付ける。1回のpublish単価だけで比較しない。
- キュー方式・プランの上限、順序キーの偏り、consumerの計算資源、非公開接続やリージョン間転送も見積もる。
DLQは未完了の仕事:安全な再投入を採用条件にする
失敗したメッセージをDLQへ送っても、発送は完了していません。たとえば配送業者の受付後に結果保存が失敗したなら、原因を直して全件再投入するだけでは二重発送になり得ます。SQSのredriveでは新しいmessageIDが付くため、元の発送操作IDで成功済みかを調べます。各社でも、再処理でIDと期限がどう扱われるかを確認します。
- 担当と通知先、保存期限、調査に必要な業務操作IDを決める。通常キューが空でもDLQに未完了が残っていないかを見る。
- 原因と既存の副作用を照合し、成功済みなら結果を修復する。期限切れや順序違反になる操作は、通常の再投入から外して判断する。
- 必要な仕事だけを少量・低速で戻し、通常処理の遅延と下流のエラーを見て増やす。中止条件も定める。
- 副作用直後、受領確定直前、ロック期限切れで停止させ、二重処理と処理漏れを業務結果で検査する。これを満たす構成同士で運用負荷と費用を比べる。
配送・順序・重複排除の範囲を確認する一次資料
- AWS — SQS standard queues
- AWS — SNS fanout to SQS
- AWS — SQS FIFO delivery logic
- AWS — SQS FIFO deduplication
- Google Cloud — Pub/Sub service overview
- Google Cloud — Pub/Sub exactly-once delivery
- Google Cloud — Pub/Sub message ordering
- Microsoft Learn — Service Bus overview
- Microsoft Learn — Service Bus duplicate detection
- Microsoft Learn — Service Bus sessions
- AWS — Transactional outbox pattern
- AWS — SQS dead-letter redrive