APIs asíncronas, LAGs y AWS sin humo

Un recorrido práctico desde async/await hasta un sistema simple con workers, colas, LLMs y Bedrock.


Prólogo

Este libro está escrito para alguien que ya programa, probablemente en Python, y quiere entender cómo se conectan varias piezas que hoy aparecen una y otra vez en sistemas reales:

  • APIs
  • procesamiento asíncrono
  • colas y workers
  • observabilidad
  • LLMs
  • Bedrock
  • servicios básicos de AWS

No está pensado como enciclopedia. Está pensado como mapa mental + ejemplo continuo.

Durante todo el libro vamos a seguir un mismo caso:

El ejemplo del libro: TextFlow

TextFlow es un sistema simple que recibe textos, los procesa en segundo plano y deja listo un resultado para consultar después.

Al principio, el procesamiento será fake. Más adelante, el worker llamará a un LLM. Después veremos cómo ese mismo sistema podría vivir sobre AWS.

La idea es avanzar siempre sobre el mismo ejemplo, sin saltar a veinte proyectos distintos.

Qué vas a aprender

Al terminar el libro deberías poder razonar con claridad sobre estas preguntas:

  • ¿Qué significa realmente async y await?
  • ¿Cuándo conviene responder rápido y procesar después?
  • ¿Qué problema resuelven una cola y un worker?
  • ¿Cómo se integra un LLM sin meterlo en cualquier parte?
  • ¿Qué lugar ocupa Bedrock?
  • ¿Para qué sirven VPC, SQS, ECS, IAM y compañía?
  • ¿Cómo se ve un sistema simple, pero serio?

Qué no intenta hacer este libro

No busca:

  • enseñarte todo Python
  • cubrir todos los detalles de FastAPI
  • convertirte en experto en Kubernetes
  • darte una guía exhaustiva de seguridad cloud
  • reemplazar la documentación oficial de AWS

Sí busca algo más útil para esta etapa: que entiendas el flujo completo.


Parte I — El problema que queremos resolver

Capítulo 1 — Por qué una API no debería hacer todo en el request

Imaginá que TextFlow recibe un texto grande y debe devolver un resumen.

La primera idea suele ser esta:

  1. llega el request
  2. la API procesa todo
  3. devuelve el resultado

Suena simple, pero enseguida aparecen problemas.

Problema 1: el cliente queda esperando

Si el procesamiento tarda mucho, la API tarda mucho.

Problema 2: si el procesamiento falla, se mezcla todo

No queda clara la frontera entre:

  • recibir un pedido
  • ejecutar trabajo
  • guardar resultado
  • exponer estado

Problema 3: si querés escalar, todo está acoplado

La API y el procesamiento viven pegados.

La idea central del libro

En vez de hacer todo dentro del request, TextFlow va a funcionar así:

  1. el cliente envía un texto
  2. la API crea una tarea
  3. la API responde rápido con un task_id
  4. un worker procesa esa tarea aparte
  5. el cliente consulta el estado después

Diagrama base

flowchart TB
    A[Cliente] --> B[POST o tasks]
    B --> C[Crear tarea]
    C --> D[Encolar tarea]
    D --> E[Responder task_id]
    F[Worker] --> G[Tomar tarea de cola]
    G --> H[Procesar texto]
    H --> I[Guardar resultado]
    A --> J["GET /tasks/{id}"]
    J --> K[Consultar estado y resultado]

Esto ya nos obliga a pensar mejor.

No construimos “una API que hace magia”. Construimos un sistema con responsabilidades separadas.


Capítulo 2 — El modelo mental de TextFlow

Antes del código, definamos el dominio.

Qué hace TextFlow

TextFlow recibe un texto y produce un resultado procesado.

Más adelante ese resultado podrá ser:

  • un resumen
  • una clasificación
  • una extracción de puntos clave
  • una llamada a un LLM

Pero por ahora alcanza con pensar en una entidad principal.

La entidad Task

Toda tarea en TextFlow va a tener, como mínimo:

  • id
  • input_text
  • status
  • result
  • error
  • created_at
  • updated_at

Estados

Vamos a usar cuatro estados simples:

  • pending
  • processing
  • done
  • failed

Diagrama de estados

stateDiagram-v2
    [*] --> pending
    pending --> processing
    processing --> done
    processing --> failed
    done --> [*]
    failed --> [*]

Ese diagrama parece chico, pero es una gran mejora mental.

Ya no pensamos “la API resume textos”.

Ahora pensamos:

  • existen tareas
  • las tareas cambian de estado
  • distintas partes del sistema participan en esas transiciones

Parte II — Async sin misterio

Capítulo 3 — Qué significa async y await

Cuando alguien empieza con Python async, muchas veces piensa que async es “hacer todo más rápido”.

No exactamente.

La idea principal es otra:

permitir que el programa no se quede bloqueado esperando operaciones que tardan, especialmente operaciones de I/O.

I/O contra CPU

Hay dos tipos de trabajo que conviene distinguir:

Trabajo I/O-bound

Es el trabajo que pasa mucho tiempo esperando:

  • red
  • disco
  • base de datos
  • cola externa
  • servicios HTTP
  • LLMs

Trabajo CPU-bound

Es el trabajo que consume cálculo intenso:

  • compresión pesada
  • visión por computadora intensa
  • algoritmos matemáticos costosos
  • embeddings locales grandes

async brilla más en el primer grupo.

Qué hace await

await no significa “pausá todo”.

Significa algo más parecido a esto:

esta coroutine necesita esperar algo; mientras tanto, el event loop puede dejar correr otras tareas.

El event loop

El event loop es el coordinador.

No “hace el trabajo” por sí solo. Organiza la ejecución de coroutines y les da tiempo para avanzar cuando pueden hacerlo.

Diagrama mental

flowchart TB
    A[Coroutine A espera red] --> L[Event Loop]
    B[Coroutine B espera cola] --> L
    C[Coroutine C puede avanzar] --> L
    L --> C

Si una tarea está esperando una respuesta externa, otra puede avanzar.

Ese es el corazón del modelo.

Lo que sí y lo que no

async sí ayuda cuando:

  • una API espera respuestas de servicios externos
  • un worker consume una cola
  • varias tareas viven concurrentemente
  • una app necesita atender muchos eventos I/O

async no arregla mágicamente:

  • cálculos pesados de CPU
  • código mal diseñado
  • arquitectura confusa
  • bloqueos introducidos por librerías no async

Capítulo 4 — Cómo pensar un sistema async

No conviene empezar escribiendo funciones async al azar.

Conviene empezar por el flujo.

Flujo de TextFlow

flowchart TD
    A[Cliente envía texto] --> B[API crea Task]
    B --> C[Storage guarda pending]
    C --> D[Queue recibe task_id]
    D --> E[Worker toma task_id]
    E --> F[Storage cambia a processing]
    F --> G[Procesamiento]
    G --> H{Éxito?}
    H -->|Sí| I[Storage cambia a done]
    H -->|No| J[Storage cambia a failed]

Este diseño nos deja ver con claridad:

  • quién produce trabajo
  • quién consume trabajo
  • dónde vive el estado
  • dónde se maneja el error

Regla útil

Si una parte del flujo puede demorar, fallar o escalar por separado, probablemente merece estar desacoplada.

En TextFlow, el procesamiento cumple con las tres.


Parte III — La API de nuestro ejemplo

Capítulo 5 — El contrato HTTP de TextFlow

TextFlow necesita muy poco para empezar.

Endpoint 1: crear tarea

POST /tasks

Recibe algo así:

{
  "input_text": "Quiero resumir este texto"
}

Y devuelve algo así:

{
  "task_id": "abc123",
  "status": "pending"
}

La clave no está en el JSON. La clave está en el comportamiento:

  • crea una tarea
  • la guarda
  • la encola
  • responde rápido

No procesa.

Endpoint 2: consultar tarea

GET /tasks/{task_id}

Puede devolver:

{
  "task_id": "abc123",
  "status": "processing",
  "result": null,
  "error": null
}

O más adelante:

{
  "task_id": "abc123",
  "status": "done",
  "result": "Resumen generado",
  "error": null
}

Por qué este contrato es bueno para aprender

Porque te obliga a pensar en:

  • estados
  • persistencia mínima
  • asincronía real
  • separación entre aceptación y ejecución

Capítulo 6 — Storage simple: dónde vive el estado

Para aprender, TextFlow no necesita base de datos todavía.

Alcanza con un almacenamiento en memoria.

Qué guarda el storage

El storage es responsable de:

  • crear tareas
  • obtener tareas por id
  • actualizar estado
  • guardar resultado
  • guardar error

Qué no debería hacer el storage

