def create(conn, %{"data" => %{"attributes" => user_params}}) do
changeset = User.changeset(%User{}, user_params)
case Repo.insert(changeset) do
{:ok, user} ->
UserMailer.send_welcome_email(user)
conn
|> put_status(:created)
|> render("show.json", model: user)
{:error, changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(MyApp.ChangesetView, "error.json", changeset: changeset)
end
end
In this controller action, UserMailer.send_welcome_email
is synchronous, and the request waits.
I wanted to make it asynchronous, so spawned a process like this instead
spawn_link(fn ->
UserMailer.send_welcome_email(user)
end)
The request doesn't not wait until the mail has been sent.
Supervisor
instead?spawn_link
fails, and logs it in our phoenix logs, it would do)Starting a process using spawn_link/1
will cause a bidirectional link, so whichever of the spawning process and the newly spawned process that happens to die first will kill the other one (unless it's trapping exits, which it probably shouldn't be). That's great in some cases, and not so great in others; if it takes a long time to send that e-mail, for example, the Phoenix request might finish first, and risk killing off the spawned process.
Due to the linking, however, there shouldn't be any risk of the processes getting orphaned.
A better approach is definitely to create a Supervisor
(or using Task.Supervisor
), and you could roll your own background job setup rather easily.
However, it might be worth looking at something like exq
that you mentioned, or Toniq for example, that could possibly have everything you need already; especially things like retries in case of failure, and presistence. There are also some other interesting options in the Awesome Elixir list if you want to try out a few different alternatives.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With