Patrón de transacción compensatoria (Compensating Transaction)

Use este patrón para deshacer el trabajo cuando se produzca un error en uno o varios pasos en una operación finalmente coherente. Las aplicaciones hospedadas en la nube que implementan procesos empresariales complejos y flujos de trabajo suelen usar operaciones que siguen el modelo de coherencia final.

Contexto y problema

Las aplicaciones en la nube suelen modificar los datos que se distribuyen entre varios orígenes de datos en diferentes ubicaciones geográficas. Para evitar la contención y mejorar el rendimiento en un entorno distribuido, las aplicaciones deben implementar la coherencia final en lugar de una coherencia transaccional sólida. En el modelo de coherencia definitiva, una operación empresarial habitual consta de una serie de pasos variados. Durante estos pasos, la vista general del estado del sistema podría ser incoherente. Pero el sistema debe volver a ser coherente cuando finalicen todos los pasos.

Gestionar los fallos de etapas presenta un desafío clave en el modelo de consistencia eventual. Después de una falla, es posible que deba deshacer el trabajo de los pasos de operación completados. Sin embargo, no siempre puede revertir los datos porque otras instancias de aplicación simultáneas podrían cambiar los datos. Incluso cuando las instancias simultáneas no cambian los datos, puede ser más complejo deshacer un paso que restaurar el estado original. Es posible que tenga que aplicar reglas específicas de la empresa. Para obtener un ejemplo, vea el ejemplo del sitio web de viajes.

Cuando una operación que implementa la coherencia final abarca varios almacenes de datos, debe acceder a cada almacén de datos para deshacer los cambios. Para evitar que el sistema permanezca incoherente, debe revertir de manera confiable el trabajo en todos los almacenes de datos.

Una operación que implementa la coherencia final no siempre almacena sus datos afectados en una base de datos. Por ejemplo, en un entorno de arquitectura orientada a servicios (SOA), una operación puede invocar una acción en un servicio y cambiar el estado que contiene el servicio. Para deshacer la operación, también debe deshacer este cambio de estado, lo que puede implicar volver a invocar el servicio para revertir los efectos de la primera acción.

Solución

Implemente una transacción de compensación que deshaga los efectos de los pasos completados en la operación original. Puede pensar que simplemente puede restaurar el sistema a su estado original, pero este enfoque puede sobrescribir los cambios de otras instancias de aplicación simultáneas. En su lugar, la transacción de compensación debe tener en cuenta de forma inteligente el trabajo simultáneo. Este proceso suele ser específico de la aplicación y depende de la operación original.

Puede usar un flujo de trabajo para implementar una operación finalmente coherente que requiera compensación. A medida que se ejecuta la operación original, el sistema registra información sobre cada paso y cómo deshacerla. Si la operación falla, el flujo de trabajo retrocede a través de los pasos completados e invierte cada paso.

Diagrama que muestra los pasos para crear un itinerario y los pasos de la transacción de compensación que cancelan el itinerario.

Aunque cada paso es una acción independiente, juntas forman una operación finalmente coherente. El sistema debe realizar los pasos y las operaciones de deshacer correspondientes para cada paso. Si el cliente cancela, estas operaciones de deshacer se pueden ejecutar como una transacción de compensación.

Un error de un solo paso no siempre requiere que revertir el sistema completo mediante una transacción compensatoria. Por ejemplo, en un escenario de sitios web de viajes, un cliente reserva vuelos F1, F2 y F3, pero no reserva una habitación en el hotel H1. Ofrecer al cliente una habitación en un hotel diferente es preferible a cancelar los vuelos. El cliente todavía puede optar por cancelar, lo que desencadena la transacción de compensación para deshacer las reservas de vuelos. Sin embargo, el cliente debe tomar esta decisión, no el sistema. Cuando las decisiones tienen un alto impacto o son difíciles de automatizar de forma confiable, incluya un humano en el proceso de toma de decisiones.

Tenga en cuenta estos puntos importantes:

  • Es posible que una transacción de compensación no necesite deshacer el trabajo en el orden inverso exacto de la operación original.

  • Puede que sea posible realizar algunos pasos de deshacer en paralelo.

  • Es posible que tenga que aplicar reglas específicas de la empresa. Por ejemplo, cancelar una reserva de un vuelo no dará derecho al cliente a un reembolso completo.

Este enfoque es similar al patrón de transacciones distribuidas de Saga.

Las transacciones de compensación son operaciones eventualmente consistentes y pueden fallar. El sistema debe registrar el progreso para que pueda reanudar la transacción de compensación desde el punto de error. Un paso puede ejecutarse varias veces cuando se reintenta, por lo que debe diseñar cada paso como un comando idempotent.

A veces, la intervención manual es la única manera de recuperarse de un paso erróneo. En estas situaciones, el sistema debe generar una alerta que incluya información detallada sobre el motivo del error.

Problemas y consideraciones