No debería:

  • saber de HTTP
  • saber cómo se procesa el texto
  • llamar al LLM
  • conocer detalles del worker

Una idea importante

Aunque estés usando un simple diccionario en memoria, ya te conviene pensar como si existiera una pequeña capa de acceso a datos.

Eso evita que el diccionario quede manoseado desde todos lados.

Diagrama de responsabilidades

flowchart LR
    A[API] --> S[Storage]
    W[Worker] --> S
    P[Procesamiento] --> S

La API y el worker comparten el mismo estado, pero no deberían compartir el caos.


Parte IV — Cola y worker

Capítulo 7 — Qué es una cola y por qué importa

Una cola resuelve un problema muy concreto:

separar el ritmo al que se generan tareas del ritmo al que se procesan.

La API puede recibir diez pedidos en un segundo.

Eso no significa que tenga que procesarlos todos en ese segundo.

La cola sirve como buffer y como punto de desacople.

Cola local

En la primera versión de TextFlow vamos a usar una cola en memoria.

No es distribuida. No es persistente. No es enterprise.

Pero enseña la idea correcta.

Productor y consumidor

  • la API es el productor
  • el worker es el consumidor

Diagrama

flowchart LR
    A[API] --> B[Queue]
    B --> C[Worker]

Qué encolamos

Lo más simple y limpio es encolar el task_id.

Eso obliga al worker a buscar la tarea en el storage.

Ese detalle es bueno porque:

  • desacopla el mensaje del estado completo
  • evita duplicar información
  • se parece más a cómo pensarías un sistema real

Capítulo 8 — El worker como pieza separada

El worker es quien realmente ejecuta el trabajo.

Qué hace el worker

En loop:

  1. espera una tarea en la cola
  2. la toma
  3. cambia el estado a processing
  4. ejecuta el procesamiento
  5. guarda done o failed

Qué no hace el worker

No atiende requests HTTP.

No genera IDs.

No decide el contrato de la API.

Por qué esta separación importa

Porque después podrás escalar:

  • más instancias de API
  • más instancias de worker
  • otra cola
  • otra base
  • otro proveedor LLM

sin reescribir la idea base.

Diagrama más completo

flowchart TD
    A[Cliente] --> B[API]
    B --> C[Storage]
    B --> D[Queue]
    D --> E[Worker]
    E --> C
    E --> F[Procesador]
    F --> C

Capítulo 9 — Simular procesamiento de forma útil

Al principio, TextFlow no necesita un modelo real.

Conviene usar un procesamiento fake, por dos razones:

  1. separás la arquitectura del proveedor
  2. podés practicar estados y errores sin distraerte

Qué debería hacer el procesador fake

  • esperar un poco
  • transformar el texto de una forma simple
  • a veces fallar a propósito

Por qué forzar fallos

Porque los sistemas reales no se miden por el camino feliz.

Se miden por cómo se comportan cuando algo sale mal.

En TextFlow, forzar un fallo te deja practicar:

  • captura de excepciones
  • actualización a failed
  • separación entre error de una tarea y caída total del sistema

Parte V — Ciclo de vida y diseño limpio

Capítulo 10 — Startup, shutdown y tareas vivas

En una aplicación async, no alcanza con definir coroutines.

También hay que decidir cuándo nacen y cuándo terminan.

En TextFlow, el worker tiene que arrancar junto con la aplicación.

Startup

Cuando la app levanta:

  • crea la cola
  • inicializa el storage
  • lanza el worker

Shutdown

Cuando la app termina:

  • debería poder cerrar de forma ordenada
  • idealmente sin dejar tareas a medio camino sin saberlo

En nuestra primera versión no vamos a resolver todo esto perfecto.

Pero sí vamos a incorporar una idea sana:

una app no es solo endpoints; también tiene procesos vivos asociados.


Capítulo 11 — Diseño limpio sin ponerse solemne

Hay una tentación común: cuando uno aprende arquitectura, quiere meter veinte capas.

No hace falta.

Para TextFlow alcanza con distinguir cuatro zonas:

  • capa HTTP
  • capa de dominio o flujo
  • storage
  • worker/procesamiento

Una estructura mental razonable

flowchart TB
    H[HTTP / Endpoints] --> D[Dominio / Casos de uso]
    D --> S[Storage]
    D --> Q[Queue]
    W[Worker] --> S
    W --> P[Procesamiento]

No es una religión. Es una forma de no mezclar todo.

La pregunta útil

Cada vez que escribís algo, preguntate:

¿esta pieza está donde corresponde o está acá porque me dio pereza separar?


Capítulo 12 — Errores y resiliencia básica

TextFlow puede fallar por muchas razones.

  • input inválido
  • tarea inexistente
  • error de procesamiento
  • proveedor externo caído
  • timeout

Qué queremos lograr

No evitar todo fallo. Eso no existe.

Queremos que el sistema:

  • falle de forma entendible
  • marque estado correcto
  • no se rompa entero por una sola tarea

Fallar bien

“Fallar bien” en TextFlow significa algo simple:

  • si una tarea falla, termina en failed
  • el worker sigue vivo
  • otras tareas pueden seguir procesándose

Diagrama de fallo controlado

flowchart TD
    A[Worker toma tarea] --> B[Procesa]
    B --> C{Error?}
    C -->|No| D[Marcar done]
    C -->|Sí| E[Capturar excepción]
    E --> F[Marcar failed]
    F --> G[Continuar con siguiente tarea]

Parte VI — Observabilidad desde temprano

Capítulo 13 — Logs que sirven

Mucha gente agrega logs tarde y mal.

Conviene pensar logs desde el principio, aunque el sistema sea local.

Qué eventos vale la pena registrar en TextFlow

  • creación de tarea
  • encolado
  • inicio de procesamiento
  • fin correcto
  • fin con error

El task_id como hilo conductor

Si todo el flujo menciona el mismo task_id, ya tenés una trazabilidad básica.

Eso parece mínimo, pero es una enorme mejora operativa.

Logs buenos y malos

Malo

  • logs vagos
  • mensajes repetidos
  • no incluir identificadores

Bueno

  • eventos concretos
  • mensajes cortos
  • identificador de tarea
  • etapa del flujo

Capítulo 14 — Métricas mentales, aunque no tengas sistema de métricas todavía

Aunque todavía no tengas Prometheus ni CloudWatch, ya conviene pensar qué medirías.

En TextFlow las métricas naturales son:

  • cantidad de tareas creadas
  • cantidad de tareas completadas
  • cantidad de tareas fallidas
  • tiempo de procesamiento por tarea
  • tareas pendientes en cola

Por qué importa pensar esto temprano

Porque después te obliga a diseñar mejor.

Si ni siquiera sabés qué te gustaría medir, probablemente todavía no entendés del todo qué hace tu sistema.


Parte VII — Escalar el ejercicio mentalmente

Capítulo 15 — Qué cambia cuando hay más de un worker

Hasta ahora imaginamos un solo worker.

Pero si TextFlow creciera, podríamos tener varios.

Qué mejora

  • más throughput
  • menos espera en cola

Qué se vuelve interesante

  • orden de procesamiento
  • competencia por mensajes
  • estados consistentes

Diagrama con múltiples workers

flowchart LR
    A[API] --> B[Queue]
    B --> W1[Worker 1]
    B --> W2[Worker 2]
    B --> W3[Worker 3]

La idea importante

La cola deja de ser solo un buffer. Se vuelve un punto de distribución de trabajo.


Capítulo 16 — Qué cambia cuando el storage deja de ser en memoria

El storage local sirve para aprender, pero tiene límites obvios.

Si reiniciás la app, perdés todo.

Si corrés varias instancias, la memoria deja de ser compartida.

Qué aparecería en una siguiente versión

  • base de datos real
  • persistencia entre reinicios
  • mejor soporte para múltiples instancias

Pero la arquitectura conceptual no cambia.

Seguís teniendo:

  • API
  • cola
  • worker
  • estado persistente

Eso es importante: la idea sobrevive al cambio de herramientas.


Parte VIII — LLMs sin humo

Capítulo 17 — Qué es un LLM en el contexto de TextFlow

Ahora sí metamos inteligencia artificial al ejemplo.

Hasta acá, el procesamiento era fake. Ahora imaginemos que TextFlow quiere generar un resumen real usando un LLM.

Qué es un LLM, en términos prácticos

Un LLM es una pieza a la que le das:

  • una instrucción
  • un contexto
  • parámetros

Y te devuelve una salida generada.

No es una base de datos.

No es garantía de verdad.

No es lógica de negocio confiable por sí sola.

Es una herramienta probabilística útil para ciertas tareas.

En TextFlow, el LLM va a reemplazar al procesador fake

