REL04-BP04 Cómo hacer idempotentes las operaciones de mutación
Un servicio idempotente promete que cada solicitud se procesará una y solo una vez, de tal forma que hacer varias solicitudes idénticas tiene el mismo efecto que hacer una sola solicitud. De este modo, un cliente lo tiene más fácil para implementar los reintentos sin la preocupación de que una solicitud se procese varias veces por error. Para ello, los clientes pueden emitir solicitudes de API con un token de idempotencia, que se utiliza siempre que se repite la solicitud. Una API de servicio idempotente usa el token para devolver una respuesta idéntica a la que se devolvió por primera vez cuando se completó la solicitud, incluso aunque haya cambiado el estado subyacente del sistema.
En un sistema distribuido, es relativamente fácil llevar a cabo una acción una vez como máximo (el cliente solo hace una solicitud) o al menos una vez (sigue haciendo la solicitud hasta que el cliente obtiene una confirmación del éxito). Es más difícil garantizar que una acción se realice exactamente una vez, de modo que hacer varias solicitudes idénticas tenga el mismo efecto que llevar a cabo una sola solicitud. Con el uso de tokens de idempotencia en las API, los servicios pueden recibir una solicitud de migración una o más veces sin necesidad de crear registros duplicados ni efectos secundarios.
Resultado deseado: un enfoque coherente, bien documentado y ampliamente adoptado para garantizar la idempotencia de todos los componentes y servicios.
Patrones comunes de uso no recomendados:
-
Aplica la idempotencia de forma indiscriminada, incluso cuando no es necesaria.
-
Introduce una lógica demasiado compleja para implementar la idempotencia.
-
Usa las marcas de tiempo como claves para la idempotencia. Esto puede provocar imprecisiones debido al sesgo de reloj o a que varios clientes utilicen las mismas marcas de tiempo para aplicar los cambios.
-
Almacena cargas útiles completas para la idempotencia. Con este enfoque, se guardan las cargas útiles de datos completas de cada solicitud y se sobrescriben en cada nueva solicitud. Esto puede reducir el rendimiento y afectar a la escalabilidad.
-
Genera claves de forma incoherente en todos los servicios. Sin claves coherentes, es posible que los servicios no reconozcan las solicitudes duplicadas, lo que se traduce en resultados imprevistos.
Beneficios de establecer esta práctica recomendada:
-
Mayor escalabilidad: el sistema puede gestionar los reintentos y las solicitudes duplicadas sin tener que realizar una lógica adicional o una compleja gestión del estado.
-
Fiabilidad mejorada: la idempotencia ayuda a los servicios a gestionar varias solicitudes idénticas de manera coherente, lo que reduce el riesgo de efectos secundarios no deseados o registros duplicados. Esto es especialmente importante en los sistemas distribuidos, donde se producen fallos de red y reintentos con frecuencia.
-
Mejora de la coherencia de datos: dado que la misma solicitud produce la misma respuesta, la idempotencia ayuda a mantener la coherencia de datos en todos los sistemas distribuidos. Esto es esencial para mantener la integridad de las transacciones y las operaciones.
-
Gestión de errores: los tokens de idempotencia simplifican la gestión de errores. Si un cliente no recibe una respuesta debido a un problema, puede reenviar la solicitud de forma segura con el mismo token de idempotencia.
-
Transparencia operativa: la idempotencia permite una mejor supervisión y registro. Los servicios pueden registrar las solicitudes con sus tokens de idempotencia, lo que facilita el rastreo y la depuración de los problemas.
-
Contrato de API simplificado: puede simplificar el contrato entre los sistemas del cliente y del servidor y reducir la preocupación por posibles errores en el procesamiento de los datos.
Nivel de riesgo expuesto si no se establece esta práctica recomendada: medio
Guía para la implementación
En un sistema distribuido, es relativamente fácil llevar a cabo una acción una vez como máximo (el cliente solo hace una solicitud) o al menos una vez (sigue haciendo la solicitud hasta que el cliente obtiene una confirmación del funcionamiento correcto). Sin embargo, es difícil implementar un comportamiento que se dé una sola vez. Para lograrlo, sus clientes deben generar y proporcionar un token de idempotencia para cada solicitud.
Mediante el uso de fichas de idempotencia, un servicio puede distinguir entre solicitudes nuevas y solicitudes repetidas. Cuando un servicio recibe una solicitud con un token de idempotencia, comprueba si el token ya se ha utilizado. Si se ha utilizado el token, el servicio recupera y devuelve la respuesta almacenada. Si el token es nuevo, el servicio procesa la solicitud, almacena la respuesta junto con el token y, a continuación, devuelve la respuesta. Este mecanismo hace que todas las respuestas sean idempotentes, lo que mejora la fiabilidad y la coherencia del sistema distribuido.
La idempotencia también es un comportamiento importante de las arquitecturas basadas en eventos. Estas arquitecturas suelen estar respaldadas por una cola de mensajes como Amazon SQS, Amazon MQ, Amazon Kinesis Streams o Amazon Managed Streaming para Apache Kafka (MSK). En algunas circunstancias, un mensaje que se ha publicado solo una vez puede entregarse accidentalmente más de una vez. Cuando un publicador genera e incluye símbolos de idempotencia en los mensajes, solicita que al procesar cualquier mensaje duplicado recibido no se repita ninguna acción para el mismo mensaje. Los consumidores deben llevar un registro de cada token recibido e ignorar los mensajes que contengan tokens duplicados.
Los servicios y los consumidores también deberían transferir el token de idempotencia recibido a cualquier servicio posterior al que este llame. Todos los servicios posteriores de la cadena de procesamiento son igualmente responsables de garantizar que la idempotencia se implemente para evitar el efecto secundario de procesar un mensaje más de una vez.
Pasos para la implementación
-
Identifique las operaciones idempotentes
Determine qué operaciones requieren idempotencia. Por lo general, incluyen los métodos HTTP POST, PUT y DELETE y las operaciones de inserción, actualización o eliminación de bases de datos. Las operaciones que no cambian de estado, como las consultas de solo lectura, no suelen requerir idempotencia, a menos que tengan efectos secundarios.
-
Use identificadores únicos
Incluye un token único en cada solicitud de operación idempotente que envíe el remitente, ya sea directamente en la solicitud o como parte de sus metadatos (por ejemplo, un encabezado HTTP). Esto permite al receptor reconocer y gestionar las solicitudes u operaciones duplicadas. Los identificadores que se utilizan habitualmente para los tokens incluyen los identificadores únicos universales (UUID)
y los identificadores únicos clasificables por K (KSUID) . -
Rastree y gestione el estado
Mantenga el estado de cada operación o solicitud de su carga de trabajo. Esto se puede lograr almacenando el token de idempotencia y el estado correspondiente (como pendiente, completado o fallido) en una base de datos, caché u otro almacén persistente. Esta información de estado permite a la carga de trabajo identificar y gestionar las solicitudes u operaciones duplicadas.
Mantenga la coherencia y la atomicidad mediante el uso de los mecanismos de control de simultaneidad adecuados, si es necesario, como bloqueos, transacciones o controles de simultaneidad optimistas. Esto incluye el proceso de registrar el token idempotente y ejecutar todas las operaciones de mutación asociadas con la atención de la solicitud. Esto ayuda a prevenir las condiciones de carrera y verifica que las operaciones idempotentes se ejecuten correctamente.
Elimine periódicamente los tokens de idempotencia antiguos del almacén de datos para gestionar el almacenamiento y el rendimiento. Si su sistema de almacenamiento lo admite, plantéese utilizar marcas de tiempo de caducidad para los datos (conocidas como tiempo de vida o valores TTL). La probabilidad de que se reutilicen los tokens de idempotencia disminuye con el tiempo.
Las opciones de almacenamiento de AWS más comunes que se suelen utilizar para almacenar los tokens de idempotencia y el estado relacionado incluyen:
-
Amazon DynamoDB: DynamoDB es un servicio de base de datos NoSQL que proporciona un rendimiento de baja latencia y alta disponibilidad, lo que lo hace ideal para el almacenamiento de datos relacionados con la idempotencia. El modelo de datos de documentos y valores clave de DynamoDB permite almacenar y recuperar de forma eficiente los símbolos de idempotencia y la información de estado asociada. DynamoDB también puede hacer que los tokens de idempotencia caduquen automáticamente si la aplicación establece un valor TTL al insertarlos.
-
Amazon ElastiCache: ElastiCache puede almacenar tokens de idempotencia con alto rendimiento, baja latencia y bajo coste. Tanto ElastiCache (Redis) como ElastiCache (Memcached) también pueden hacer que los tokens de idempotencia caduquen automáticamente si la aplicación establece un valor TTL al insertarlos.
-
Amazon Relational Database Service (RDS): puede utilizar Amazon RDS para almacenar los tokens de idempotencia y la información de estado relacionada, especialmente si su aplicación ya utiliza una base de datos relacional para otros fines.
-
Amazon Simple Storage Service (S3): Amazon S3 es un servicio de almacenamiento de objetos duradero y altamente escalable que se puede utilizar para almacenar tokens de idempotencia y metadatos relacionados. Las capacidades de control de versiones de S3 pueden resultar particularmente útiles para mantener el estado de las operaciones idempotentes. La elección del servicio de almacenamiento suele depender de factores como el volumen de datos relacionados con la idempotencia, las características de rendimiento requeridas, la necesidad de durabilidad y disponibilidad y la forma en que el mecanismo de idempotencia se integra en la arquitectura de la carga de trabajo general.
-
-
Implemente operaciones idempotentes
Diseñe sus componentes de API y de carga de trabajo para que sean idempotentes. Incorpore controles de idempotencia en los componentes de su carga de trabajo. Antes de procesar una solicitud o realizar una operación, compruebe si el identificador único ya se ha procesado. Si es así, devuelva el resultado anterior en lugar de volver a ejecutar la operación. Por ejemplo, si un cliente envía una solicitud para crear un usuario, compruebe si ya existe un usuario con el mismo identificador único. Si el usuario existe, debería devolver la información del usuario existente en lugar de crear una nueva. Del mismo modo, si un consumidor de la cola recibe un mensaje con un token de idempotencia duplicado, debe ignorar el mensaje.
Cree conjuntos de pruebas integrales que validen la idempotencia de las solicitudes. Deben cubrir una amplia gama de escenarios, como las solicitudes correctas, las fallidas y las duplicadas.
Si su carga de trabajo aprovecha las funciones de AWS Lambda, puede usar Powertools para AWS Lambda. Powertools para AWS Lambda es un kit de herramientas para desarrolladores para implementar prácticas recomendadas sin servidor y aumentar la velocidad de los desarrolladores cuando trabaja con funciones de AWS Lambda. En concreto, proporciona una utilidad para convertir las funciones de Lambda en operaciones idempotentes que se pueden volver a intentar de forma segura.
-
Comunique la idempotencia con claridad
Documente su API y los componentes de la carga de trabajo para comunicar claramente la naturaleza idempotente de las operaciones. Esto ayuda a los clientes a entender el comportamiento esperado y cómo interactuar con su carga de trabajo de forma fiable.
-
Monitoree y audite:
Implemente mecanismos de supervisión y auditoría para detectar cualquier problema relacionado con la idempotencia de las respuestas, como las variaciones inesperadas de las respuestas o el exceso de gestión de solicitudes duplicadas. Esto puede ayudarlo a detectar e investigar cualquier problema o comportamiento inesperado en su carga de trabajo.
Recursos
Prácticas recomendadas relacionadas:
-
REL05-BP03 Control y limitación de las llamadas de reintento
-
REL06-BP01 Supervisión de todos los componentes de la carga de trabajo (generación)
-
REL06-BP03 Envío de notificaciones (procesamiento y alarmas en tiempo real)
-
REL08-BP02 Integración de las pruebas funcionales como parte de la implementación
Documentos relacionados:
Videos relacionados:
-
Building Distributed Applications with Event-driven Architecture. Charlas técnicas en línea de AWS
-
AWS re:Invent 2023 - Building next-generation applications with event-driven architecture
-
AWS re:Invent 2023 - Advanced integration patterns & trade-offs for loosely coupled systems
-
AWS re:Invent 2023 - Advanced event-driven patterns with Amazon EventBridge
-
AWS re:Invent 2019 - Moving to event-driven architectures (SVS308)
Herramientas relacionadas: