🏗️ 4 Estrategias para Procesar Mensajes SQS en Batch con AWS Lambda

7 min de lectura Ver original en Hashnode
Compartir:

Durante una charla de sincronización con un compañero de trabajo, sobre un cliente que necesita una estrategia para gestionar una cola de eventos a través de procesamiento por lotes, surgió la duda de cuál es la estrategia que más le convenía, y cuál era el estado del arte de las colas SQS en estos momentos.

Basado en lo que sacamos de esa charla, en este post exploraremos 4 estrategias diferentes para procesar mensajes de Amazon SQS en lotes usando AWS Lambda (como único consumidor, esto es importante para el análisis). Cada estrategia tiene sus propios casos de uso, ventajas y consideraciones de rendimiento que analizaremos con ejemplos prácticos.

🚀 Por qu procesar mensajes SQS en lotes?

Amazon SQS permite procesar hasta 10 mensajes por llamada usando receive_message() (Python), lo que es ms eficiente que procesar mensajes uno por uno. Sin embargo, no todos los mensajes pueden procesarse exitosamente en el primer intento, por lo que necesitamos diferentes estrategias para manejar los fallos.

Validación del Límite de 10 Mensajes

AWS SQS estrictamente limita el parámetro MaxNumberOfMessages entre 1 y 10. Si intentas usar un valor mayor, obtienes este error:

{ "error": "An error occurred (InvalidParameterValue) when calling the ReceiveMessage operation: Value 20 for parameter MaxNumberOfMessages is invalid. Reason: Must be between 1 and 10, if provided."}

Este límite aplica tanto para colas Standard como FIFO, confirmando que el procesamiento en lotes está limitado a máximo 10 mensajes por operación receive_message().

Colas estndar vs Colas FIFO: Ref

🏗 Arquitectura del Proyecto

Importante: Este análisis y casos de uso están basados en un consumidor único por cola. Con múltiples consumidores concurrentes, el comportamiento y las consideraciones de cada estrategia pueden ser significativamente más complejas, especialmente en términos de duplicados, orden de procesamiento y gestión de fallos.

El proyecto implementa 4 funciones Lambda diferentes, cada una con una estrategia distinta para manejar mensajes que no cumplen ciertos criterios (en nuestro caso, números menores a 50). Partiendo de un script que genera 100 mensajes aleatorios con números entre 1 y 100 (el script pueden encontrarlo en el GitHub):

https://github.com/olcortesb/aws-examples

A continuacin, las dos colas que utilizaremos en las pruebas:

Resources: MessagesQueue: Type: AWS::SQS::Queue Properties: VisibilityTimeoutSeconds: 30 MessageRetentionPeriod: 1209600 DeadLetterQueue: Type: AWS::SQS::Queue Properties: MessageRetentionPeriod: 1209600

📋 Las 4 Estrategias Implementadas

1. Estrategia Simple: Eliminar Todos los Mensajes

Función: GetMessagesFunction

