Hands-on


1. Create Standard Queue With DLQ#

export AWS_REGION=ap-east-1
export QUEUE_NAME=dev-order-payment-capture
export DLQ_NAME=dev-order-payment-capture-dlq

Create DLQ:

aws sqs create-queue \
  --region "$AWS_REGION" \
  --queue-name "$DLQ_NAME" \
  --attributes '{
    "MessageRetentionPeriod": "1209600",
    "ReceiveMessageWaitTimeSeconds": "20",
    "SqsManagedSseEnabled": "true"
  }'

Get DLQ ARN:

export DLQ_URL=$(aws sqs get-queue-url \
  --region "$AWS_REGION" \
  --queue-name "$DLQ_NAME" \
  --query 'QueueUrl' \
  --output text)

export DLQ_ARN=$(aws sqs get-queue-attributes \
  --region "$AWS_REGION" \
  --queue-url "$DLQ_URL" \
  --attribute-names QueueArn \
  --query 'Attributes.QueueArn' \
  --output text)

Create source queue:

cat > /tmp/sqs-source-attributes.json <<EOF
{
  "MessageRetentionPeriod": "345600",
  "VisibilityTimeout": "120",
  "ReceiveMessageWaitTimeSeconds": "20",
  "SqsManagedSseEnabled": "true",
  "RedrivePolicy": "{\"deadLetterTargetArn\":\"$DLQ_ARN\",\"maxReceiveCount\":\"5\"}"
}
EOF

aws sqs create-queue \
  --region "$AWS_REGION" \
  --queue-name "$QUEUE_NAME" \
  --attributes file:///tmp/sqs-source-attributes.json

Get queue URL:

export QUEUE_URL=$(aws sqs get-queue-url \
  --region "$AWS_REGION" \
  --queue-name "$QUEUE_NAME" \
  --query 'QueueUrl' \
  --output text)

2. Send Message#

aws sqs send-message \
  --region "$AWS_REGION" \
  --queue-url "$QUEUE_URL" \
  --message-body '{
    "schema_version": 1,
    "event_type": "payment.capture.requested",
    "request_id": "req-123",
    "order_id": "ord-1001",
    "amount_cents": 1200
  }' \
  --message-attributes '{
    "trace_id": {
      "DataType": "String",
      "StringValue": "trace-abc"
    },
    "schema_version": {
      "DataType": "Number",
      "StringValue": "1"
    }
  }'

3. Receive And Delete#

Receive:

aws sqs receive-message \
  --region "$AWS_REGION" \
  --queue-url "$QUEUE_URL" \
  --max-number-of-messages 1 \
  --wait-time-seconds 20 \
  --attribute-names ApproximateReceiveCount SentTimestamp \
  --message-attribute-names All

Delete after successful processing:

export RECEIPT_HANDLE='<paste-receipt-handle-from-receive-message>'

aws sqs delete-message \
  --region "$AWS_REGION" \
  --queue-url "$QUEUE_URL" \
  --receipt-handle "$RECEIPT_HANDLE"

Extend visibility for long processing:

aws sqs change-message-visibility \
  --region "$AWS_REGION" \
  --queue-url "$QUEUE_URL" \
  --receipt-handle "$RECEIPT_HANDLE" \
  --visibility-timeout 300

4. Create FIFO Queue#

aws sqs create-queue \
  --region "$AWS_REGION" \
  --queue-name dev-order-state.fifo \
  --attributes '{
    "FifoQueue": "true",
    "ContentBasedDeduplication": "true",
    "ReceiveMessageWaitTimeSeconds": "20",
    "VisibilityTimeout": "120",
    "SqsManagedSseEnabled": "true"
  }'

Send FIFO message:

export FIFO_URL=$(aws sqs get-queue-url \
  --region "$AWS_REGION" \
  --queue-name dev-order-state.fifo \
  --query 'QueueUrl' \
  --output text)

aws sqs send-message \
  --region "$AWS_REGION" \
  --queue-url "$FIFO_URL" \
  --message-group-id "order-ord-1001" \
  --message-deduplication-id "order-state-ord-1001-v7" \
  --message-body '{
    "schema_version": 1,
    "event_type": "order.state.changed",
    "order_id": "ord-1001",
    "version": 7,
    "status": "PAID"
  }'

5. Inspect Queue#

aws sqs get-queue-attributes \
  --region "$AWS_REGION" \
  --queue-url "$QUEUE_URL" \
  --attribute-names All
aws cloudwatch get-metric-statistics \
  --region "$AWS_REGION" \
  --namespace AWS/SQS \
  --metric-name ApproximateAgeOfOldestMessage \
  --dimensions Name=QueueName,Value="$QUEUE_NAME" \
  --start-time "$(date -u -v-30M +%FT%TZ)" \
  --end-time "$(date -u +%FT%TZ)" \
  --period 60 \
  --statistics Maximum

6. Cleanup#

aws sqs delete-queue \
  --region "$AWS_REGION" \
  --queue-url "$QUEUE_URL"

aws sqs delete-queue \
  --region "$AWS_REGION" \
  --queue-url "$DLQ_URL"

7. Redrive Messages From DLQ After Fix#

aws sqs start-message-move-task \
  --region "$AWS_REGION" \
  --source-arn "$DLQ_ARN"
redrive notes:
    this moves messages from DLQ back to the configured source queue
    use only after root cause is fixed
    replay in controlled window
    watch duplicate side effects and source queue age during replay