evoq_saga_compensation (evoq v1.14.1)

View Source

Saga compensation for rollback transactions.

Provides utilities for implementing compensating transactions in process managers (sagas).

Compensation Flow

1. Saga dispatches commands: [Cmd1, Cmd2, Cmd3] 2. Cmd3 fails 3. Saga calls compensate/2 for rollback 4. Compensation generates: [Compensate2, Compensate1] 5. Compensating commands executed in reverse order

Example

  -module(order_saga).
  -behaviour(evoq_process_manager).
 
  compensate(State, #evoq_command{command_type = ship_order}) ->
      %% Compensate shipping by canceling shipment
      CancelCmd = evoq_command:new(cancel_shipment, shipping, ...),
      {ok, [CancelCmd]};
 
  compensate(State, #evoq_command{command_type = charge_payment}) ->
      %% Compensate payment by issuing refund
      RefundCmd = evoq_command:new(issue_refund, payment, ...),
      {ok, [RefundCmd]};
 
  compensate(_State, _Cmd) ->
      skip.  %% No compensation needed

Summary

Functions

Build a chain of compensating commands for all executed commands. Returns commands in reverse order (last executed = first compensated).

Execute compensation for a failed command. Generates and dispatches compensating commands.

Get all executed commands from saga state.

Record an executed command in the saga state. Used for tracking commands for compensation.

Functions

build_compensation_chain(PMModule, PMState)

-spec build_compensation_chain(atom(), term()) ->
                                  [#evoq_command{command_id :: binary() | undefined,
                                                 command_type :: atom() | undefined,
                                                 aggregate_type :: atom() | undefined,
                                                 aggregate_id :: binary() | undefined,
                                                 payload :: map(),
                                                 metadata :: map(),
                                                 causation_id :: binary() | undefined,
                                                 correlation_id :: binary() | undefined,
                                                 idempotency_key :: binary() | undefined}].

Build a chain of compensating commands for all executed commands. Returns commands in reverse order (last executed = first compensated).

execute_compensation(PMPid, Evoq_command, Opts)

-spec execute_compensation(pid(),
                           #evoq_command{command_id :: binary() | undefined,
                                         command_type :: atom() | undefined,
                                         aggregate_type :: atom() | undefined,
                                         aggregate_id :: binary() | undefined,
                                         payload :: map(),
                                         metadata :: map(),
                                         causation_id :: binary() | undefined,
                                         correlation_id :: binary() | undefined,
                                         idempotency_key :: binary() | undefined},
                           map()) ->
                              {ok, [{ok, non_neg_integer(), [map()]} | {error, term()}]} | skip.

Execute compensation for a failed command. Generates and dispatches compensating commands.

get_executed_commands(State)

-spec get_executed_commands(term()) ->
                               [#evoq_command{command_id :: binary() | undefined,
                                              command_type :: atom() | undefined,
                                              aggregate_type :: atom() | undefined,
                                              aggregate_id :: binary() | undefined,
                                              payload :: map(),
                                              metadata :: map(),
                                              causation_id :: binary() | undefined,
                                              correlation_id :: binary() | undefined,
                                              idempotency_key :: binary() | undefined}].

Get all executed commands from saga state.

record_command(Evoq_command, State)

-spec record_command(#evoq_command{command_id :: binary() | undefined,
                                   command_type :: atom() | undefined,
                                   aggregate_type :: atom() | undefined,
                                   aggregate_id :: binary() | undefined,
                                   payload :: map(),
                                   metadata :: map(),
                                   causation_id :: binary() | undefined,
                                   correlation_id :: binary() | undefined,
                                   idempotency_key :: binary() | undefined},
                     term()) ->
                        term().

Record an executed command in the saga state. Used for tracking commands for compensation.