def lambda_handler(event, context): response = sqs.receive_message( QueueUrl=queue_url, MaxNumberOfMessages=10, # Mximo permitido por AWS WaitTimeSeconds=5 ) messages = response.get('Messages', []) for msg in messages: # Procesar mensaje processed_messages.append({ "messageId": msg['MessageId'], "body": msg['Body'] }) # Eliminar siempre sqs.delete_message( QueueUrl=queue_url, ReceiptHandle=msg['ReceiptHandle'] )

Caractersticas:

  • Simplicidad: Lgica directa y fcil de entender
  • Rendimiento: No hay operaciones adicionales
  • Pérdida de datos: Los mensajes fallidos se pierden permanentemente

Caso de uso: Procesamiento donde la pérdida ocasional de mensajes es aceptable.

2. Estrategia de Reenvo: Devolver Mensajes a la Cola

Función: ProcessMessagesFunction

def lambda_handler(event, context): for msg in messages: message_number = int(msg['Body']) if message_number < threshold: # Reenviar a la misma cola sqs.send_message( QueueUrl=queue_url, MessageBody=msg['Body'] ) # Eliminar mensaje original sqs.delete_message( QueueUrl=queue_url, ReceiptHandle=msg['ReceiptHandle'] )

Caractersticas:

  • Sin pérdida: Los mensajes fallidos se reenvían
  • Flexibilidad: Permite modificar el mensaje antes del reenvo (creo que es lo nico positivo que tiene este caso de uso, pero lo dejo porque alguna vez lo he tenido que usar 😂)
  • Duplicación: Puede crear mensajes duplicados
  • Overhead: Operaciones adicionales de escritura

Caso de uso: Cuando necesitas modificar (por ejemplo, sumar un número en nuestro caso de prueba) mensajes antes de reintentar un nuevo procesamiento.

3. Estrategia de Visibility Timeout: Dejar Reaparecer

Función: ProcessWithVisibilityFunction

def lambda_handler(event, context): for msg in messages: message_number = int(msg['Body']) if message_number >= threshold: # Solo eliminar si cumple criterios sqs.delete_message( QueueUrl=queue_url, ReceiptHandle=msg['ReceiptHandle'] ) # Los mensajes no eliminados reaparecen automticamente

Caractersticas:

  • Eficiencia: Menos operaciones de escritura
  • Automático: SQS maneja la reaparición
  • Sin duplicados: No crea mensajes adicionales
  • Tiempo fijo: Depende del visibility timeout configurado Visibility Timeout grande: Puede darse el caso que el procesamiento de mensajes puede ser distinto por ejemplo un minuto para un mensaje y 30 minutos para otro, por lo tanto, es necesario entender si podemos esperar ese tiempo o colocar un visibility más bajo.
  • Visibility Timeout pequeo: Si el timeout es muy pequeo y el procesamiento de los mensajes es mayor al timeout, se pueden generar duplicados.

Caso de uso: Ideal para reintentos automticos sin lgica compleja.

4. Estrategia Dead Letter Queue: Segregar Mensajes Fallidos

Función: ProcessWithDlqFunction

def lambda_handler(event, context): for msg in messages: message_number = int(msg['Body']) if message_number >= threshold: processed_messages.append(msg) else: # Enviar a Dead Letter Queue sqs.send_message( QueueUrl=dlq_url, MessageBody=msg['Body'] ) # Eliminar de cola original sqs.delete_message( QueueUrl=queue_url, ReceiptHandle=msg['ReceiptHandle'] )

Caractersticas:

  • Segregación: Mensajes fallidos en cola separada
  • Análisis: Permite investigar patrones de fallo
  • Reprocesamiento: Posibilidad de procesar DLQ posteriormente
  • Complejidad: Requiere gestin de mltiples colas

Caso de uso: Sistemas crticos que requieren anlisis de fallos y reprocesamiento.

🚀 Despliegue y Pruebas

1. Desplegar la Aplicación

sam buildsam deploy --guided --profile your-aws-profile

2. Enviar Mensajes de Prueba

cd scriptspython3 send_messages.py

El script enva 100 mensajes numerados del 1 al 100 para probar las diferentes estrategias (los que no tomamos como vlidos son los menores a 50).

3. Probar Cada Estrategia

# Estrategia 1: Eliminar todosaws lambda invoke --function-name bach-messages-GetMessagesFunction-XXXXX response1.json# Estrategia 2: Reenvoaws lambda invoke --function-name bach-messages-ProcessMessagesFunction-XXXXX response2.json# Estrategia 3: Visibility Timeoutaws lambda invoke --function-name bach-messages-ProcessWithVisibilityFunction-XXXXX response3.json# Estrategia 4: Dead Letter Queueaws lambda invoke --function-name bach-messages-ProcessWithDlqFunction-XXXXX response4.json

📊 Comparación de Estrategias

EstrategiaPérdida de DatosDuplicadosOperaciones ExtraComplejidadAnálisis de Fallos
SimpleNoNingunaBajaNo
ReenvíoNoPosibleSendMediaNo
VisibilityNoNoNingunaBajaLimitado
DLQNoNoSendAltaCompleto

🎯 Recomendaciones por Caso de Uso

Usar Estrategia Simple cuando:

  • Los mensajes no son crticos
  • El rendimiento es prioritario
  • La prdida ocasional es aceptable

Usar Estrategia de Reenvío cuando:

  • Necesitas modificar mensajes antes del reintento
  • Tienes lógica compleja de reintento
  • Puedes manejar duplicados

Usar Visibility Timeout cuando:

  • Quieres reintentos automáticos simples
  • El rendimiento es importante
  • Los fallos son temporales

Usar Dead Letter Queue cuando:

  • Los mensajes son críticos
  • Necesitas análisis de fallos
  • Requieres reprocesamiento manual
  • Tienes sistemas de monitoreo avanzados

🔧 Configuraciones Importantes

Visibility Timeout

MessagesQueue: Properties: VisibilityTimeoutSeconds: 30 # Tiempo antes de reaparecer

Message Retention

DeadLetterQueue: Properties: MessageRetentionPeriod: 1209600 # 14 das

Lambda Timeout

Globals: Function: Timeout: 3 # Debe ser menor que visibility timeout

📈 Métricas y Monitoreo

Para cada estrategia, monitorea:

  • Throughput: Mensajes procesados por segundo
  • Error Rate: Porcentaje de mensajes fallidos
  • Latency: Tiempo de procesamiento por lote
  • Queue Depth: Mensajes pendientes en cola
  • DLQ Messages: Mensajes en Dead Letter Queue (estrategia 4)

Y luego identifica cul es mejor para tu caso de uso.

🏁 Conclusiones

Cada estrategia tiene su lugar en diferentes arquitecturas:

  • Simple: Para casos no críticos con alta performance
  • Reenvío: Para lógica compleja de reintento
  • Visibility Timeout: Para reintentos automáticos eficientes
  • Dead Letter Queue: Para sistemas críticos con análisis de fallos

La eleccin depende de tus requisitos especficos de confiabilidad, rendimiento y observabilidad.

Recuerda: Estas recomendaciones aplican principalmente para consumidores únicos. En escenarios con múltiples consumidores, considera factores adicionales como concurrencia, orden de mensajes y coordinación entre procesos.

El código completo está disponible en el repositorio aws-examples con todas las implementaciones y documentación detallada.

Gracias por leer, Saludos!


Artículo original: 🏗️ 4 Estrategias para Procesar Mensajes SQS en Batch con AWS Lambda