Antes teníamos:

  • esperar unos segundos
  • transformar el texto de forma simple

Ahora tendremos:

  • construir un prompt
  • invocar el modelo
  • recibir una respuesta
  • guardar el resultado

Diagrama actualizado

flowchart TD
    A[API] --> B[Queue]
    B --> C[Worker]
    C --> D[Construir prompt]
    D --> E[LLM]
    E --> F[Guardar resultado]

Capítulo 18 — Prompt, contexto y límites

No alcanza con “llamar un modelo”.

También hay que decidir qué le pedís y cómo.

Prompt

Es la instrucción que le das al modelo.

En TextFlow podría ser algo como:

  • resumí el siguiente texto en tres puntos
  • identificá ideas clave
  • devolvé JSON con ciertos campos

Contexto

Es la información que acompaña el pedido.

En nuestro caso, el contexto principal es el input_text.

Más adelante podría haber:

  • metadata del usuario
  • tipo de tarea
  • idioma
  • reglas de formato

Límites importantes

Un LLM:

  • puede tardar
  • puede fallar
  • puede responder mal
  • puede alucinar
  • tiene costo

Esa es una de las razones por las que encaja tan bien detrás de una cola y un worker.


Capítulo 19 — Integrar un LLM sin ensuciar todo

Una mala integración se ve así:

  • el endpoint llama directo al modelo
  • el prompt está hardcodeado en cualquier lado
  • no hay manejo claro de errores
  • no se registra nada útil

Una integración un poco más sana separa estas responsabilidades:

  • la API acepta el trabajo
  • el worker decide cuándo ejecutarlo
  • una capa de cliente LLM se encarga de invocar al proveedor
  • el resultado vuelve al storage

Cliente LLM

Conviene pensar una pequeña capa cliente que tenga a su cargo:

  • armar la request al proveedor
  • pasar parámetros relevantes
  • devolver una respuesta limpia a tu dominio
  • traducir errores del proveedor a algo manejable

Por qué esto importa

Porque más adelante podrías cambiar:

  • de Bedrock a otro proveedor
  • de un modelo a otro
  • de resumen a clasificación

sin reescribir toda la app.


Capítulo 20 — Timeouts, retries y costo

Cuando integrás un LLM aparecen tres preguntas prácticas:

¿Cuánto tarda?

Si tarda mucho, no conviene dejar al cliente colgado.

¿Qué pasa si falla?

Necesitás decidir si:

  • marcás failed
  • reintentás
  • degradás a un modo más simple

¿Cuánto cuesta?

Cada llamada puede tener costo por tokens o por uso.

Eso cambia cómo diseñás el sistema.

Qué conviene medir cuando entra un LLM

  • duración de invocación
  • cantidad de errores
  • tamaño aproximado de input
  • tamaño aproximado de output
  • cuántas tareas usan el modelo

Parte IX — Amazon Bedrock dentro del mapa

Capítulo 21 — Qué es Bedrock

Bedrock es una capa administrada de AWS para acceder a modelos fundacionales.

La ventaja conceptual es simple:

  • seguís dentro del ecosistema AWS
  • usás IAM y permisos consistentes
  • integrás inferencia con el resto de tu arquitectura cloud

Qué cambia en TextFlow si usamos Bedrock

Cambia el proveedor de inferencia.

No cambia la arquitectura principal.

Eso es bueno. Significa que el diseño estaba bien desacoplado.

Diagrama

flowchart TB
    A[Cliente] --> B[API]
    B --> C[Queue]
    C --> D[Worker]
    D --> E[Cliente Bedrock]
    E --> F[Modelo]
    D --> G[Storage]

Qué sigue igual

  • la API no procesa
  • el worker sigue siendo quien ejecuta
  • el estado sigue viviendo aparte

Qué aparece nuevo

  • permisos AWS
  • configuración del cliente
  • observabilidad del proveedor
  • límites y errores específicos de inferencia

Capítulo 22 — Cómo pensar la integración con Bedrock

No hace falta entrar a SDKs para entender el diseño.

La integración sana de TextFlow con Bedrock tendría estas piezas:

  • un worker que toma tareas
  • un cliente Bedrock que sabe invocar el modelo
  • prompts definidos con criterio
  • manejo de excepciones del proveedor
  • persistencia del output

Qué no conviene hacer

No conviene que Bedrock “invada” todo el sistema.

