AJUAREZ93
WorkTech ExplicadoNewsletterExperienceStackContact
Hire me
Tech Explicado

22 de julio de 2026

AWS SQS: el buffer que desacopla tu sistema (y cuándo no usarlo)

Cloud & InfraArchitecture & Tooling

El problema que SQS resuelve

Antes de hablar de colas hay que hablar de lo que pasa sin ellas: dos servicios que se llaman directo. Tu API recibe un request, necesita que otro servicio haga un trabajo —enviar un correo, redimensionar una imagen, invocar una — y llama a ese servicio síncronamente. Mientras espera la respuesta, ese worker de tu API está bloqueado. Si el servicio de destino está lento, tu API está lenta. Si está caído, tu API también falla, aunque el trabajo en sí no fuera urgente.

Ese acoplamiento es el problema real. No es solo velocidad, es disponibilidad compartida: dos servicios que antes fallaban de forma independiente ahora fallan juntos, porque uno depende de que el otro responda ahora mismo. Y cuando el tráfico sube, ese acoplamiento síncrono no absorbe la carga, la propaga: si el consumidor no puede procesar 500 requests por segundo, el productor tampoco puede terminar de responder, así que ambos se degradan al mismo tiempo.

SQS resuelve esto metiendo un buffer durable en medio. El productor ya no necesita que el consumidor esté disponible, rápido, ni siquiera corriendo en ese momento — solo necesita que la cola acepte el mensaje. Eso es lo que la infografía resume como "desacopla productor y consumidor", pero la parte que no se ve en cuatro palabras es cómo logra esa garantía sin perder mensajes ni duplicar trabajo sin control — y ahí es donde vale la pena entender el mecanismo real.

El flujo real: qué pasa entre "enviar" y "eliminar"

La infografía muestra cuatro pasos: productor envía → cola almacena → consumidor procesa → mensaje se elimina. Cada flecha de ese diagrama esconde una decisión de diseño que sí importa en producción.

Enviar no es empujar. A diferencia de un sistema pub/sub que empuja mensajes a quien esté escuchando, SQS no le avisa a nadie. El consumidor tiene que preguntar activamente si hay mensajes, con ReceiveMessage. Aquí aparece la primera decisión real: short polling (pregunta y regresa inmediato, aunque no haya nada) vs long polling (WaitTimeSeconds hasta 20 segundos — la conexión se queda abierta esperando a que llegue algo antes de regresar vacía). Long polling casi siempre es la opción correcta: reduce el número de requests vacíos a la API de SQS y baja la latencia percibida, porque el consumidor recibe el mensaje en el momento en que llega en vez de esperar al siguiente ciclo de polling.

"Consumidor procesa" no elimina el mensaje. Cuando un consumidor recibe un mensaje, SQS no lo borra — lo vuelve invisible para otros consumidores durante un periodo configurable: el Visibility Timeout. Si el consumidor termina y confirma con DeleteMessage antes de que ese timeout expire, el mensaje desaparece para siempre. Si no confirma a tiempo —porque el proceso crasheó, se quedó colgado, o simplemente tardó más de lo esperado— el mensaje vuelve a estar visible y otro consumidor (o el mismo, en el siguiente ciclo) lo va a recibir de nuevo. Esto es lo que le da a SQS su garantía de at-least-once delivery: nunca vas a perder un mensaje silenciosamente, pero sí puedes llegar a procesarlo más de una vez. Cualquier consumidor de SQS tiene que ser idempotente —procesar el mismo mensaje dos veces debe dar el mismo resultado que procesarlo una vez— porque tarde o temprano va a pasar, no es un caso extremo.

Los mensajes no viven para siempre. El Message Retention Period es de 4 días por default, configurable hasta 14. Si tu consumidor está caído más tiempo que eso, esos mensajes se pierden — no hay alerta automática, simplemente expiran. Es una razón más para monitorear la edad del mensaje más viejo en la cola (lo desarrollo abajo), no solo si la cola tiene mensajes o no.

