Skip to content

Instantly share code, notes, and snippets.

@AndrewDryga
Last active March 22, 2018 12:14
Show Gist options
  • Save AndrewDryga/077dadc2afd8c20a0c26023f3ef58397 to your computer and use it in GitHub Desktop.
Save AndrewDryga/077dadc2afd8c20a0c26023f3ef58397 to your computer and use it in GitHub Desktop.
defmodule SageExample do
import Sage
require Logger
# It is better to build Saga struct in a module attribute,
# we don't need to spend resources each time we want to execute it
@create_and_subscribe_user_sage
new()
|> run(:user, &create_user/2)
|> run(:plans, &fetch_subscription_plans/2)
|> run(:subscription, &create_subscription/2, &delete_subscription/3)
|> run_async(:delivery, &schedule_delivery/2, &delete_delivery_from_schedule/3)
|> run_async(:receipt, &send_email_receipt/2, &send_excuse_for_email_receipt/3)
|> run(:update_user, &set_plan_for_a_user/2)
|> finally(&acknowledge_job/2)
def create_and_subscribe_user(attrs) do
Sage.transaction(@create_and_subscribe_user_sage, Repo, attrs)
end
# Transaction callback receives previously created effects
# and attributes passed to `execute/2` or `transaction/3`
def create_user(_effects, attrs) do
# We can use raw insert if we expect Sage to be wrapped in a transaction,
# because DB-related effects would be compensated on transaction rollback
Repo.insert(%SageExample.User{login: attrs["login"], password: attrs["password"]})
end
# .. other transaction callbacks
# Steps have access to effects created on previous steps
def create_subscription(%{user: user}, attrs) do
BillingAPI.subscribe_user(user, plan: attrs["plan"])
end
# If we failed on this step because of timeout of external service, let's keep retrying
def delete_subscription(nil, {:subscription, _reason}, _attrs) do
{:retry, retry_limit: 5}
end
# Cleanup effect created by create_subscription/2
def delete_subscription(_subscription_or_nil, _errorred_step_name_and_reason, _attrs) do
Logger.error("Failed to create subscription because of #{inspect(reason)}")
# We must delete all subscriptions for a newly created user,
# because we don't know if it was actually created on third-party service or not
BillingAPI.Subscription.delete_all_by_email(attrs["email"])
end
# .. other transaction callbacks
def acknowledge_job(:ok, attrs) do
Logger.info("Successfully created user #{attrs["email"]}")
end
def acknowledge_job(_error, attrs) do
Logger.warn("Failed to create user #{attrs["email"]}")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment