📬 AWS SQS DLQ & Redrive

レストランの注文システムで理解する!失敗メッセージの処理完全ガイド

🍽️ レストランの注文システムで例えると超わかりやすい!

SQSは「レストランの注文伝票システム」のようなもの!

注文が正常に処理できない場合、どうしますか?
Dead-letter Queue (DLQ) = 問題のある注文を別の場所に保管
Redrive Policy = 何回失敗したら別の場所に移すかのルール

この仕組みを完璧にマスターしましょう✨

📚 基本概念:レストラン vs AWS

🍽️
レストランの注文システム

📋 注文伝票トレイ
厨房に届く注文伝票を置く場所


👨‍🍳 シェフ(処理担当)
注文を見て料理を作る人


❌ 作れない注文
材料不足、レシピ不明など


🗂️ 問題注文ボックス
処理できない注文を別管理


📏 再試行ルール
「3回失敗したら問題ボックスへ」

☁️
AWS SQSの世界

📬 Standard Queue(通常キュー)
メッセージを保管するメインキュー


⚙️ Consumer(コンシューマー)
メッセージを処理するアプリケーション


❌ 処理失敗メッセージ
エラーや例外で処理できないメッセージ


💀 Dead-letter Queue (DLQ)
失敗メッセージを隔離する専用キュー


🔄 Redrive Policy
maxReceiveCount = 何回で移動するか

🎨 全体の流れ - ビジュアル図解

📤
Producer
メッセージ送信元
📬
Main Queue
メインキュー
⚙️
Consumer
処理アプリ
📬
Main Queue
3回失敗
⬇️
💀
Dead-letter Queue
失敗メッセージ保管
🔄 Redrive Policy の役割
maxReceiveCount = 3 の場合
メッセージが3回処理失敗したら自動的にDLQへ移動

🔄 メッセージ処理フロー:5つのステップ

1
📬 メッセージ受信
🍽️ レストラン: 新しい注文伝票が届く
☁️ AWS: Consumerがメインキューからメッセージを取得
→ ReceiveMessageが実行される
2
⚙️ 処理試行
🍽️ レストラン: シェフが注文を見て料理を作ろうとする
☁️ AWS: アプリケーションロジックでメッセージを処理
→ ビジネスロジックの実行
3
❌ 処理失敗
🍽️ レストラン: 材料不足で料理が作れない!
☁️ AWS: 例外エラーが発生、DeleteMessageが呼ばれない
→ メッセージはキューに残る(ReceiveCountが+1)
4
🔄 リトライ判定
🍽️ レストラン: 「まだ2回目だからもう一度トライ」
☁️ AWS: ReceiveCount < maxReceiveCount なら再度キューに戻る
→ Visibility Timeout後に再度取得可能
5
💀 DLQへ移動
🍽️ レストラン: 「3回失敗!問題注文ボックスへ」
☁️ AWS: ReceiveCount ≥ maxReceiveCount でDLQへ自動移動
→ 後で原因調査と修正が可能

⚖️ 正常処理 vs 問題発生時の違い

正常処理の流れ
  • 1️⃣ メッセージ受信 (ReceiveMessage)
  • 2️⃣ 処理成功 (ビジネスロジック実行)
  • 3️⃣ メッセージ削除 (DeleteMessage)
  • 🎉 完了!キューから消える
失敗時の流れ
  • 1️⃣ メッセージ受信 (ReceiveMessage)
  • 2️⃣ 処理失敗 (例外エラー発生)
  • 3️⃣ DeleteMessage呼ばれず
  • 4️⃣ ReceiveCount +1 (受信回数カウント)
  • 5️⃣ Visibility Timeout後に再度出現
  • 6️⃣ maxReceiveCount到達でDLQへ
  • 💀 DLQで隔離・原因調査

⚙️ Redrive Policy 設定方法

📝 必須パラメータ
maxReceiveCount
意味: 何回受信失敗したらDLQへ移動するか
推奨値: 3〜5回
🍽️ 例: 「3回作れなかったら問題ボックスへ」
注意: 1回だと誤検知が多く、10回以上だと遅すぎる
deadLetterTargetArn
意味: どのDLQへ移動するか(DLQのARN)
形式: arn:aws:sqs:region:account:queue-name
🍽️ 例: 「問題注文ボックスの場所」
注意: 事前にDLQ用のキューを作成しておく必要あり
🔍 関連する重要設定
Visibility Timeout
意味: メッセージが他のConsumerに見えなくなる時間
推奨値: 処理時間の6倍
🍽️ 例: 「この注文は今調理中」の札を付ける時間
注意: 短すぎると重複処理、長すぎるとリトライが遅い
Message Retention Period
意味: DLQでメッセージを保持する期間
推奨値: 14日間(最大)
🍽️ 例: 「問題注文を2週間保管」
注意: DLQは長めに設定して原因調査の時間を確保
📟 AWS CLI での設定例
# 1. DLQ用のキューを作成
aws sqs create-queue \
  --queue-name my-app-dlq \
  --attributes MessageRetentionPeriod=1209600  # 14日間

# 2. DLQのARNを取得
aws sqs get-queue-attributes \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/123456789012/my-app-dlq \
  --attribute-names QueueArn

# 3. メインキューにRedrive Policyを設定
aws sqs set-queue-attributes \
  --queue-url https://sqs.ap-northeast-1.amazonaws.com/123456789012/my-main-queue \
  --attributes '{
    "RedrivePolicy": "{
      \"deadLetterTargetArn\": \"arn:aws:sqs:ap-northeast-1:123456789012:my-app-dlq\",
      \"maxReceiveCount\": \"3\"
    }",
    "VisibilityTimeout": "300"
  }'
🏗️ Terraform での設定例
# DLQ用のキュー
resource "aws_sqs_queue" "dlq" {
  name                      = "my-app-dlq"
  message_retention_seconds = 1209600  # 14日
  
  tags = {
    Name        = "My App DLQ"
    Environment = "production"
  }
}

# メインキュー(Redrive Policy設定済み)
resource "aws_sqs_queue" "main" {
  name                      = "my-main-queue"
  visibility_timeout_seconds = 300      # 5分
  message_retention_seconds = 345600   # 4日
  
  # 🔄 Redrive Policy設定
  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.dlq.arn
    maxReceiveCount     = 3
  })
  
  tags = {
    Name        = "My Main Queue"
    Environment = "production"
  }
}

# 💀 DLQからメインキューへのRedrive設定(オプション)
resource "aws_sqs_queue_redrive_allow_policy" "dlq_redrive" {
  queue_url = aws_sqs_queue.dlq.id
  
  redrive_allow_policy = jsonencode({
    redrivePermission = "byQueue"
    sourceQueueArns   = [aws_sqs_queue.main.arn]
  })
}

💼 実際のユースケース

📧

メール配信システム

シナリオ:
メールアドレスが無効、SMTPサーバーダウン

設定:
maxReceiveCount = 5
→ 5回失敗したらDLQへ

対応:
DLQのメッセージを見て無効アドレスをDBから削除
💳

決済処理システム

シナリオ:
外部API障害、タイムアウト、データ不整合

設定:
maxReceiveCount = 3
→ すぐに隔離して手動確認

対応:
重要度が高いので即座にアラート発行、手動再処理
📊

データ分析バッチ

シナリオ:
不正なデータフォーマット、計算エラー

設定:
maxReceiveCount = 2
→ 少ない回数で早めに分離

対応:
DLQメッセージを分析してデータバリデーション改善
🖼️

画像変換サービス

シナリオ:
破損画像、未対応フォーマット、メモリ不足

設定:
maxReceiveCount = 4
→ 一時的エラーを考慮して少し多め

対応:
問題ファイルのパターンを分析してエラー処理強化

✅ ベストプラクティス

🎯
1. maxReceiveCountは慎重に設定
推奨: 3〜5回
• 1回:誤検知が多すぎる
• 10回以上:問題発見が遅れる
• 処理の重要度とコストを考慮して決定
🔔
2. DLQの監視とアラート設定
必須: CloudWatch Alarmを設定
• ApproximateNumberOfMessagesVisible > 0
• メッセージがDLQに入ったら即座に通知
• Slackやメールで開発チームへアラート
📝
3. DLQのメッセージに詳細情報を追加
推奨: MessageAttributesを活用
• エラー理由、タイムスタンプ
• 元のReceiveCount
• 処理試行時のログID
→ 原因調査が劇的に楽になる
🔄
4. Redrive機能で再処理
活用: 問題修正後にDLQから戻す
• AWSコンソールの「Start DLQ redrive」
• 修正済みメッセージを元のキューへ
• 一括処理が可能で効率的
⏱️
5. Retention期間を長めに設定
DLQ推奨: 14日間(最大値)
• メインキュー:4日間
• DLQ:14日間
→ 問題調査と修正の時間を十分確保
🏗️
6. 環境ごとにDLQを分離
構成: dev/staging/prod別々に
• my-app-dev-dlq
• my-app-staging-dlq
• my-app-prod-dlq
→ 環境間の混乱を防止
⚠️ よくある間違いと注意点
❌ 間違い1: DLQを設定せずに運用
→ 失敗メッセージが永遠にリトライされ続ける。Visibility Timeout後に何度も処理されてコストが増大。

❌ 間違い2: maxReceiveCountを1に設定
→ 一時的なネットワークエラーなどでも即DLQ行きになり、誤検知が多すぎる。

