
Twilio+SQS+Lambda 構成で発生した「再送ループによる課金」事例と防止策
対象読者
- Twilio API を利用した開発を検討しているエンジニア
- AWS Lambda や Amazon SQS を組み合わせた非同期処理設計を行うエンジニア
- Slack などのチャットツールと外部サービス連携を行いたい開発者
- 再送制御や冪等性設計について具体的な事例を知りたい方
- クラウドサービスの利用時に課金リスクへの備えをしたい方
はじめに
本記事では、Twilio API を利用した SMS コマンド機能の検証中に発生した、意図しない課金の発生事例 について報告します。
当該インシデントは、筆者が以下の記事シリーズを執筆する過程において発生しました。
[Twilio+Slack] Slack に SMS 送信コマンドを実装する: SQS+Lambda での非同期処理構成
Twilio API では容易に SMS を送信できる一方で、キューや非同期処理を組み合わせた設計においては、意図しないリトライや再送によって課金が過剰に発生するリスク があります。
本記事では、実際に検証環境で発生した課金トラブルについて、その原因と再発防止策を共有します。ポイントは、「非同期処理における副作用(API 呼び出し)が終了処理の失敗によって重複実行される場合がある」 という設計上の落とし穴と、それを防ぐための 冪等性設計とリトライ制御の重要性 です。
1. インシデント概要
発生時期
2025 年 4 月、Twilio API を利用した検証中に発生。
検証環境構成
- Slack からのコマンド入力
- AWS Lambda → Amazon SQS 経由で非同期的に SMS 送信
- SQS から Lambda ワーカーがメッセージを受信
- Lambda 内で Twilio API により SMS を送信
- Lambda の終了処理が正常に完了しなかった場合、SQS がエラーと判断してメッセージを再投入 → 再送発生
事象
- Lambda 内で Twilio API 呼び出しは成功していた
- しかし、Lambda の終了時にエラーが発生
- 例: API 呼び出し後の終了時に context.succeed() 相当の処理を行わず例外がスローされた。これにより、Twilio API の呼び出し自体は成功しているにもかかわらず、Lambda はジョブ失敗とみなされ、SQS 側で再送が行われる状態となっていた。
- そのため SQS がジョブ失敗と判断し、同一メッセージを再送
- 冪等性を考慮していなかったため、再送ごとに新規の SMS 送信が実行され、本来数件程度の送信が想定されていたところ、1 件のコマンドに対し数十件の SMS が送信され、数千円規模の課金 に至った
2. 詳細な発生状況
実装していたフロー
- Slack から指定のコマンドを送信
- API Gateway → Lambda によってコマンドを受理
- Lambda から SQS にメッセージ送信
- 別の Lambda(ワーカー)が SQS からメッセージを受信
- Lambda 内で Twilio API により SMS 送信を実行
- 送信成功後、Lambda の終了処理でエラー発生 → SQS から再送 → 再度 Lambda 起動
- 冪等性チェックがなかったため、再起動のたびに新しい SMS が送信された
3. 課金が発生した構造と要因
要素 | 内容 |
---|---|
Lambda 終了処理の失敗 | API 呼び出しは成功していたが、Lambda 側での終了処理が失敗し、SQS が再送を実行した |
冪等性の欠如 | 再送時に同一リクエストであることを判別できず、都度 SMS が送信された |
使用していた番号の地域差 | Twilio の US 番号から日本番号への送信だったため、1 回あたりの単価が高額 |
番号取得コストのトレードオフ | 日本番号の取得は高額であり、検証時には US 番号を利用していた |
課金監視手段の不十分さ | Usage Trigger などによる事前の課金制御を行っておらず、発生後に気づいた |
4. 再発防止策
Lambda ワーカー設計の見直し
- Lambda の終了処理を適切に実装し、API 呼び出し成功時は必ず
SUCCESS
で終了するよう修正 - エラー発生時にも「どの処理までが完了したか」を記録する
- 処理途中でのタイムアウト回避のため、Lambda のタイムアウト時間を適切に設定
冪等設計の導入
- メッセージごとに一意のリクエスト ID を付与
- 再送時にリクエスト ID をチェックし、すでに送信済みであれば SMS 送信処理をスキップ
SQS 設定の見直し
- Visibility Timeout を Lambda の処理時間 + α に調整
- Maximum Receives と Dead Letter Queue(DLQ)の設定により無限再送を防止
Twilio 課金対策
- Usage Trigger による総量監視の導入(ただし根本的な再送制御が重要)
- 開発環境では送信先をホワイトリスト制限し、誤送信を防止
- 番号選定(US 番号 vs. 日本番号)の費用対効果を検討
- 検証時には Twilio API 呼び出し部分をコメントアウトする、またはモック化することで、送信動作の確認と課金発生を切り分けてテストする
5. まとめ
Twilio API の運用においては、非同期メッセージング × 冪等性欠如 × ワーカーエラー時の再送設計不備 という条件が重なると、検証環境であっても想定以上のコストが発生します。
特に、Lambda の中で副作用(API 呼び出し)が発生しているにもかかわらず、終了時のエラーでリトライされる という構造は、見落とされやすい設計上の罠です。
同様の構成を採用する際は、
「再送時にも同一リクエストかどうかを必ず判別し、冪等性を担保する」
この設計思想を持つことが、不要なコスト発生を防ぐ上で不可欠です。
本事例が、Twilio および AWS を組み合わせたシステム設計を行うエンジニアにとって、課金リスクを低減するための注意喚起となれば幸いです。