Las cuatro piezas detrás de las feature cards

La infografía las resume en un check cada una. Vale la pena entender el mecanismo real detrás de cada check, porque son la diferencia entre "usé SQS" y "usé SQS bien".

Desacopla productor y consumidor. No es solo un desacople estructural (dos servicios distintos), es un desacople temporal. El productor no necesita que el consumidor esté corriendo en el mismo instante — puede estar desplegando una nueva versión, escalado a cero, o simplemente ocupado con otra cosa. Los mensajes esperan en la cola. Eso convierte una dependencia dura (el consumidor tiene que estar arriba ahora) en una dependencia blanda (el consumidor tiene que ponerse al día eventualmente), que es una garantía mucho más fácil de sostener en producción.

Reintentos automáticos. No son gratis — son el resultado directo del mecanismo de Visibility Timeout de arriba: si nadie confirma, el mensaje vuelve a aparecer. Lo que sí tienes que configurar a mano es cuándo dejar de reintentar. Eso se hace con una Redrive Policy: después de maxReceiveCount intentos fallidos, el mensaje se mueve automáticamente a una Dead Letter Queue (DLQ) en vez de seguir reintentando para siempre.

{
  "RedrivePolicy": {
    "deadLetterTargetArn": "arn:aws:sqs:us-east-1:123456789:mi-cola-dlq",
    "maxReceiveCount": 5
  }
}

Sin DLQ, un mensaje "envenenado" (uno que siempre falla, por un bug o un dato corrupto) se reintenta infinitamente, consumiendo capacidad de tu consumidor una y otra vez sin que nadie se entere. Con DLQ, después de 5 intentos se aparta a una cola separada donde puedes inspeccionarlo sin que bloquee el flujo normal.

Escala sin configuración manual. A diferencia de un sistema como Kafka, donde el throughput depende de cuántas particiones definiste de antemano, una cola SQS Standard no tiene un límite de throughput que tengas que planear — escala horizontalmente por detrás sin que declares capacidad. Es una de las razones por las que es la opción por default para "necesito una cola ya" sin pasar por una conversación de sizing.

Standard vs FIFO. Esta es la decisión de diseño más importante y la que menos gente evalúa a fondo. Standard da at-least-once delivery, no garantiza orden (un mensaje puede llegar antes o después de otro que se envió primero), y throughput prácticamente ilimitado. FIFO garantiza orden estricto dentro de un mismo MessageGroupId y entrega exactly-once (usando deduplicación por MessageDeduplicationId o content-based deduplication), a cambio de un límite de throughput —300 mensajes por segundo por defecto, hasta 3,000 con batching, o prácticamente ilimitado si activas high throughput mode con más de un MessageGroupId en paralelo—. Si tu caso de uso no necesita orden estricto, usar FIFO "porque suena más seguro" es pagar un límite de throughput real a cambio de una garantía que no estás aprovechando.

Casos de uso reales

Procesamiento asíncrono. El patrón más directo: la API recibe el request, valida lo mínimo indispensable, encola el trabajo pesado, y responde de inmediato.

import boto3

sqs = boto3.client('sqs')

def encolar_generacion_imagen(prompt, user_id):
    sqs.send_message(
        QueueUrl='https://sqs.us-east-1.amazonaws.com/123456789/generacion-imagenes',
        MessageBody=json.dumps({'prompt': prompt, 'user_id': user_id}),
    )
    return {'status': 'encolado'}

El usuario recibe una respuesta en milisegundos aunque el trabajo real tarde minutos — el mismo patrón que uso con para las llamadas a / en StudioEngine.ai, donde el trabajo pesado nunca corre en el request-response ciclo de la API principal.

Buffer ante picos de tráfico. Si tu consumidor procesa a un ritmo fijo (por ejemplo, un worker que solo puede atender 50 trabajos por minuto por límites de una API externa), una cola absorbe la diferencia entre "llegaron 5,000 requests en un minuto" y "solo puedo procesar 50 por minuto" sin tumbar nada — los mensajes simplemente esperan más tiempo en la cola en vez de saturar al consumidor o perderse.