Tenga en cuenta los siguientes puntos a medida que decida cómo implementar este patrón:

  • Es probable que no sea fácil determinar el momento en el que ha dado error un paso de una operación que implementa una coherencia definitiva. Es posible que un paso no produzca un error inmediatamente, sino que se bloquee. Es posible que tenga que implementar un mecanismo de tiempo de espera.

  • No es fácil generalizar la lógica de compensación. Una transacción de compensación es específica de la aplicación. Se basa en la aplicación que tiene suficiente información para deshacer los efectos de cada paso en una operación con error.

  • Las transacciones de compensación no siempre funcionan. Defina los pasos de una transacción de compensación como comandos idempotentes para que pueda repetirlos si se produce un error en la transacción de compensación.

  • La infraestructura que controla los pasos debe cumplir los siguientes criterios:

    • Es resistente tanto en la operación original como en la transacción de compensación.

    • No pierde la información necesaria para compensar un paso fallido.

    • Supervisa de forma confiable el progreso de la lógica de compensación. Las transacciones de compensación se ejecutan después de la confirmación de las operaciones originales y otras transacciones pueden cambiar los estados intermedios. Por lo tanto, asegúrese de que puede correlacionar y auditar tanto la operación original como su compensación de un extremo a otro.

  • Una transacción de compensación no devuelve los datos del sistema al estado que tenía al inicio de la operación original. En su lugar, la transacción compensa el trabajo que la operación completó correctamente antes de fallar.

  • Los pasos de transacción de compensación no siempre invierten la operación original en el orden opuesto exacto. Por ejemplo, si un almacén de datos es más sensible a las incoherencias que otros, deshaga primero los cambios en ese almacén.

  • Algunas medidas pueden ayudarle a mejorar las tasas de éxito. Puede colocar un bloqueo a corto plazo con un tiempo de espera en cada recurso necesario para completar una operación. Puede adquirir estos recursos de antemano y, a continuación, realizar el trabajo solo después de adquirir todos los recursos. Finalice todas las acciones antes de que expiren los bloqueos.

  • La lógica de reintento que trata más errores como transitorios puede ayudar a minimizar los errores que desencadenan una transacción de compensación. Cuando se produce un error en un paso de una operación que implementa la coherencia final, controle como una excepción transitoria y vuelva a intentar el paso. Solo detenga la operación y desencadene la compensación si el paso falla repetidamente o no pueda recuperarse. Para obtener más información sobre las estrategias de reintento, consulte Control de errores transitorios.

  • Al implementar una transacción de compensación, se enfrentan a muchos desafíos similares a la implementación de la coherencia final. Para obtener más información, vea Minimizar la coordinación.

  • Defina puntos claros sin retorno ni pasos irreversibles. En flujos de trabajo complejos, no se pueden deshacer de manera segura o con sentido algunas operaciones, como efectos secundarios externos o acciones legalmente vinculantes. Identificar pasos compensables frente a irreversibles. Diseñe el flujo de trabajo para que los pasos irreversibles se produzcan solo después de que todas las validaciones críticas se realicen correctamente.

Cuándo usar este patrón

Use este patrón en los siguientes supuestos:

  • Una operación empresarial abarca varios pasos, servicios o almacenes de datos y debe deshacerse si se produce un error en un paso posterior. Este escenario suele producirse en flujos de trabajo de larga duración que siguen un modelo de coherencia final y no pueden confiar en transacciones atómicas.

  • La recuperación de errores a menudo requiere lógica específica del dominio en lugar de una reversión de datos simple. Use acciones de compensación cuando deshacer el trabajo requiere que aplique reglas de negocio, como cancelar reservas o emitir reembolsos parciales.

Este patrón podría no ser adecuado cuando:

  • Las operaciones se pueden reintentar de forma segura y la mayoría de los errores son transitorios. La lógica de reintento a menudo es suficiente en estos casos y las transacciones de compensación agregan complejidad innecesaria.

  • El sistema no puede tolerar incoherencia temporal o la compensación no puede restaurar de forma confiable un estado válido. Use mecanismos de coherencia fuerte o transacciones atómicas en todos los pasos en su lugar.

Diseño de cargas de trabajo

Evalúe cómo usar la transacción de compensación en el diseño de una carga de trabajo para abordar los objetivos y principios descritos en los pilares de Azure Well-Architected Framework. En la tabla siguiente se proporciona una guía sobre cómo este patrón apoya los objetivos de cada pilar.

Fundamento Cómo apoya este patrón los objetivos de los pilares
Las decisiones de diseño de fiabilidad ayudan a que su carga de trabajo sea resiliente a fallos y garantizan que se recupere a un estado de pleno funcionamiento después de que se produzca un fallo. Las acciones de compensación abordan el mal funcionamiento de las rutas de trabajo críticas mediante procesos como revertir directamente los cambios de datos, interrumpir bloqueos de transacción o incluso ejecutar el comportamiento del sistema nativo para revertir el efecto.

- RE:02 Flujos críticos
- RE:09 Recuperación ante desastres

Si este patrón introduce concesiones dentro de un pilar, considérelas en relación con los objetivos de los otros pilares.

Ejemplo

