Español

Herramientas de desarrollo · UUID generador

Claves de idempotencia: uso de una UUID generada por el cliente para hacer que los reintentos sean seguros

· Por qué es importante

uuid criptografía APIS del navegador

Un diagrama de secuencia que muestra a un cliente enviando la misma clave de idempotencia dos veces y el servidor devolviendo la respuesta almacenada en caché
Ilustración de vector original de ToolAcre

Un tiempo de espera en una solicitud de pago lo deja sin estar seguro de si se realizó. Las claves de idempotencia le permiten reintentar de forma segura y una UUID generada por CSPRNG es la clave natural. Esta publicación explica el patrón de principio a fin.

El tiempo de espera que podría haberle cobrado al cliente dos veces; existen claves de idempotencia del modo de falla para solucionarlo

Un tiempo de espera durante una solicitud de pago crea una incertidumbre genuina para el cliente y el sistema. Su cliente HTTP dejó de esperar una respuesta, pero es posible que el servidor de pago haya procesado la transacción antes de que se cerrara la conexión o se agotara el tiempo de espera. Si vuelve a intentar realizar la misma solicitud, es posible que le cobre al cliente dos veces. Si no lo vuelve a intentar, el pago nunca se completa. El sistema de pago cae en un término medio infeliz: el dinero del cliente puede haberse agotado, puede llegar mañana, puede estar atrapado en una cola de procesamiento o puede que no haya salido de la cuenta en absoluto. Esta ambigüedad es inaceptable para los sistemas financieros.

Cómo funcionan las claves de idempotencia: el servidor almacena la primera respuesta bajo la clave y la reproduce para repeticiones

Las claves de idempotencia resuelven este problema de manera elegante al hacer que los reintentos sean seguros y deterministas. El cliente genera una clave única para cada intención (un pago, una transferencia, un cargo) y la incluye en cada solicitud. El servidor procesa el pago, almacena en caché la respuesta bajo esa clave y almacena tanto la clave como el resultado. Si la misma clave llega nuevamente dentro de una ventana de retención, el servidor reproduce la respuesta almacenada en caché sin procesar el pago nuevamente. El cliente puede volver a intentarlo con confianza sabiendo que exactamente la misma clave siempre producirá el mismo resultado, sin importar cuántas veces se envíe. Este patrón elimina la ambigüedad y hace que la lógica de reintento sea segura.

Generar antes del primer intento: por qué la clave debe existir antes de que salga la solicitud y reutilizarse palabra por palabra al reintentar

El patrón es más antiguo que las especificaciones HTTP modernas, pero ganó importancia en los pagos después de pérdidas financieras generalizadas y quejas de los clientes por cargos duplicados. Todas las API de pago y muchas API de servicios web ahora admiten claves de idempotencia. Una UUID generada por CSPRNG es la elección natural para la clave porque es imposible de adivinar, única sin ninguna coordinación entre clientes y no requiere asignación del lado del servidor ni autoridad central. El cliente lo genera antes del primer intento, lo reutiliza palabra por palabra en cada reintento y recibe la misma respuesta cada vez. No se necesita ningún estado del lado del servidor para coordinar la generación de claves.

Por qué un UUID aleatorio y no un contador o un hash de carga útil: unicidad sin coordinación y sin reutilización accidental entre intentos

La clave debe existir antes de que la solicitud abandone el cliente porque generarla al reintentar es demasiado tarde para garantizar la idempotencia. Si la primera solicitud tuvo éxito y se cobró al cliente, generar una nueva clave al reintentar enmascararía el problema y se cobraría nuevamente. El cliente debe comprometerse con una clave antes del primer intento, almacenarla en la memoria o en un almacenamiento persistente y reutilizar esa misma clave si es necesario un tiempo de espera o un reintento. Para las pruebas de API manuales, el generador ToolAcre produce claves que puede pegar en curl o en un cliente REST, copiarlas y reutilizarlas en múltiples solicitudes para probar el comportamiento de idempotencia.

Alcance y duración: claves por operación, por cuenta y durante cuánto tiempo el servidor debe recordarlas

¿Por qué un UUID en lugar de un hash o un contador secuencial para claves de idempotencia? Un hash de la carga útil de la solicitud parece intuitivo: cargas útiles idénticas obtienen hashes idénticos y, por lo tanto, claves idénticas. Pero los hashes son débiles para este caso de uso porque dos solicitudes casi idénticas con diferentes cantidades, diferentes destinatarios o diferentes parámetros producen hashes completamente diferentes y, por lo tanto, generan cargos separados, lo cual es correcto pero no proporciona toda la protección necesaria. Un contador secuencial requiere coordinación y estado distribuido: si dos clientes generan claves basadas en contadores en su infraestructura, sus contadores podrían colisionar. Un UUID no requiere una autoridad central, es imposible de adivinar y es extremadamente improbable que colisione por casualidad en todo Internet en todos los tiempos.

Ejemplo resuelto: una secuencia de reintento con la misma clave, que muestra lo que envía el cliente y lo que devuelve el servidor cada vez.

La implementación del lado del servidor almacena respuestas bajo claves y devuelve respuestas almacenadas en caché al repetirse. La complejidad está en decidir cuestiones operativas prácticas: el período de retención, cuánto tiempo recordar una clave, el tamaño de la caché, cuántas claves recordar, el bloqueo, cómo evitar que dos solicitudes simultáneas con la misma clave procesen el pago dos veces y la limpieza, cuándo olvidar una clave. Estas son preguntas sobre almacenamiento y confiabilidad fuera del alcance del generador UUID. El trabajo del cliente es generar una buena clave y reutilizarla en los reintentos; El trabajo del servidor es implementar el caché de forma correcta y duradera.

Lo que esto no cubre: el almacenamiento y el bloqueo del lado del servidor necesarios para implementar el patrón, que es un diseño separado.

Un ejemplo resuelto muestra una secuencia típica en la práctica. Una aplicación móvil necesita transferir dinero a un amigo mediante una API que admita idempotencia. Antes de enviar la solicitud, la aplicación genera un UUID usando su biblioteca criptográfica local o recupera uno del generador ToolAcre para fines de prueba: 3fa85f64-5717-4562-b3fc-2c963f66afa6. La aplicación envía una solicitud POST a /transfers con un cuerpo JSON y un encabezado HTTP Idempotency-Key: 3fa85f64-5717-4562-b3fc-2c963f66afa6. El servidor procesa la transferencia, almacena 3fa85f64-5717-4562-b3fc-2c963f66afa6 → {status: "success", transferId: "xfer-12345"} en su caché y devuelve una respuesta 200 con el resultado.

Conclusión: una intención, una clave: el generador ToolAcre le brinda un UUID respaldado por CSPRNG para usar como clave al probar una integración manualmente

La red agota el tiempo de espera y el cliente no ve la respuesta del primer intento. La aplicación vuelve a intentar la misma solicitud con la misma clave de idempotencia sin generar un nuevo UUID. El servidor reconoce la clave en su caché, encuentra la respuesta almacenada en caché e inmediatamente devuelve {status: "success", transferId: "xfer-12345"} sin procesar una nueva transferencia y sin cobrar nuevamente al cliente. La operación es idempotente: reintentar produce el mismo resultado observable cada vez. Para una prueba realizada, el generador ToolAcre puede proporcionar la clave; genere un UUID, inclúyalo en el encabezado, observe la respuesta y vuelva a enviarlo con la misma clave para verificar que el servidor implemente el almacenamiento en caché correctamente.