Ir al contenido

Idempotencia

Usted envía un POST para crear una venta. La conexión se cae antes de que la respuesta vuelva. Y ahora: ¿la venta se creó o no?

Sin ayuda, las dos salidas son malas. Si repite, puede crear dos ventas. Si no repite, puede no haber creado ninguna.

Envíe un encabezado Idempotency-Key con un valor suyo, único para esa operación:

Ventana de terminal
curl -X POST "https://api.fatureihoje.com/public/v1/clients" \
-H "Authorization: Bearer $FH_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6b7f0e8a-4c21-4d9e-9f3a-71c5d8b2e044" \
-d '{ "name": "Ana Souza" }'

El encabezado es el mismo en toda ruta de creación.

La API guarda la respuesta junto con esa clave. Si la misma clave llega de nuevo con la misma solicitud, devuelve la respuesta guardada, con el mismo estado y el mismo cuerpo, sin ejecutar nada otra vez. La respuesta repetida viene con el encabezado Idempotent-Replayed: true, y así usted sabe que el efecto ocurrió la primera vez.

Entonces la regla de reintento queda simple: repita la misma solicitud, con la misma clave, cuantas veces necesite.

  • Siempre que un POST cree algo, y sobre todo cuando repite la llamada después de un error de red, un tiempo agotado, un 500 o un 503.
  • En una cola y en un trabajo que corre de nuevo después de fallar. Genere la clave junto con la tarea y guárdela con ella, no en el momento de la llamada: generar una clave nueva en cada intento anula el mecanismo.

El valor puede ser cualquier texto. Un UUID versión 4 por operación es la opción más simple, y es lo que generan los ejemplos de abajo.

La clave sola no basta. La API también guarda una huella de la solicitud, hecha de cuatro cosas:

  1. el método;
  2. la ruta, en minúsculas y sin barra final;
  3. la consulta, la parte después del ?;
  4. el cuerpo, comparado por contenido y no por texto, así que el orden de los campos del JSON no importa.

Si la clave llega de nuevo con la misma huella, es repetición: vuelve la respuesta guardada. Si la huella es distinta, no es repetición, es un error, y la respuesta es 422 idempotency_key_reused.

CódigoEstadoCuándoQué hacer
idempotency_key_reused422Misma clave, solicitud distintaGenere una clave nueva para esta operación
idempotency_in_progress409La primera solicitud con esa clave todavía está en cursoEspere unos segundos y repita la misma solicitud, con la misma clave

En el 409, no genere una clave nueva. La primera llamada todavía puede terminar bien, y una clave nueva ejecutaría la operación una segunda vez, que es exactamente lo que este mecanismo existe para evitar.

Si el procesamiento falla, la clave se libera al instante, y el siguiente intento con ella corre de verdad. La reserva también tiene plazo: si el proceso muere a mitad de camino, vence en pocos minutos y la clave vuelve a aceptar intentos, en vez de quedar trabada por un día.

LímiteValor
Cuánto tiempo queda guardada la respuesta24 horas
Tamaño máximo del registro guardado256 KB
Tamaño máximo del cuerpo de la solicitud1 MB

Las 24 horas cuentan desde la primera llamada. Pasado ese plazo, la misma clave con la misma solicitud ejecuta de nuevo, porque ya no hay nada guardado para devolver.

El tope de 256 KB es de la respuesta guardada, no de la solicitud. Una respuesta mayor que eso no queda guardada entera: la API recuerda que la operación ya corrió, y la repetición recibe 409 en vez de repetir la respuesta. El efecto ocurrió una sola vez, que es lo que importa, pero usted necesita leer el recurso para saber cómo quedó.

La respuesta guardada vale para el acceso del momento en que se generó. Si la clave, o el miembro que representa, perdió un permiso desde entonces, la repetición no devuelve ese cuerpo: sin el permiso de la ruta la respuesta es 403, y con la ruta todavía permitida pero el acceso cambiado (por ejemplo, la clave perdió finance:read y la venta guardada traía los movimientos) la respuesta es 409 conflict. En los dos casos no se ejecuta nada de nuevo; lea el recurso para saber cómo quedó.

El tope de 1 MB es del cuerpo de la solicitud y vale para toda llamada, con o sin Idempotency-Key. Por encima de él la respuesta es 413 request_too_large.

Dos claves de API distintas tienen espacios de idempotencia separados. El mismo valor de Idempotency-Key en dos claves son dos operaciones, no una repetición, y ninguna ve la respuesta guardada de la otra.

Eso es bueno por un lado: dos sistemas de la misma empresa pueden generar el valor a su manera sin acordar nada.

Lo mismo vale para revocar una clave y crear otra: es una clave nueva, es un espacio nuevo.

Ventana de terminal
KEY=$(uuidgen | tr 'A-Z' 'a-z')
echo "$KEY"

uuidgen viene en macOS y en la mayoría de las distribuciones de Linux. Donde no exista, sirve cat /proc/sys/kernel/random/uuid.

  • Errores: el catálogo completo, con los dos códigos de idempotencia.
  • Autenticación: cómo funciona la rotación de clave, que es la trampa de arriba.