Saltar al contenido principal

Idempotencia

🎯 Objetivo

Regla

14.1: Se debe comprender y aplicar el concepto de idempotencia correctamente para los métodos HTTP que lo requieren (GET, PUT, DELETE, HEAD, OPTIONS). 14.2: Se debe proporcionar un mecanismo para que los clientes puedan realizar solicitudes POST o PATCH de forma idempotente cuando sea necesario para evitar efectos secundarios no deseados debido a reintentos de red.

📖 Concepto y Definición

Regla

14.3: Una operación (solicitud HTTP) es idempotente si realizarla múltiples veces tiene el mismo efecto en el estado del servidor que realizarla una sola vez. El resultado (respuesta) de la segunda y subsiguientes solicitudes puede diferir (ej. un DELETE puede dar 204 la primera vez y 404 las siguientes), pero el estado final del servidor debe ser el mismo. 14.4: Métodos HTTP Idempotentes por Naturaleza (Norma 3.3 - Propiedades):

  • GET, HEAD, OPTIONS: Son seguros y, por lo tanto, inherentemente idempotentes. Su implementación debe mantener esta propiedad.
  • PUT: Debe ser implementado como idempotente (reemplazo total del recurso).
  • DELETE: Debe ser implementado como idempotente (eliminar el recurso). 14.5: Métodos HTTP No Idempotentes por Naturaleza:
  • POST: Generalmente no es idempotente (ej. crea un nuevo recurso cada vez). Puede ser problemático si un cliente reintenta una solicitud POST debido a un fallo de red temporal (podría crear duplicados).
  • PATCH: No es inherentemente idempotente (ej. PATCH /counter { "op": "increment", "value": 1 }). Reintentarlo podría incrementar el contador múltiples veces.

Mecanismo Obligatorio para Idempotencia en POST/PATCH:

Regla

14.6: Para operaciones POST o PATCH donde la creación accidental de duplicados o la aplicación múltiple de efectos secundarios debido a reintentos sea un riesgo significativo (ej. crear un pago, procesar una orden), la API debe soportar un mecanismo de idempotencia basado en una clave proporcionada por el cliente. 14.7: El mecanismo obligatorio es el uso del encabezado Idempotency-Key en la solicitud:

  • El cliente debe generar una clave única (preferiblemente un UUID v4) para cada operación que desea realizar de forma idempotente.
  • El cliente debe enviar esta clave en el encabezado Idempotency-Key: <valor-uuid-unico>.
  • El cliente debe reintentar la misma solicitud (misma URI, método, cuerpo y Idempotency-Key) si no recibe una respuesta definitiva (ej. timeout de red). 14.8: El servidor debe:
  • Al recibir una solicitud POST o PATCH con Idempotency-Key, buscar si ya ha procesado (o está procesando) una solicitud con esa misma clave.
  • Si la clave es nueva: procesar la solicitud normalmente, almacenar el resultado (código de estado y cuerpo de la respuesta) asociado a esa clave idempotente durante un período razonable (ej. 24 horas), y devolver el resultado.
  • Si la clave ya existe y la operación original se completó: no volver a ejecutar la operación. En su lugar, debe devolver inmediatamente el resultado almacenado previamente (mismo código de estado y cuerpo).
  • Si la clave ya existe pero la operación original aún está en progreso: puede devolver un error de conflicto (ej. 409 Conflict) para indicar al cliente que espere y reintente más tarde, o esperar a que la operación original termine.

🤔 ¿Por qué es Importante? / Beneficios Clave (Contexto)

  • Fiabilidad: Evita la creación de recursos duplicados o la aplicación múltiple de acciones debido a problemas de red y reintentos del cliente.
  • Robustez: Hace que las interacciones con la API sean más resistentes a fallos transitorios.
  • Experiencia del Cliente: Previene resultados inesperados para el usuario final (ej. cobros duplicados).

