Saltar al contenido
nvíame
Capítulo 3 de 512 min19 sep 2026

Retries, idempotencia y colas

Cómo reintentar sin enviar dos veces el mismo SMS.

La red falla. Tu petición de envío puede llegar al proveedor, ejecutarse, y perderse la respuesta. Si reintentas a ciegas, el usuario recibe dos códigos. La idempotencia resuelve esto.

La clave de idempotencia

Generas un identificador único por intención de envío y lo mandas en un header. Si el proveedor ya procesó esa clave, devuelve el resultado original en vez de enviar de nuevo.

Envío con reintento seguro
−async function sendOtp(phone, code) {
+async function sendOtp(phone, code, requestId) {
+ const key = "otp:" + requestId; // estable entre reintentos
for (let i = 0; i < 3; i++) {
− try { return await api.post("/sms", { to: phone, text: code }); }
− catch { /* reintentar */ }
+ try {
+ return await api.post("/sms", { to: phone, text: code },
+ { headers: { "Idempotency-Key": key } });
+ } catch (err) {
+ if (!isRetryable(err)) throw err;
+ await sleep(backoff(i));
+ }
}
}
Mostrando: antes · haz clic para alternar

Colas

Para volumen, no envíes desde el request del usuario. Publica un trabajo en una cola; un worker lo consume, envía y registra el resultado. La cola te da reintentos, control de throughput y una dead letter queue para inspeccionar fallos permanentes.

La idempotencia se garantiza en el servidor, pero se habilita desde el cliente: es el cliente quien genera la clave y la reutiliza en cada reintento. Si generas una clave nueva por intento, anulas la protección.

Pruébalo

// Server-side dedupe by idempotency key
const vistos = new Map();

async function enviarIdempotente(key, msg) {
  if (vistos.has(key)) {
    console.log("duplicado ignorado:", key);
    return vistos.get(key);
  }
  const res = await mx.sms.send(msg);
  vistos.set(key, res);
  return res;
}

const key = "pedido-4821-despacho";
const msg = { to: "+56912345678", text: "Tu pedido 4821 fue despachado" };

// Simulate a client that retries after a timeout
await enviarIdempotente(key, msg);
await enviarIdempotente(key, msg);
await enviarIdempotente(key, msg);
console.log("mensajes enviados de verdad:", vistos.size);

Tres llamadas, un solo mensaje. Esa es la propiedad que quieres de cualquier operación que pueda reintentarse.

Se completa automáticamente al leer el 80%.

Comentarios (3)

Valentina Muñoz19 sep 2026

¿Cuánto tiempo guardo las claves de idempotencia?

Equipo nvíameAutor19 sep 2026

Al menos lo que dure tu ventana máxima de reintentos, más un margen. 24 horas es un valor habitual; con un TTL en tu almacén clave-valor se limpian solas.

Joaquín Vega19 sep 2026

Lo apliqué en el proyecto del trabajo. La sección «Retries, idempotencia y colas» me ahorró una discusión larga con el equipo: ahora tenemos un criterio compartido.

Matías Herrera19 sep 2026

¿Hay alguna diferencia importante si lo implemento en un servicio con varias instancias detrás de un balanceador?

Equipo nvíameAutor19 sep 2026

Sí: todo estado que hoy vive en memoria (contadores, sesiones, claves vistas) debe moverse a un almacén compartido con TTL, como Redis. Si no, cada instancia ve solo una parte de la historia.

Los comentarios se guardan solo en este navegador.