En el diagrama siguiente se muestra una implementación práctica Azure del patrón de transacción de compensación. Otras implementaciones también pueden funcionar para sus requisitos de carga de trabajo. Un orquestador que se ejecuta en Azure Container Apps coordina cada paso de un flujo de trabajo de larga duración mediante el envío de comandos a través de Azure Service Bus. A medida que cada paso adelante se realiza correctamente, el orquestador registra el estado de ejecución y la acción de compensación correspondiente en Azure Cosmos DB para que el flujo de trabajo se pueda reanudar, correlacionar y auditar.

Diagrama que muestra una implementación de Azure del patrón de transacción compensatoria.

Este modelo utiliza primero los reintentos para conservar el progreso. Si se produce un error en un paso, el orquestador aplica lógica de reintento para errores transitorios e intenta continuar con la operación original. La compensación solo se invoca cuando el progreso hacia delante se vuelve imposible, como cuando se agotan los reintentos o el error se clasifica como notransciente.

Las reglas específicas de la empresa también pueden preferir el progreso hacia delante sobre la compensación inmediata. Si se produce un error en un paso, el orquestador puede seleccionar una ruta alternativa, como sustituir un servicio equivalente o una opción de reserva, en lugar de revertir el flujo de trabajo. Para casos de alto impacto o ambiguos, puede pausar el flujo de trabajo para la revisión humana, antes de decidir si desea continuar en una ruta alternativa o activar compensación. Este enfoque trata la compensación como último recurso y permite que las reglas de dominio impulsen las decisiones de recuperación.

En una secuencia típica, el orquestador envía mensajes de pasos a través de Service Bus (pasos 1 y 2), recibe resultados exitosos y almacena los metadatos de compensación y reenvío en Azure Cosmos DB.

Puede desencadenar una compensación de dos maneras:

  • Cuando un paso posterior de la misma carga de trabajo falla y tiene que deshacer los pasos que fueron exitosos anteriormente. Esta compensación puede ocurrir inmediatamente cuando un paso devuelve un error empresarial, como un error de validación de reglas, o después de que se agoten los intentos técnicos y el mensaje se mueve a la cola de letra muerta.

  • Cuando un cliente posterior solicita explícitamente que cancele una operación completada.

En cualquier caso, el orquestador lee los registros de compensación almacenados y envía comandos de compensación al servicio correspondiente. Si se produce un error transitorio en un paso de compensación, los reintentos de Service Bus pueden completarlo sin escalar el problema.

Si los reintentos repetidos siguen fallando, Service Bus mueve el mensaje a una cola de mensajes fallidos y conserva los detalles del error. El orquestador, o un procesador dedicado de mensajes fallidos, genera una alerta y emite telemetría estructurada, incluidas la razón del fallo y los identificadores de correlación, hacia Azure Monitor y Log Analytics, que se pueden visualizar en Application Insights. Esta ruta operativa ayuda a los equipos a diagnosticar errores, determinar la necesidad de intervención manual y mantener la rastreabilidad en los flujos originales y compensadores.

El flujo de trabajo puede iniciar la compensación automáticamente por condiciones claras y de bajo riesgo o pausar para la revisión humana cuando la situación es ambigua, de alto impacto o requiere una decisión manual.

Use identidades administradas y autorización basada en Microsoft Entra ID entre componentes para evitar secretos compartidos y aplicar el acceso con privilegios mínimos. Al crear un diagrama de referencia simplificado, trate estos controles de identidad y autorización como problemas de implementación de línea base en lugar de pasos de flujo explícitos. Mantenga el diagrama centrado en la orquestación, reintento, compensación y control de errores.

  • Consideraciones de datos para microservicios: obtenga información sobre por qué la coherencia final y el error parcial son inherentes a los sistemas distribuidos. El patrón de transacción de compensación proporciona un mecanismo concreto para controlar esos errores cuando las operaciones abarcan varios servicios.

  • Patrón de bandeja de salida transaccional con Azure Cosmos DB: utilice este patrón cuando las transacciones de compensación necesiten publicar eventos o comandos de manera confiable. Ayuda a garantizar que los cambios de estado y los mensajes se registran de forma atómica, lo que evita la pérdida de mensajes.

  • Diseño para la recuperación automática: use transacciones compensatorias como parte de un enfoque de recuperación automática para las aplicaciones.

  • Patrón de Supervisor del Agente de Planificación: Utilice este patrón para implementar sistemas resilientes que ejecutan operaciones de negocio en servicios y recursos distribuidos. A veces, estos sistemas necesitan transacciones compensatorias para deshacer el trabajo.

  • Patrón de reintento: use este patrón para controlar errores transitorios y minimizar la necesidad de compensar transacciones.

  • Patrón Saga de transacciones distribuidas: usar este patrón para administrar la consistencia de los datos entre microservicios en transacciones distribuidas. Saga usa transacciones compensatorias para la recuperación de errores.

  • Patrón de Tubos y Filtros: utiliza este patrón con el patrón de Transacción de Compensación como alternativa a las transacciones distribuidas cuando descompones tareas complejas en pasos reutilizables.