Ir al contenido principal
Laravel, shipping fast.

Capítulo 8

Idempotencia y reintentos seguros

Julian Beaujardin

Un consumidor envía POST /api/licenses. Tu API llama al proveedor, el proveedor crea la licencia y tu API escribe la respuesta. En algún punto entre tu servidor y el consumidor, la conexión se cae.

El consumidor vio un timeout. Desde donde está, hay dos posibilidades y ninguna forma de distinguirlas: la petición nunca llegó, o llegó y la respuesta se perdió. Así que hace lo único razonable. Lo intenta otra vez.

Ahora hay dos licencias.

Nadie cometió un error. El consumidor hizo bien en reintentar, y tu API hizo exactamente lo que se le pidió, dos veces. El Capítulo 7 le dio una forma a cada fallo. Este capítulo trata de los fallos que no la tienen, porque el consumidor nunca recibió respuesta alguna, y de conseguir que «inténtalo otra vez» sea algo seguro de decir.

Qué peticiones ya son seguras

HTTP define algunos métodos como idempotentes: enviar la petición dos veces deja al servidor en el mismo estado que enviarla una.

  • GET no cambia nada. Reintenta con libertad.
  • PUT y PATCH con valores absolutos fijan un estado. Fijar un nombre en «Partner A» dos veces da un consumidor llamado Partner A.
  • DELETE elimina una cosa. Eliminarla dos veces la deja eliminada.
  • POST crea. Dos veces crea dos.

Los métodos solo lo prometen. Tu código tiene que cumplir la promesa. El Capítulo 3 hizo que el driver tratara un 404 al borrar como un éxito, y eso es lo que hace que DELETE /api/licenses/{license} sea idempotente de hecho y no solo de nombre. Una API que responde 404 al segundo borrado ha convertido un reintento seguro en un error que el consumidor tiene que interpretar.

Vigila las excepciones. Un PATCH que dice «súmale 10 al límite de peticiones» no es idempotente. Diseña las actualizaciones como «fija este valor» siempre que puedas, y el método cumple su promesa.

Queda POST. No puede hacerse idempotente por naturaleza, así que hay que hacerlo idempotente por acuerdo.

La clave de idempotencia

El acuerdo es simple. El consumidor genera un valor único para cada operación que quiere realizar y lo envía en una cabecera:

POST /api/licenses
Idempotency-Key: 8e03978e-40d5-43e8-bc93-6894a57f9324

Si tiene que reintentar, envía otra vez el mismo valor. La parte del trato que le toca al servidor:

  1. La primera vez que ve una clave, hace el trabajo y recuerda la respuesta.
  2. Cualquier petición posterior con la misma clave recibe la respuesta recordada. El trabajo no se hace otra vez.
  3. La misma clave con un cuerpo distinto es un error, porque significa que el consumidor tiene un bug.
  4. A la misma clave que llega mientras la primera sigue en curso se le dice que espere.

La clave identifica una operación, no una petición. «Crear la licencia para acme.test» tiene una sola clave por muchas veces que se envíe.

No se pudo cargar el audio. Inténtalo de nuevo en un momento.