Conoce los límites
| Plataforma | Límites típicos (consulta la documentación vigente) | Señal |
|---|---|---|
| Telegram | Aprox. 1 msg/s por chat, 20 msg/min por grupo, unos 30 msg/s en total | 429 con parameters.retry_after |
| Discord | Buckets por ruta más unas 50 pet/s globales; límites más estrictos en algunas acciones | Cabeceras X-RateLimit-*, 429 con retry_after |
| Slack | Niveles por método; chat.postMessage aprox. 1 msg/s por canal |
429 con Retry-After |
| Rendimiento por número y niveles diarios de destinatarios únicos | Códigos de error en la respuesta | |
| APIs de IA | Peticiones y tokens por minuto | 429, cabeceras propias del proveedor |
Los límites son un contrato. Respetarlos sale más barato que recuperarse de un bloqueo.
Maneja bien el 429
Cuando recibas un 429, espera el tiempo que indica el servidor (Retry-After, retry_after) antes de reintentar esa ruta. No reintentes de inmediato en bucle: alarga la penalización y puede provocar bloqueos por IP. Bibliotecas como discord.js y grammY (con su plugin throttler) lo gestionan por ti; si haces llamadas HTTP directas, centralízalas en un cliente que lo haga.
Limita los mensajes salientes
Un token bucket permite ráfagas cortas mientras impone una tasa media:
class TokenBucket {
constructor(ratePerSec, burst) { this.rate = ratePerSec; this.cap = burst; this.tokens = burst; this.last = Date.now(); }
async take() {
for (;;) {
const now = Date.now();
this.tokens = Math.min(this.cap, this.tokens + ((now - this.last) / 1000) * this.rate);
this.last = now;
if (this.tokens >= 1) { this.tokens -= 1; return; }
await new Promise(r => setTimeout(r, ((1 - this.tokens) / this.rate) * 1000));
}
}
}
const global = new TokenBucket(25, 25); // por debajo del límite de difusión de Telegram
const perChat = new Map(); // chatId -> TokenBucket(1, 1)
async function send(chatId, payload) {
if (!perChat.has(chatId)) perChat.set(chatId, new TokenBucket(1, 1));
await global.take(); await perChat.get(chatId).take();
return telegram("sendMessage", { chat_id: chatId, ...payload });
}
Con varias instancias, guarda el bucket en un almacén compartido (Redis, un Durable Object) o envía todo a través de un único worker.
Colas
Pon en una cola todo lo lento o con picos: llamadas a IA, difusiones, procesamiento multimedia, llamadas a APIs de terceros. Ventajas: respuestas rápidas al webhook, carga suave, reintentos automáticos y una cola de mensajes fallidos para los que fallan una y otra vez, de modo que un elemento defectuoso no bloquee al resto. Haz idempotentes a los consumidores, porque las colas entregan al menos una vez.
Idempotencia y orden
- Deduplica los eventos entrantes por ID (
update_id,event_idde Slack,idde mensaje de WhatsApp, ID de interacción). - En conversaciones de varios pasos, guarda la transición de estado con un número de versión y rechaza actualizaciones obsoletas.
- No supongas orden entre chats distintos; dentro de un chat, procesa en secuencia si el orden importa.
Tiempos de espera y circuit breakers
Toda llamada saliente necesita un tiempo de espera. Si una dependencia (un proveedor de IA, una base de datos) está fallando, deja de llamarla durante un rato (circuit breaker) y responde con un mensaje amable ("Tengo problemas ahora mismo, inténtalo en unos minutos") en lugar de acumular peticiones.
Apagado ordenado y despliegues
Ante SIGTERM, deja de aceptar trabajo nuevo, termina los manejadores en curso dentro de un plazo, vacía los registros y cierra conexiones. En bots de Gateway, evita ejecutar dos instancias con el mismo shard durante un despliegue. En bots de webhooks los despliegues son transparentes si la versión antigua y la nueva pueden procesar eventos.
Observabilidad
- Registros estructurados (JSON) con ID de evento, plataforma, manejador, resultado, latencia y un hash del ID de usuario.
- Métricas: eventos recibidos, errores de manejadores, errores de API salientes por código, número de 429, profundidad y antigüedad de la cola, tokens de IA usados.
- Alertas: tasa de errores por encima de lo normal, antigüedad de la cola en aumento, ningún evento durante un periodo inusual (webhook roto), 401 repetidos (token revocado).
- Comando de estado: un
/statussolo para administradores que muestre versión, tiempo activo y profundidad de la cola es muy útil durante un incidente.
Pruebas
- Guarda payloads reales (limpios) como fixtures y ejecuta los manejadores contra ellos en pruebas unitarias.
- Simula las APIs de la plataforma, incluidas respuestas 429 y 5xx, para probar los caminos de reintento.
- Usa un bot de staging en un chat privado o servidor de pruebas antes de cada versión.
- Haz pruebas de carga de las difusiones con el limitador activo, contra un simulador, nunca contra usuarios reales.