I have a question regarding polling strategies.
I generally like doing GenServer + Process.send_after because it’s more intuitive for me.
Lately I came across two Internet sources that do this:
defmodule Example.BitcoinPriceUpdater do
use Task
def start_link(_arg) do
Task.start_link(&poll/0)
end
def poll() do
receive do
after
60_000 ->
get_price()
poll()
end
end
defp get_price() do
# Call API & Persist
IO.puts "To the moon!"
end
end
(excerpt: Periodic tasks with Elixir. No more Googling for Cron syntax | by Ville | Medium)
And also the same usage in JokenJwks
library:
use Task, restart: :transient
...
@doc false
def poll(opts) do
interval = opts[:time_interval]
receive do
after
interval ->
_ = check_fetch(opts)
poll(opts)
end
end
Is there any "gotcha"s when you use Task-based polling under Supervisor?
Specifically, at work I had the JokenJwks.DefaultStrategyTemplate
used (via use
) in my Phoenix application, but I was able to crash the module based Task when I ran a custom Mix Task, while the app was running.
The custom Mix Task did not even remotely use the same code base.
Setting :logger
config to:
config :logger,
level: :debug,
handle_sasl_reports: true
Allowed me to see that when the process crashed, resources were:
- Heap Size: 29_000
- Reduction was around 440_000. Isn’t that pretty high? I still don’t know how to guage reduction units…
When I do a Process.monitor() + receive
, I get instantaneous DOWN
messages, so I am even more puzzled.
Frankly, I can’t quite grasp how a recursing poll()
in a use Task
, under Supervisor would be running - so is this like a long-running process?
Now if we talk about doing a GenServer + polling via recursive Process.send_after
, I totally get that right off the bat - I know what’s happening.
So I wanted to ask - is there a case where one would want to use receive + after
polling with use Task
? Besides the fact that it might look “simpler”, especially when the intention is for a long-running poll?
Thanks in advance.