Guía10 min

Concurrencia y réplicas de un agente IA: cuándo no escalar

Resumen

Cómo no clonar el sqlite ni disparar 200 llamadas al modelo a la vez: Fluid Compute de Vercel, límites de Workers (128 MB, conexiones), fly scale count, réplicas en Railway y Compose. Un webhook no es un stateless API: la réplica extra a veces duplica el bot.

VercelCloudflareRailway
Varias réplicas de un agente frente a un solo volumen de estado

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

Escalar un agente no es replicas: 3. Tres procesos con el mismo token de Telegram y el mismo sqlite son tres bots peleando. Cold start cubre el primer hit lento. Esta guía cubre cuántos procesos a la vez y qué se rompe si subes el número. Fuentes oficiales consultadas el 3 de septiembre de 2026.

Healthchecks dicen si una réplica está viva. Backup cubre el disco. Graceful shutdown cubre matar una. Aquí: cuántas dejas vivas.

La regla

Estado local (sqlite, cola en RAM, un solo webhook) → una réplica de escritura. Stateless (proxy al modelo + tools HTTP) → el proveedor escala solo. Tope de llamadas al modelo en vuelo, no de contenedores.

Si no puedes decir dónde vive el estado, no subas réplicas.

Vercel

Fluid Compute reusa la instancia entre requests: varias invocaciones pueden compartir un mismo proceso. No pones un número de réplicas; Vercel prioriza instancias idle antes de abrir otra. En limitations (Fluid, consultado hoy): memoria máxima 2 GB Hobby / 4 GB Pro y Enterprise; duración Hobby 300 s (default y techo); Pro 300 s default, 800 s máximo, 1800 s extendido (beta). La concurrencia automática llega a 30.000 en Hobby/Pro y 100.000+ en Enterprise.

Concurrency scaling (docs, 11 agosto 2026): el burst es 1.000 ejecuciones concurrentes / 10 s / región. Si lo pasas, 503 FUNCTION_THROTTLED. Un agente en Route Handler: cada request es un turno. No clones Functions a mano. El peligro no es “poca réplica”; es N webhooks en paralelo gastando la misma API key.

Pon un semáforo en proceso (inFlight < 4) o una cola. Fluid no te lo pone. El tope de plataforma es de invocaciones, no de llamadas al modelo.

Workers

Límites verificados en docs (Free vs Paid, 3 sep 2026): 128 MB de memoria en ambos, 6 conexiones salientes simultáneas por request, CPU 10 ms Free / hasta 5 min Paid (default 30 s). Startup 1 s. No hay fly scale count: Cloudflare aísla por request. Un Worker no es un daemon de 3 réplicas.

Si necesitas un solo hilo de trabajo (un sqlite, un lock de chat), usa una cola (Queues) o un Durable Object de escritura, no replicas. Las 6 conexiones importan: un turno con modelo + tool + Telegram ya son tres sockets; un fan-out de tools las agota.

Fly.io

fly scale count 2 arranca dos Machines. No montan el mismo volumen: Fly documenta mapeo 1:1 — un volumen se adjunta a una Machine, una Machine monta un volumen. scale count crea volúmenes nuevos y vacíos (o reusa desadjuntos) y no copia datos. El resultado de subir count con sqlite en disco no es “database is locked” por dos writers en un archivo: son dos discos distintos, dos bots, cerebro partido.

Volumen = una Machine de escritura. Escala lecturas en otro proceso, no el bot que escribe sqlite. Autostop de cold starts no sustituye este techo. HA de sqlite en Fly es LiteFS u otro motor con replicación, no scale count 2.

Railway

Vertical por defecto: el servicio crece hasta el vCPU/RAM del plan. Horizontal: réplicas a mano en settings. Cada réplica tiene el techo completo del plan (docs: Pro hasta 24 vCPU / 24 GB por réplica; dos réplicas → hasta 48/48 repartidos). Tráfico público se reparte al azar en la región; multi-región, al más cercano y luego al azar. No hay sticky sessions.

Métricas de N réplicas se suman (2 × 100 MB = 200 MB en el tab). Horizontal + sqlite en volumen es carrera de writers: Railway no promete un writer único ni sesión pegajosa. Vertical (más RAM/CPU) es más seguro para un agente con estado. Réplicas tienen sentido si cada una es un worker de cola sin el webhook de Telegram.

Compose

deploy.replicas en Swarm/Compose no magia el sqlite. Un servicio agent con replicas: 2 y un bind mount es un incidente. O una réplica, o Postgres/Redis compartido.

Tabla

Una réplica con estado frente a varias sin estado

PlataformaPalanca verificadaEstado localStateless
VercelFluid + auto hasta 30k (Hobby/Pro); burst 1000/10s/regiónno clones; semáforo de LLMdeja Fluid
Workersisolate por request; 128 MB; 6 conexiones1 Durable Object de escriturarequest isolation
Flyscale count crea Machines; volumen 1:1, vacíos al escalarcount 1 + volumencount N sin volumen compartido
Railwayvertical al techo del plan; réplicas a mano; sin stickyvertical primeroréplicas si no hay sqlite local
Composedeploy.replicas1N detrás de cola

Errores comunes

Dos réplicas contestando el mismo chat

SíntomaCausaFix
El bot contesta dos vecesdos procesos, un webhookuna réplica o lock
sqlite “database is locked”dos procesos, un archivoun writer
dos sqlite vacíos tras scale countFly no replica volúmenescount 1 o LiteFS
429 del proveedorN turnos × toolssemáforo / cola
503 FUNCTION_THROTTLEDburst 1000/10s/regióncola, no más Functions
Worker 1102 / CPUloop de tools en un requestmenos tools o Paid + límite
Railway bill suberéplicas idlevertical, no N=5

Relación con el resto

Checklist

  • Sabes si el estado es local o externo
  • Webhook de prod → un solo proceso
  • Tope de llamadas al modelo en vuelo
  • Fly: no asumas que scale count comparte el sqlite
  • Railway: sin sticky; no uses réplicas para sesión en RAM
  • Workers: 6 conexiones / 128 MB no te sorprenden
  • Ensayo: dos mensajes a la vez, una sola respuesta

FAQ

¿Réplicas para HA? HA de un sqlite es backup + restore, no dos writers. Postgres o LiteFS sí.

¿Vercel se “queda corto”? Casi nunca es réplicas. Es maxDuration, el semáforo o un 503 de burst.

¿Compose en una VPS? replicas: 1 hasta que el estado salga del disco.

El ensayo: manda dos updates de Telegram en el mismo segundo. Una respuesta. Si ves dos, ya tenías concurrencia accidental.

Siguiente paso: si una réplica basta y igual se cae, alertas y logs. Sin runtime: curso.