Por ejemplo, no hace falta que:

  • el storage sepa de Bedrock
  • el endpoint sepa cómo se arma el prompt
  • el modelo de dominio dependa de detalles del proveedor

Bedrock es una dependencia de infraestructura, no el corazón conceptual de tu aplicación.


Parte X — RAG y agentes, pero sin confundir niveles

Capítulo 23 — Qué es RAG y cuándo entra

RAG significa, en esencia:

  1. buscar contexto relevante
  2. dárselo al modelo
  3. generar con esa base

En TextFlow, RAG entraría si el resumen no dependiera solo del texto enviado, sino también de documentos relacionados.

Ejemplo:

  • el usuario sube un texto
  • el sistema busca políticas internas relacionadas
  • el modelo resume usando ambas cosas

Qué agrega RAG

  • una capa de recuperación
  • mejor grounding
  • más complejidad

Qué no conviene hacer todavía

No metas RAG antes de entender bien:

  • la API
  • la cola
  • el worker
  • la persistencia
  • el cliente LLM

RAG es una capa nueva. No una excusa para tapar una base floja.

Diagrama con RAG

flowchart TD
    A[Worker toma tarea] --> B[Buscar contexto]
    B --> C[Armar prompt con contexto]
    C --> D[LLM / Bedrock]
    D --> E[Guardar resultado]

Capítulo 24 — Qué es un agente y cuándo no hace falta

La palabra “agente” se usa demasiado. Conviene bajarla.

En términos simples, un agente es un flujo donde el modelo no solo genera texto, sino que también:

  • decide pasos
  • elige herramientas
  • ejecuta acciones
  • vuelve a razonar

En TextFlow, un agente sería necesario si…

En vez de solo resumir, el sistema tuviera que:

  1. clasificar el pedido
  2. decidir si buscar contexto
  3. consultar una herramienta
  4. sintetizar el resultado final

Cuándo no hace falta agente

Si solo querés resumir un texto, probablemente no hace falta.

Un error común es meter agentes donde alcanza con un pipeline claro.

Regla práctica

Si podés describir el flujo con pasos fijos, tal vez no necesitás un agente. Necesitás una orquestación simple.


Parte XI — AWS explicado simple

Capítulo 25 — Cómo pensar AWS sin memorizar servicios

AWS se entiende mejor si lo dividís por problemas, no por siglas.

Grandes categorías

  • red
  • compute
  • storage
  • bases de datos
  • mensajería
  • seguridad
  • observabilidad
  • IA

La pregunta correcta

En vez de preguntar “¿qué hace SQS?”, conviene preguntar:

tengo componentes desacoplados que necesitan intercambiar trabajo; ¿qué servicio de AWS resuelve eso?

Recién ahí aparece SQS.

Esa forma de pensar reduce muchísimo la confusión.


Capítulo 26 — VPC: para qué sirve de verdad

Una VPC es, conceptualmente, tu red privada dentro de AWS.

No es “para desplegar una API” en sí.

Es para controlar conectividad y aislamiento entre recursos.

Cuándo te importa una VPC

Cuando querés decidir cosas como:

  • qué queda expuesto a internet
  • qué queda privado
  • qué puede hablar con qué
  • cómo sale tráfico hacia afuera

Ejemplo con TextFlow

Imaginemos una versión cloud de TextFlow.

Podrías querer:

  • un balanceador público
  • servicios privados de API y worker
  • una base privada

Diagrama simple de red

flowchart TB
    U[Usuario en Internet] --> ALB[ALB público]
    ALB --> API[API en subnet privada]
    API --> DB[(Base privada)]
    API --> Q[SQS]
    Q --> W[Worker en subnet privada]
    W --> DB

Qué no conviene pensar

No pienses la VPC como una pieza aislada y misteriosa.

Pensala como el contenedor de tu topología de red.


Capítulo 27 — SQS: para qué sirve una cola real

SQS es una cola administrada.

En el libro empezamos con una cola en memoria. SQS es la versión cloud seria de esa idea.

Para qué lo usarías en TextFlow

Para desacoplar:

  • la API que recibe tareas
  • del worker que las procesa

Qué ventajas te da

  • absorber picos
  • desacoplar ritmos
  • distribuir trabajo entre workers
  • manejar reintentos

Diagrama

flowchart LR
    A[API] --> B[SQS]
    B --> C[Worker 1]
    B --> D[Worker 2]
    B --> E[Worker 3]

