Patrón Compensating Transaction (Transacción de compensación)

Azure

El patrón de transacción de compensación le es útil cuando usa una operación que termina por ser coherente y que consta de una serie de pasos. En concreto, si se produce un error en uno o varios de los pasos, puede usar el patrón de transacción de compensación para deshacer el trabajo que han realizado los pasos. Las operaciones que siguen el modelo de coherencia finalmente definitiva se suelen encontrar en aplicaciones hospedadas en la nube que implementan flujos de trabajo y procesos empresariales complejos.

Contexto y problema

Las aplicaciones que se ejecutan en la nube modifican datos con frecuencia. Estos datos se reparten en ocasiones entre varios orígenes de datos de ubicaciones geográficas diferentes. Para evitar la contención y mejorar el rendimiento en un entorno distribuido, una aplicación no debe intentar proporcionar una fuerte coherencia transaccional. Por el contrario, la aplicación debe implementar coherencia definitiva. En el modelo de coherencia definitiva, una operación empresarial habitual consta de una serie de pasos variados. Aunque la operación realiza estos pasos, la vista general del estado del sistema podría ser poco coherente. Sin embargo, cuando finaliza la operación y todos los pasos se han ejecutado, el sistema volverá a ser coherente.

En el Manual básico de coherencia de datos se proporciona información sobre por qué las transacciones distribuidas no se escalan bien. Este recurso también enumera los principios del modelo de coherencia definitiva.

Una de las dificultades del modelo de coherencia definitiva es cómo tratar con un paso que ha dado error. Tras un error, es posible que tenga que deshacer todo el trabajo que han realizado los pasos anteriores en la operación. Sin embargo, no basta simplemente con revertir los datos, ya que es posible que otras instancias simultáneas de la aplicación los hayan cambiado. Incluso en los casos en los que las instancias simultáneas no han modificado los datos, deshacer un paso puede resultar más complejo que restaurar el estado original. Es probable que sea necesario aplicar varias reglas específicas de la empresa. Como ejemplo, consulte el sitio web de viajes que la sección de Ejemplo describe más adelante en este artículo.

Si una operación que implementa coherencia definitiva abarca varios almacenes de datos heterogéneos, para deshacer los pasos de la operación será necesario visitar cada almacén de datos uno por uno. Para evitar que el sistema quede incoherente, debe deshacer de forma fiable el trabajo que ha realizado en cada almacén de datos.

No todos los datos afectados por una operación que implementa la coherencia definitiva se pueden mantener en una base de datos. Por ejemplo, imagine un entorno de arquitectura orientada a servicios (SOA). Una operación SOA puede invocar una acción en un servicio y provocar un cambio en el estado que dicho servicio mantiene. Para deshacer la operación debe deshacer también este cambio de estado. Este proceso puede consistir en volver a invocar el servicio y realizar otra acción que invierta los efectos de la primera.

Solución

La solución pasa por implementar una transacción de compensación. Los pasos de una transacción de compensación deshacen los efectos de los pasos de la operación original. Un enfoque intuitivo consiste en reemplazar el estado actual por el estado en el que se encontraba el sistema al principio de la operación. Sin embargo, una transacción de compensación no siempre es capaz de adoptar ese enfoque, ya que podría sobrescribir los cambios que han realizado otras instancias simultáneas de una aplicación. En su lugar, una transacción de compensación debe ser un proceso inteligente y que tiene en cuenta cualquier trabajo que realizan las instancias simultáneas. Normalmente, este proceso será específico de la aplicación y dependerá de la naturaleza del trabajo realizado por la operación original.

Un enfoque común es usar un flujo de trabajo para implementar una operación de coherencia definitiva que requiere compensación. Mientras se realiza la operación original, el sistema registra información acerca de cada paso, incluida la manera con la que se puede deshacer el trabajo realizado por cada uno de ellos. Si se produce un error en la operación en cualquier momento, el flujo de trabajo revierte a los pasos que ha completado. En cada paso, el flujo de trabajo realiza el trabajo que revierte dicho paso.

