Spawn an asynchronous worker

https://dashbit.co/blog/you-may-not-need-redis-with-elixir contains a sentence: “even if you prefer to deliver emails outside of the request, in order to send an earlier response to users, you can spawn an asynchronous worker […]”. Now, is it about something as simple as:

Task.start(
	fn ->
		Email.important_stuff(user)
		|> Mailer.deliver_now!
	end
)

or did the author have something else / more sophisticated in mind? What is the typical way of “spawning an asynchronous worker” from within a request-serving process?

Almost that, except you should start the task under a supervision tree:

children = [
  {Task.Supervisor, name: MyApp.TaskSupervisor}
]

Task.Supervisor.start_child(MyApp.TaskSupervisor, fn ->
  IO.puts "I am running in a task"
end)

From the docs:

Note that the spawned process is not linked to the caller, but only to the supervisor. This command is useful in case the task needs to perform side-effects (like I/O) and you have no interest on its results nor if it completes successfully.

But if you’re on PostreSQL, then consider Oban. It’s awesome.

2 Likes

This depends mostly on what guarantees you actually need. What should happen if things go sideways like when whatever the worker does crashes, or the machine shuts down mid way.

Right, I see your point. “Thinking aloud” for a case like in the example (dispatching outside the request serving) I don’t think I need to account for machine being shut down mid-way. OTOH I wouldn’t like to just “fire and forget” either. As a bare minimum I would like to know that the task was/wasn’t successful. Even if – to keep things simple – it would only be a log entry.

If someone’s wondering, the children match is not actually a part of the serving code but an addition to the application startup, like insideapplication.ex in Phoenix, right?

Yes, exactly.