❌ 間違い3: DLQの監視をしない
→ メッセージがDLQに溜まっていることに気づかず、重要な処理が止まったまま。

❌ 間違い4: 処理失敗時にDeleteMessageを呼ぶ
→ リトライの機会を失い、データロスの原因に。エラー時はDeleteしてはいけない。

❌ 間違い5: DLQのRetention期間が短い
→ 原因調査中にメッセージが消えてしまい、問題の再現ができなくなる。
💡 プロのTips:DLQ運用テクニック
📊 Tip 1: DLQメッセージのダッシュボード作成
CloudWatch Logsにエラーパターンを集約し、どの種類のエラーが多いか可視化。優先度をつけて対応。

🔍 Tip 2: Lambda DLQトリガーで自動分析
DLQにメッセージが入ったらLambdaを起動し、エラー分類を自動実行。Slackに詳細レポート送信。

⚙️ Tip 3: 段階的リトライ戦略
maxReceiveCount=3でDLQ行き → Lambda処理 → 修正可能なら元のキューへRedrive → 不可能ならS3へアーカイブ。

🧪 Tip 4: DLQでテスト環境を作る
本番のDLQメッセージをコピーして開発環境で再現テスト。実際の問題データで検証できて確実。

📈 Tip 5: エラー率をKPIとして追跡
(DLQメッセージ数 / 総メッセージ数) × 100 = エラー率。週次レポートで改善トレンドを可視化。
🐍 Python実装例:エラーハンドリング付きConsumer
import boto3
import json
import logging
from time import sleep

logger = logging.getLogger()
logger.setLevel(logging.INFO)

sqs = boto3.client('sqs', region_name='ap-northeast-1')
QUEUE_URL = 'https://sqs.ap-northeast-1.amazonaws.com/123456789012/my-main-queue'

def process_message(message_body):
    """ビジネスロジック:ここで実際の処理を実行"""
    # 例:外部APIを呼び出す、DBに書き込むなど
    data = json.loads(message_body)
    
    # 何らかの処理...
    if data.get('invalid'):
        raise ValueError("Invalid data format")
    
    return True

def consume_messages():
    """メッセージを受信して処理する"""
    while True:
        # メッセージを受信(Long Polling: 20秒)
        response = sqs.receive_message(
            QueueUrl=QUEUE_URL,
            MaxNumberOfMessages=10,  # 一度に10件まで
            WaitTimeSeconds=20,       # Long Polling
            AttributeNames=['ApproximateReceiveCount']
        )
        
        messages = response.get('Messages', [])
        
        if not messages:
            logger.info("No messages, waiting...")
            continue
        
        for message in messages:
            receipt_handle = message['ReceiptHandle']
            message_id = message['MessageId']
            body = message['Body']
            receive_count = int(message['Attributes'].get('ApproximateReceiveCount', 0))
            
            logger.info(f"Processing message {message_id} (ReceiveCount: {receive_count})")
            
            try:
                # ✅ ビジネスロジック実行
                process_message(body)
                
                # ✅ 成功したらメッセージを削除
                sqs.delete_message(
                    QueueUrl=QUEUE_URL,
                    ReceiptHandle=receipt_handle
                )
                logger.info(f"Successfully processed message {message_id}")
                
            except Exception as e:
                # ❌ エラー発生時はDeleteしない → 自動的にキューに戻る
                logger.error(f"Failed to process message {message_id}: {str(e)}")
                logger.error(f"Receive count: {receive_count}. Will retry or move to DLQ.")
                
                # ⚠️ DeleteMessageを呼ばないことで:
                # 1. Visibility Timeout後に再度処理可能になる
                # 2. ReceiveCountが+1される
                # 3. maxReceiveCountに達したら自動的にDLQへ
                continue

if __name__ == "__main__":
    logger.info("Starting SQS Consumer...")
    consume_messages()

🎓 まとめ

🍽️ レストランの問題注文システム = SQS DLQ

Dead-letter Queue と Redrive Policy で
失敗メッセージを適切に管理 できる✨

💀
DLQ

失敗メッセージを
安全に隔離して
後で調査
🔄
Redrive Policy

何回失敗したら
DLQへ移動するかの
ルール設定
📊
監視

DLQにメッセージが
入ったら即座に
アラート通知

🎯 重要ポイント:

1️⃣ maxReceiveCount = 3〜5 が推奨
2️⃣ エラー時は DeleteMessage を呼ばない
3️⃣ CloudWatch Alarm でDLQ監視
4️⃣ 問題修正後は Redrive機能 で再処理
5️⃣ DLQのRetention期間は 14日間 (最大)

🎉 これでSQSの失敗処理はバッチリ!
安心してメッセージキューを運用できます!

Created by SSuzuki1063

AWS SAP Learning Resources