Se deben tener en cuenta las dos observaciones siguientes:

  • Es posible que una transacción de compensación no tenga que deshacer el trabajo en el orden inverso exacto de la operación original.
  • Es posible realizar algunos de los pasos de la fase de reversión en paralelo.

Este método es similar a la estrategia de Sagas que se describe en el blog de Clemens Vasters.

Una transacción de compensación es una operación de coherencia definitiva, por lo que puede dar error. El sistema debería poder reanudar la transacción de compensación en el punto de error y continuar. Como podría ser necesario repetir un paso que no se pudo realizar, deben definirse los pasos de una transacción de compensación como comandos idempotentes. Para más información, consulte los patrones de idempotencia en el blog de Jonathan Oliver.

En algunos casos, la intervención manual podría ser la única manera de recuperarse de un paso que ha fallado. En estas situaciones, el sistema debe generar una alerta y proporcionar tanta información como sea posible sobre el motivo del error.

Problemas y consideraciones

Tenga en cuenta los puntos siguientes al decidir 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 dé error inmediatamente. En cambio, podría bloquearse. Es probable 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 información suficiente para poder deshacer los efectos de cada paso en una operación con errores.

  • Los pasos de una transacción de compensación se deben definir como comandos idempotentes. Si lo consigue, se pueden repetir los pasos si la transacción de compensación produce un error.

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

    • Es resistente en la operación original y en la transacción de compensación.
    • No pierde la información necesaria para compensar un paso con errores.
    • Supervisa de forma fiable el progreso de la lógica de compensación.
  • 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 que se produjese un error.

  • El orden de los pasos de la transacción de compensación no tiene que ser el opuesto exacto de los pasos de la operación original. Por ejemplo, un almacén de datos podría ser más inconsistente que otro. Los pasos de la transacción de compensación que deshacen los cambios de este almacenamiento deberían ocurrir en primer lugar.

  • Algunas medidas ayudan a aumentar la probabilidad de que la actividad general se realice correctamente. En concreto, puede colocar un bloqueo basado en un tiempo de espera a corto plazo en cada recurso necesario para completar una operación. También puede obtener estos recursos de antemano. A continuación, realice el trabajo solo después de haber adquirido todos los recursos. Finalice todas las acciones antes de que expiren los bloqueos.

  • Una lógica de reintentos que sea más flexible de lo habitual ayudará a reducir los errores que desencadenan una transacción de compensación. Si se produce un error en una operación que implementa coherencia definitiva, intente abordarlo como una excepción transitoria y repita el paso. Detenga la operación e inicie una transacción de compensación solamente si un paso da error de forma repetida o no se puede recuperar.

  • Al implementar una transacción de compensación, se enfrenta a muchos de los mismos problemas que debe solucionar al implementar la coherencia final. Para obtener más información, consulte la sección: "Consideraciones para implementar coherencia definitiva" en el Manual básico de coherencia de datos.

Cuándo usar este patrón

Use este patrón únicamente en operaciones que se deben deshacer si se produce un error. Si es posible, diseñe soluciones que eviten la complejidad de exigir transacciones de compensación.

Diseño de cargas de trabajo

Un arquitecto debe evaluar cómo se puede usar el patrón Compensating Transaction en el diseño de sus cargas de trabajo para abordar los objetivos y principios descritos en los pilares del Marco de buena arquitectura de Azure. Por ejemplo:

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

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

Al igual que con cualquier decisión de diseño, hay que tener en cuenta las ventajas y desventajas con respecto a los objetivos de los otros pilares que podrían introducirse con este patrón.

Ejemplo