Comunicación entre microservicios. Cuando el Servicio A necesita informarle algo al Servicio B pero no necesita una respuesta inmediata (ni siquiera necesita saber si B ya lo procesó), una cola es más simple y más resiliente que un HTTP call directo con su propio retry logic, timeouts, y circuit breakers reinventados a mano.

Trabajos en segundo plano. Envío de correos, generación de reportes, limpieza de datos — cualquier tarea que no bloquea al usuario pero sí necesita ejecutarse de forma confiable, con reintentos, sin que un fallo silencioso la pierda.

Buenas prácticas que casi nadie configura bien la primera vez

Define un Dead Letter Queue desde el día uno, no cuando ya tienes mensajes envenenados en producción. Sin DLQ no tienes forma de distinguir "la cola está vacía porque no hay trabajo" de "hay un mensaje que lleva 200 intentos fallando en silencio".

Ajusta el Visibility Timeout con el dato real, no con una adivinanza. AWS recomienda que sea al menos 6 veces el tiempo promedio de procesamiento de tu consumidor. Si tu timeout es más corto que el tiempo real de procesamiento, el mensaje se vuelve visible mientras todavía se está procesando — y otro consumidor lo toma, procesándolo dos veces en paralelo. Es uno de los bugs más comunes con SQS y el que menos se nota hasta que alguien pregunta por qué se enviaron dos correos idénticos.

No uses SQS para tiempo real estricto. El long polling reduce la latencia, pero sigue siendo un modelo de pull, no de push — no hay garantía de milisegundos, y en una cola Standard tampoco hay garantía de orden. Para notificaciones en tiempo real (un usuario viendo "escribiendo..." en un chat, actualizaciones de UI instantáneas), WebSockets o un sistema pub/sub como (con la limitación de que no persiste mensajes) encajan mejor con ese requisito.

Monitorea mensajes en cola, no solo si el consumidor está vivo. Las métricas que de verdad importan en CloudWatch son ApproximateNumberOfMessagesVisible (cuántos mensajes esperan) y, sobre todo, ApproximateAgeOfOldestMessage — si ese número empieza a crecer sostenidamente, tu consumidor no está siguiendo el ritmo del productor, y vas a tener mensajes expirando por retención antes de que alguien lo note si no tienes una alarma configurada sobre esa métrica específica.

Cuándo NO usar SQS

  • Cuando necesitas fan-out a múltiples consumidores. SQS es punto a punto: un mensaje lo procesa un solo consumidor de esa cola. Si cinco servicios distintos necesitan enterarse del mismo evento, necesitas SNS publicando a múltiples colas SQS (una por servicio), no una sola cola compartida.
  • Cuando necesitas orquestación con estado, no solo desacople. Si el flujo real es "haz A, luego B solo si A tuvo éxito, luego espera una aprobación humana antes de C", eso es un problema de orquestación (Step Functions, o un orquestador dedicado), no una cola — vas a terminar reconstruyendo máquina de estados a mano sobre mensajes.
  • Cuando la latencia tiene que ser predecible en milisegundos y estricta, como ya se mencionó: es un modelo de pull con polling, no la herramienta correcta para tiempo real duro.
  • Cuando un request síncrono simple es suficiente. Si el trabajo es rápido (unos milisegundos), no tiene beneficio real de desacoplarse, y el consumidor casi siempre está disponible, meter una cola de por medio solo agrega latencia, un componente más que puede fallar, y complejidad operativa —DLQ, visibility timeout, monitoreo— para un problema que un HTTP call directo ya resolvía bien.

Ejemplo práctico: una API que dispara Lambdas sin esperarlas

Este es el patrón que uso en producción: una API que necesita disparar trabajo en otra función sin bloquear al usuario ni acoplar su disponibilidad a la de esa función.

