Nebo15 / sage

A dependency-free tool to run distributed transactions in Elixir, inspired by Sagas pattern.
MIT License
912 stars 40 forks source link

Question about intermediary inputs #62

Closed lud closed 3 years ago

lud commented 3 years ago

Hi,

I wonder if you need to have all the required data to run a saga upfront.

If I use the flight booking example, after having the users chose they seats, we would need to ask them if they want to rent a car, then if they want to book a hotel room, and then which kind of room, etc.

This cannot be executed in the same code block, we need to get the user input (for instance, we have a website and we display the current state of the transaction, ask questions, and get HTTP requests as the input).

But looking at the examples it seems that you need to have any required data available before calling Sage.transaction ; and also that unless this function is called you do not run any of the intermediary steps. So:

Is there an example that addresses this problem?

Thank you

AndrewDryga commented 3 years ago

Hello @lud,

That is a great question. I feel like you need a way to pause a transaction and wait for additional input and Sage is not designed to do that. It expects you to collect all input when the Sage struct is built or executed.

To archive this behavior we would need something like a GenServer that implements a Sage internally waiting for intermediate user commands. Eg. you can use Sage on separate steps (booking a flight, booking a car, booking a hotel) and have GenServer logic that holds intermediate data and cancels all executions if one of the transactions fail (this would require a PR discussed in this issue: https://github.com/Nebo15/sage/issues/58). You can check gen_statem docs for a behavior that is designed to deal with such situations.

An additional challenge with paused sagas is that you would need some persistent storage for that GenServer that survives deployment rollout so that you won't have committed transactions that can not be rolled back if the server restarts.

P.S.: I felt like once you book a flight adding a vehicle to your booking is a separate transaction so if vehicle booking failed I still have my tickets. But looks like your business requirements are different.

lud commented 3 years ago

Hi,

Indeed I was thinking about having a gen server that calls the run / run_async functions for each step, but I now know that it wouldn't do anything until the transaction is ran.

Thank you for your answer :)