✅ Buenas Prácticas y Recomendaciones Clave

Regla

14.9: Se refuerza la Norma 14.4: La implementación de PUT y DELETE debe ser estrictamente idempotente. 14.10: Se refuerza la Norma 14.6 y 14.7: El soporte del encabezado Idempotency-Key es obligatorio para operaciones POST y PATCH sensibles a duplicados o efectos secundarios por reintentos. 14.11: La unicidad de la Idempotency-Key debe ser gestionada por el cliente. Se recomienda el uso de UUIDs v4. 14.12: El servidor debe almacenar el resultado de la solicitud original (código de estado y cuerpo) asociado a la Idempotency-Key durante un tiempo suficiente para cubrir posibles reintentos, pero no indefinidamente (ej. 24 horas). 14.13: La implementación del almacenamiento y recuperación de claves/resultados idempotentes debe ser eficiente. 14.14: La documentación de la API (Norma 9.5) debe indicar claramente qué endpoints POST/PATCH soportan Idempotency-Key y cómo usarlo.

Don'ts (Prohibido):

  • 14.15: No se debe implementar PUT o DELETE de forma no idempotente.
  • 14.16: No se debe ignorar el encabezado Idempotency-Key si se recibe en un endpoint que lo soporta.
  • 14.17: No se debe reutilizar una Idempotency-Key para operaciones lógicamente diferentes.
  • 14.18: No se debe asumir que todas las operaciones POST o PATCH necesitan Idempotency-Key. Debe aplicarse donde el riesgo de reintento sea problemático.

💡 Ejemplos Prácticos (Ilustrativos)

Ejemplo 1: Cliente Realizando Pago con Idempotencia

Primera Solicitud:

POST /v1/charges HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: f1d2d2f9-1a2b-4c3d-8e4f-5a6b7c8d9e0f // UUID v4 generado por cliente

{
"amount": 1000,
"currency": "usd",
"source": "tok_visa"
}

Respuesta del Servidor (Éxito):

HTTP/1.1 201 Created
Content-Type: application/json
Request-Id: req_123 // ID único de esta solicitud

{
"chargeId": "ch_abc123",
"status": "succeeded",
"amount": 1000
}

El servidor almacena esta respuesta asociada a la clave f1d2d2f9-1a2b-4c3d-8e4f-5a6b7c8d9e0f.

Ejemplo 2: Cliente Reintentando Debido a Timeout

El cliente no recibió la respuesta anterior (timeout). Reintenta exactamente la misma solicitud:

POST /v1/charges HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: f1d2d2f9-1a2b-4c3d-8e4f-5a6b7c8d9e0f // MISMA CLAVE

{
"amount": 1000,
"currency": "usd",
"source": "tok_visa"
}

Respuesta del Servidor (Resultado Idempotente):

El servidor detecta la Idempotency-Key duplicada, no procesa el pago de nuevo, y devuelve la respuesta almacenada:

HTTP/1.1 201 Created // Mismo código de estado original
Content-Type: application/json
Request-Id: req_456 // Nuevo ID para esta solicitud, pero resultado original
Idempotency-Replayed: true // Encabezado opcional indicando reintento

{
"chargeId": "ch_abc123", // Mismo resultado original
"status": "succeeded",
"amount": 1000
}

Resultado: Solo se realizó un cobro.

🛠️ Herramientas y Consideraciones Adicionales (Contexto)

  • Generación de Claves: Los clientes deben usar generadores de UUID robustos.
  • Almacenamiento de Claves: El servidor necesita un almacenamiento eficiente (ej. Redis, base de datos) para las claves y resultados, con políticas de expiración.
  • Alcance de la Clave: La clave idempotente debe ser única por cliente/usuario autenticado para evitar colisiones.
  • Casos de Uso: Es especialmente crítico para APIs financieras, procesamiento de pedidos, o cualquier operación donde la ejecución múltiple sea perjudicial.