Primero, la cola con su DLQ configurada (Terraform):

resource "aws_sqs_queue" "tareas_dlq" {
  name                      = "tareas-dlq"
  message_retention_seconds = 1209600 # 14 días
}

resource "aws_sqs_queue" "tareas" {
  name                       = "tareas"
  visibility_timeout_seconds = 180 # 6x el tiempo promedio de procesamiento (~30s)

  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.tareas_dlq.arn
    maxReceiveCount     = 5
  })
}

La API encola sin esperar respuesta:

def handler_api(event, context):
    body = json.loads(event['body'])
    sqs.send_message(
        QueueUrl=QUEUE_URL,
        MessageBody=json.dumps({'tarea_id': body['id'], 'tipo': body['tipo']}),
    )
    return {'statusCode': 202, 'body': json.dumps({'status': 'aceptado'})}

Nota el 202 Accepted, no un 200 — le dice honestamente al cliente que el trabajo fue recibido, no que ya terminó.

Y la Lambda consumidora, disparada automáticamente por SQS vía event source mapping (AWS invoca la Lambda directo cuando hay mensajes, sin que tengas que hacer polling a mano):

def handler_worker(event, context):
    for record in event['Records']:
        tarea = json.loads(record['body'])
        try:
            procesar_tarea(tarea)  # idempotente: procesar dos veces no rompe nada
        except Exception as e:
            # No hacer catch-and-swallow: si re-lanzas, el mensaje NO se borra
            # y SQS lo reintenta según el Visibility Timeout configurado.
            raise

Si procesar_tarea lanza una excepción, Lambda no confirma el mensaje, SQS lo vuelve a poner disponible después del timeout, y tras 5 intentos fallidos se va a la DLQ automáticamente — sin una sola línea de retry logic escrita a mano en el consumidor.

Mi conclusión

SQS no es la pieza más emocionante de una arquitectura, y es precisamente por eso que es fácil subestimarla: no hace nada vistoso, solo se asegura de que un mensaje que entra, salga procesado —tarde o temprano, con reintentos, sin que nadie tenga que estar viéndolo—. Lo que he aprendido usándola para desacoplar APIs de Lambdas y Fargates es que el valor real no está en "tener una cola", está en las decisiones que obliga a tomar explícitamente: qué pasa si esto falla, cuánto tiempo es razonable reintentar, y qué haces con un mensaje que nunca va a poder procesarse. Esas preguntas existen con o sin SQS — la diferencia es que sin una cola, la mayoría de los equipos las responde de forma implícita (o no las responde, hasta que algo se cae en producción).


Este artículo acompaña la infografía de AWS SQS — parte de la serie Tecnolog-IA con Inteligenc-IA.

Dónde lo he usado

En producción

  • StudioEngine.ai — cuando la API principal necesita disparar un proceso pesado (por ejemplo, encolar una nueva generación de imagen para que la procese , o delegar una tarea puntual a una ), no llama directo al servicio de destino: publica un mensaje en una cola SQS. El consumidor correspondiente la procesa cuando tiene capacidad libre — si falla, SQS lo reintenta automáticamente sin que la API que lo originó tenga que saberlo, quedarse esperando, o implementar su propia lógica de retry.
AWS SQS
TrabajoIntercoProyectoStudioEngine.ai

Temas relacionados

Lambda vs Fargate: ¿cuál usar y cuándo?

Lambda vs Fargate: ¿cuál usar y cuándo?

Redis: ¿por qué es tan rápido? (y cuándo no usarlo)

Redis: ¿por qué es tan rápido? (y cuándo no usarlo)

AWS SQS: el buffer que desacopla tu sistema (y cuándo no usarlo)

Infografía generada con ChatGPT usando un prompt personalizado. —

AJUAREZ93

© 2026 Jose Alfonso Guerrero Juarez · Built from Guanajuato, México

github.com/ajuarez93 ↗