I've got a workflow that involves waking up every 30 seconds or so and polling a database for updates, taking action on that, then going back to sleep. Setting aside that database polling doesn't scale and other similar concerns, what is the best way to structure this workflow using Supervisors, workers, Tasks, and so forth?
I'll lay out a few ideas I've had and my thoughts for/against. Please help me figure out the most Elixir-y approach. (I'm still very new to Elixir, btw.)
1. Infinite Loop Through Function Call
Just put a simple recursive loop in there, like so:
def do_work() do
# Check database
# Do something with result
# Sleep for a while
do_work()
end
I saw something similar when following along with a tutorial on building a web crawler.
One concern I have here is infinite stack depth due to recursion. Won't this eventually cause a stack overflow since we're recursing at the end of each loop? This structure is used in the standard Elixir guide for Tasks, so I'm probably wrong about the stack overflow problem.
Update - As mentioned in the answers, tail call recursion in Elixir means stack overflows are not a problem here. Loops that call themselves at the end are an accepted way to do infinite looping.
2. Use a Task, Restart Each Time
The basic idea here is to use a Task that runs once and then exits, but pair it with a Supervisor with a one-to-one
restart strategy, so it gets restarted each time after it completes. The Task checks the database, sleeps, then exits. The Supervisor sees the exit and starts a new one.
This has the benefit of living inside a Supervisor, but it seems like an abuse of the Supervisor. It's being used for looping in addition to error trapping and restarting.
(Note: There's probably something else that can be done with Task.Supervisor, as opposed to the regular Supervisor and I'm just not understanding it.)
3. Task + Infinite Recursion Loop
Basically, combine 1 and 2 so it's a Task that uses an infinite recursion loop. Now it's managed by a Supervisor and will restart if crashed, but doesn't restart over and over as a normal part of the workflow. This is currently my favorite approach.
4. Other?
My concern is that there's some fundamental OTP structures that I'm missing. For instance, I am familiar with Agent and GenServer, but I just recently stumbled onto Task. Maybe there's some kind of Looper for exactly this case, or some use case of Task.Supervisor that covers it.