Qué idea conviene guardar

SQS no procesa nada. Solo ordena el intercambio de trabajo.


Capítulo 28 — ECS, EKS, Lambda y EC2: dónde corre tu código

Acá aparece una de las confusiones más comunes.

La pregunta no es “cuál es mejor”.

La pregunta es:

qué modelo operativo quiero para correr mi aplicación.

EC2

Una máquina virtual.

Te da control, pero también responsabilidad.

Lambda

Funciones que corren por evento.

Muy útil cuando el trabajo es pequeño, stateless y encaja bien en ese modelo.

ECS

Contenedores administrados por AWS.

Suele ser una opción muy razonable para APIs y workers si querés Docker sin meterte de lleno en Kubernetes.

EKS

Kubernetes administrado.

Útil cuando realmente necesitás el ecosistema y el modelo de Kubernetes. No siempre conviene por defecto.

Qué elegiría para TextFlow

Para una versión simple y seria:

  • API en ECS
  • worker en ECS
  • cola en SQS
  • storage en base o DynamoDB

Diagrama

flowchart TB
    U[Usuario] --> ALB[ALB]
    ALB --> ECS1[Servicio API en ECS]
    ECS1 --> SQS[SQS]
    SQS --> ECS2[Servicio Worker en ECS]

Capítulo 29 — S3, RDS, DynamoDB y Redis: qué guardar y dónde

Una app seria necesita decidir dónde vive cada tipo de dato.

S3

Para archivos y objetos.

RDS / Aurora

Para datos relacionales y consultas estructuradas.

DynamoDB

Para modelos clave-valor o documento, especialmente cuando el acceso por clave está muy claro.

Redis / ElastiCache

Para caché, estados rápidos, colas en algunos diseños, o datos temporales.

En TextFlow

Podrías imaginar algo así:

  • texto original grande en S3
  • estado de tarea en DynamoDB o Postgres
  • caché o rate-limiting en Redis

Diagrama simple

flowchart TD
    API[API] --> STATE[(Estado de tareas)]
    API --> FILES[(S3 para archivos)]
    Worker[Worker] --> STATE
    Worker --> FILES

La lección no es memorizar siglas. Es asociar tipos de datos con herramientas razonables.


Capítulo 30 — IAM y secretos: acceso sin caos

Cuando una app corre en AWS, necesita permisos.

No querés access keys pegadas por todos lados.

IAM

IAM resuelve identidad y permisos.

Roles

Una app debería tener un rol con los permisos mínimos que necesita.

Por ejemplo, el worker de TextFlow podría:

  • leer de SQS
  • escribir estado
  • invocar Bedrock

Y nada más.

Secretos

Para configuración sensible existen servicios específicos.

La idea de fondo es simple:

los permisos y secretos no deberían estar mezclados arbitrariamente con el código.


Capítulo 31 — CloudWatch y observabilidad en AWS

Todo lo que aprendimos localmente sobre logs y trazabilidad sigue siendo válido en AWS.

Solo cambia el entorno.

Qué querés observar en TextFlow cloud

  • logs de la API
  • logs del worker
  • errores del cliente LLM
  • tiempos de procesamiento
  • cantidad de tareas fallidas

La idea no cambia

Seguís queriendo poder responder preguntas como:

  • ¿entró la tarea?
  • ¿se encoló?
  • ¿la tomó un worker?
  • ¿falló Bedrock?
  • ¿se guardó el resultado?

Parte XII — Arquitectura completa del ejemplo

Capítulo 32 — La arquitectura final de TextFlow

A esta altura ya podemos mostrar el sistema completo, de punta a punta.

Versión local

flowchart TB
    U[Usuario] --> API[API async]
    API --> ST[(Storage en memoria)]
    API --> Q[Queue en memoria]
    Q --> W[Worker async]
    W --> LLM[Procesador fake o LLM]
    W --> ST
    U --> GET["GET /tasks/{id}"]
    GET --> ST

Versión cloud

flowchart TB
    U[Usuario] --> DNS[Route 53]
    DNS --> ALB[ALB o API Gateway]
    ALB --> API[ECS API]
    API --> DB[(RDS o DynamoDB)]
    API --> SQS[SQS]
    SQS --> W[ECS Worker]
    W --> DB
    W --> B[Bedrock]
    API --> CW[CloudWatch]
    W --> CW

Qué cambió entre la primera y la última versión

No cambió la idea principal.

