Altamar Labs
Volver a Blog
arquitectura· 3 min de lectura

Idempotencia: el problema que todos creen entender hasta que llega el primer duplicado

Casi todo ingeniero puede definir idempotencia en una entrevista. Muy pocos sistemas la implementan correctamente donde de verdad importa.

La definición de idempotencia es sencilla: una operación es idempotente si ejecutarla varias veces produce el mismo resultado que ejecutarla una sola vez. Cualquier ingeniero con un par de años de experiencia puede recitarla. Y sin embargo, es una de las garantías que con más frecuencia se rompe en producción, no porque nadie sepa la definición, sino porque nadie se detiene a preguntar dónde, específicamente, puede duplicarse una operación.

Por qué los duplicados son inevitables, no un edge case

En cualquier sistema que se comunica por red, hay una zona gris fundamental: el cliente envía una petición, el servidor la procesa, pero la respuesta se pierde antes de llegar de vuelta. Desde la perspectiva del cliente, la operación falló y debe reintentarse. Desde la perspectiva del servidor, la operación ya se ejecutó con éxito. Ese desacuerdo no es un bug, es una consecuencia matemática de comunicarse sobre una red que no garantiza entrega, y ocurre constantemente: timeouts, reinicios de proceso a mitad de una petición, colas de mensajes que reentregan al no recibir un ack a tiempo.

Si la operación es "leer un dato", el duplicado no importa. Si la operación es "cobrar una tarjeta" o "descontar inventario", un duplicado no manejado es dinero real perdido o ganado incorrectamente.

Dónde vive realmente la idempotencia

El error más común es tratar la idempotencia como una propiedad del código ("mi función no tiene efectos secundarios raros") en vez de una propiedad del protocolo entre dos sistemas. La idempotencia real requiere que el cliente y el servidor se pongan de acuerdo en algo muy concreto: una clave de idempotencia, generada por el cliente, que identifica de forma única ese intento específico de la operación, no la operación en abstracto, sino este intento particular, incluyendo sus reintentos.

Con esa clave, el servidor puede responder correctamente a la pregunta que realmente importa: "¿ya procesé esto antes?", y si la respuesta es sí, devolver el resultado ya calculado en lugar de ejecutar la operación de nuevo.

Los matices que separan una implementación correcta de una que solo parece correcta

  • La ventana de deduplicación tiene que sobrevivir tanto como los reintentos razonables del cliente. Si un cliente puede reintentar hasta 24 horas después de una falla de red prolongada, y el registro de idempotencia expira en 10 minutos, la protección es ilusoria para ese caso.
  • Guardar la clave no es suficiente, hay que guardar también el resultado. Si el servidor solo recuerda "ya vi esta clave" pero no qué respondió la primera vez, el segundo intento no puede devolver una respuesta consistente, solo puede rechazar la petición, lo cual rompe la experiencia para el cliente.
  • La operación de verificar y marcar tiene que ser atómica. Si dos peticiones con la misma clave llegan casi simultáneamente, y "verificar si ya existe" y "marcar como procesada" son dos pasos separados sin bloqueo, ambas pueden pasar la verificación antes de que ninguna marque nada, la misma condición de carrera que arruina cualquier sistema de reserva de asientos o inventario.

La lección

La idempotencia no falla porque el equipo no entienda el concepto. Falla porque se implementa como una casilla de verificación ("agregamos una tabla de idempotencia") sin razonar sobre las tres condiciones de arriba, que son las que realmente determinan si la protección funciona en el peor caso, no en el caso feliz donde todo llega en orden y a tiempo, sino en el caso donde una petición se retrasa, se duplica, o llega exactamente al mismo milisegundo que otra.