Los clientes usan un sitio web de viajes para reservar itinerarios. Un único itinerario consta de vuelos y hoteles, por ejemplo. Un cliente que viaja de Seattle a Londres y luego a París realizará los siguientes pasos al crear un itinerario:

  1. Reservar una plaza en el vuelo F1 de Seattle a Londres.
  2. Reservar una plaza en el vuelo F2 de Londres a París.
  3. Reservar una plaza en el vuelo F3 de París a Seattle.
  4. Reservar una habitación en el hotel H1 en Londres.
  5. Reservar una habitación en el hotel H2 en París.

Estos pasos constituyen una operación de coherencia definitiva, aunque cada paso es una acción independiente. Además de realizar estos pasos, el sistema también registrará las operaciones de contador para deshacer cada paso. Esta información es necesaria en caso de que el cliente cancele el itinerario. Los pasos necesarios para realizar las operaciones de contador se pueden ejecutar después como una transacción de compensación.

Es posible que los pasos de la transacción de compensación no sean exactamente opuestos a los pasos originales. Además, la lógica de cada paso de la transacción de compensación debe tener en cuenta las reglas específicas de la empresa. Por ejemplo, cancelar una reserva de un vuelo no dará derecho al cliente a un reembolso completo.

En la ilustración siguiente se muestran los pasos de una transacción de larga duración para reservar un itinerario de viaje. También puede ver los pasos de la transacción de compensación que deshacen la transacción.

Diagram that shows the steps for creating an itinerary. The steps of the compensating transaction that cancels the itinerary are also shown.

Nota:

Es posible realizar en paralelo los pasos de la transacción de compensación en función de cómo haya diseñado la lógica de compensación de cada paso.

En muchas soluciones empresariales, que un único paso produzca error no significa que haya que revertir el sistema mediante una transacción de compensación. Por ejemplo, imagine una situación en la que visualiza el sitio web del viaje. Supongamos que el cliente reserva los vuelos F1, F2 y F3, pero no puede reservar una habitación en el hotel H1. Es preferible ofrecer al cliente una habitación en un hotel diferente en la misma ciudad en lugar de cancelar los vuelos. Aún así, el cliente sigue siendo capaz de cancelar los vuelos. En ese caso, la transacción de compensación se ejecuta y deshace las reservas para los vuelos F1, F2 y F3. Ahora bien, el cliente debe tomar esta decisión, no el sistema.

Pasos siguientes

  • Data Consistency Primer (Manual básico de coherencia de datos). El patrón Compensating Transaction se usa a menudo para deshacer operaciones que implementan el modelo de coherencia definitiva. En este manual se proporciona información sobre las ventajas e inconvenientes de la coherencia definitiva.
  • Patrones de idempotencia. Le recomendamos usar comandos idempotentes en una transacción de compensación. En esta entrada de blog se describen los factores que se deben tener en cuenta al implementar la idempotencia.
  • Patrón Scheduler Agent Supervisor. En este artículo se describe cómo implementar sistemas resistentes que realizan operaciones empresariales en las que se emplean recursos y servicios distribuidos. A veces es necesario usar una transacción de compensación en estos sistemas para deshacer el trabajo que realiza una operación.
  • Patrón Retry. Las transacciones de compensación pueden ser exigentes a nivel de computación. Puede intentar minimizar su uso mediante el patrón Reintentar para implementar una directiva eficaz a la hora de reintentar operaciones fallidas.
  • Patrón de transacciones distribuidas Saga. En este artículo se explica cómo usar el patrón Saga para administrar la coherencia de los datos a través de microservicios en escenarios de transacciones distribuidas. El patrón Saga se encarga de la recuperación de errores en las transacciones de compensación.
  • Patrón Canalizaciones y filtros. En este artículo se describe el patrón Canalizaciones y filtros, que puede usar para descomponer una tarea de procesamiento compleja en una serie de elementos reutilizables. Puede usar el patrón Canalizaciones y filtros en combinación con el patrón Transacción de compensación como una opción alternativa a la implementación de las transacciones distribuidas.
  • Diseño para la recuperación automática. En esta guía se explica cómo diseñar aplicaciones que emplean una recuperación automática. Puede usar transacciones de compensación como parte de un método de recuperación automática.