Cambió la infraestructura que sostiene la idea.

Eso es exactamente lo que uno quiere cuando el diseño base es bueno.


Capítulo 33 — Qué ideas nuevas aparecieron y por qué valieron la pena

A lo largo del libro agregamos piezas nuevas cuando el ejemplo las necesitó.

Primero

  • task
  • estados
  • API
  • cola
  • worker

Después

  • logs
  • fallos controlados
  • múltiples workers
  • storage persistente mental

Más adelante

  • cliente LLM
  • prompt y contexto
  • tiempo, costo y errores de inferencia

Finalmente

  • Bedrock
  • VPC
  • SQS real
  • ECS
  • IAM
  • observabilidad cloud

La lógica del aprendizaje fue esta:

no meter tecnología por moda, sino cuando el ejemplo la reclama.

Ese es también un buen criterio para la vida real.


Parte XIII — Cierre

Capítulo 34 — Qué deberías llevarte de verdad

Si este libro funcionó, no solo aprendiste nombres.

Aprendiste a pensar sistemas.

Lo esencial

1. async y await

No son decoración. Son una forma de modelar espera sin bloquear.

2. Una API no tiene por qué hacer todo

Aceptar trabajo y procesarlo después suele ser mejor.

3. Una cola desacopla

No resuelve magia. Resuelve ritmos distintos y separación de responsabilidades.

4. Un worker ejecuta trabajo fuera del request

Eso te da flexibilidad y claridad.

5. Un LLM es una dependencia del sistema, no el sistema entero

Tiene que entrar en una arquitectura sana.

6. Bedrock es una forma de consumir modelos dentro del mapa AWS

No reemplaza diseño.

7. AWS se entiende mejor por problemas que por nombres

  • red
  • compute
  • storage
  • mensajería
  • seguridad
  • observabilidad
  • IA

La mejor conclusión posible

Si mañana tuvieras que diseñar una versión chica de TextFlow en serio, ya no arrancarías a ciegas.

Probablemente pensarías algo como esto:

  1. defino entidad y estados
  2. defino contrato API
  3. desacoplo procesamiento
  4. agrego cola y worker
  5. mido logs y errores
  6. recién después meto LLM y cloud

Ese orden mental vale muchísimo.


Apéndice A — Resumen de servicios AWS del libro

VPC

Para controlar red y aislamiento.

SQS

Para colas y desacople.

ECS

Para correr contenedores sin administrar Kubernetes directamente.

EKS

Para Kubernetes administrado si realmente lo necesitás.

Lambda

Para funciones por evento.

S3

Para archivos y objetos.

RDS / Aurora

Para datos relacionales.

DynamoDB

Para acceso clave-valor/documento a escala.

IAM

Para permisos.

CloudWatch

Para logs y métricas.

Bedrock

Para inferencia con modelos dentro del ecosistema AWS.


Apéndice B — Roadmap sugerido después de este libro

  1. implementar TextFlow local
  2. dockerizar API y worker
  3. pasar storage a SQLite o Postgres
  4. reemplazar cola en memoria por Redis o SQS
  5. integrar un LLM real
  6. mover el sistema a AWS
  7. agregar observabilidad seria
  8. experimentar con RAG
  9. recién después evaluar agentes

Apéndice C — Preguntas para verificar si entendiste

  • ¿Por qué el endpoint de creación no debería procesar el texto directamente?
  • ¿Qué problema resuelve una cola?
  • ¿Qué diferencia hay entre API y worker?
  • ¿Qué rol cumple el storage en el sistema?
  • ¿Qué cambia cuando el procesador fake pasa a ser un LLM?
  • ¿Qué valor agrega Bedrock y qué cosas no resuelve?
  • ¿Para qué sirve una VPC en el ejemplo de TextFlow?
  • ¿Cuándo elegirías ECS por sobre Lambda en este caso?
  • ¿Qué métricas mínimas mirarías si el sistema falla?

Epílogo

El error más común al estudiar estos temas es aprenderlos por separado:

  • por un lado async
  • por otro APIs
  • por otro AWS
  • por otro LLMs

En la práctica, lo difícil no es memorizar cada pieza. Lo difícil es ver cómo encajan.

Por eso seguimos un solo ejemplo durante todo el libro.

Si al terminar sentís que ahora podés mirar un diagrama de arquitectura y entenderlo con bastante más tranquilidad, entonces el libro cumplió su trabajo.