Nebo15 / sage

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

Error is thrown when compensation is :noop #44

Open kokjinsam opened 4 years ago

kokjinsam commented 4 years ago

Example

Sage.new()
|> Sage.run(:step1, &fun_one/2)
|> Sage.run_async(:step2, &fun_two/2, :noop)
|> Sage.run(:step3, &fun_three/2)
|> Sage.transaction(Repo, args)

Expected Behavior

When step3 returns {:error, reason}, operations in step1 and step3 should be rolled back and :noop for step2.

Current Behavior

The following error is thrown:

** (BadFunctionError) expected a function, got: :noop
    (sage) lib/sage/executor.ex:299: Sage.Executor.apply_compensation_fun/5
    (sage) lib/sage/executor.ex:271: Sage.Executor.safe_apply_compensation_fun/5
    (sage) lib/sage/executor.ex:264: Sage.Executor.execute_compensation/2
    (sage) lib/sage/executor.ex:240: Sage.Executor.execute_compensations/4
    (sage) lib/sage/executor.ex:29: Sage.Executor.execute/2
    (sage) lib/sage.ex:422: anonymous fn/3 in Sage.transaction/3
    (ecto_sql) lib/ecto/adapters/sql.ex:898: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
    (db_connection) lib/db_connection.ex:1415: DBConnection.run_transaction/4
    (sage) lib/sage.ex:421: Sage.transaction/3

Why this is a problem?

This behavior will become an issue if you're composing multiple sages.

AndrewDryga commented 4 years ago

Hey @sammkj. This is a good suggestion, I did not support idempotent initially but I see cases where you want to issue multiple async GET requests and then use their returns, so Ill definitely implement this.

We also need a better guards there to make sure error can be raised at compile-time.