washeku
Background Task with autokilling itself and repeated action each N minutes
I want to create a bunch of background jobs dynamically. A job should run at most N minutes, strictly, and then kill itself.
It’ll check a state of some console application once a minute and if a state returns true, a job writes data into a database and kills itself ahead right away, otherwise it’ll keep checking for N minutes at most.
I’m looking for pointers of how to achieve that. I want a simple solution.
Should I use Task? If so, how should I specify the N minutes over the course of which it’ll be alive?
How can I make it to poll a state of an application once a minute?
Most Liked
josevalim
For some yes, for others no.
We need to be careful with those sentences because they may discourage conversation. If something is trivial and someone doesn’t understand it, they may think it is their fault after all.
In any case, you are right, so thanks for pointing to the right direction. @washeku please see the following example as a starting point: How can I schedule code to run every few hours in Elixir or Phoenix framework? - Stack Overflow
if it is not clear, please ask! I am sure @OvermindDL1, myself and others will be happy to help!
OvermindDL1
All fairly trivial with a GenServer, you can even send back yourself a message with a timeout (it only arrives if no other messages have arrived) or always in some-odd minutes regardless of any other messages (send_after). 
You can do it with Task by using send_after though, but a GenServer really sounds best for this task as it does not sound like a one-off.
OvermindDL1
With send_after, something like:
def init(args) do
killSelfTime = args.death_minutes * 1000 * 60 # send_after wants milliseconds
Process.send_after(self(), :kill_self, killSelfTime)
state = encodeWhateverYouWantToDoHere(args)
{:ok, state}
end
def handle_info(:kill_self, state) do
cleanup()
{:stop, :normal}
end
defp cleanup() do
# If you need to do something else like cancel some pending function or un-register with
# another server or something
end
sasajuric
+1
Two processes would be my choice here as well. One does the job, another watches over the job runner and terminates it if it doesn’t finish in the given timeframe. That should properly take care of the case where the job is blocking for a long time (possibly forever).
Arguably the simplest solution could be based on a task:
poll_loop = Task.async(fn ->
# simulation of a poll loop
:timer.sleep(:rand.uniform(:timer.seconds(2)))
end)
# waiting for at most 1 second for the loop to finish
case Task.yield(poll_loop, :timer.seconds(1)) do
{:ok, _} -> IO.puts "loop finished."
nil ->
IO.puts "timeout: killing the loop runner."
Task.shutdown(poll_loop, :brutal_kill)
end
Probably the simplest way to implement the poll loop would be through recursion:
def poll_loop() do
case poll() do
:ok -> :ok
:error ->
:timer.sleep(:timer.seconds(1))
poll_loop()
end
end







