Say, I want to do some tasks, always in background, every hour. I’ll use GenServer.
But the frequency of doing the tasks also can be changed, namely, it can be set up by user, for example, via web UI.
How can I create such a GenServer?
I know how to create a normal one only, with a constant frequency.
Here is a simple example, though it’s kind of crude. Like @kokolegorille said, you keep track of the interval as part of the genserver state and then supply a handler that allows you to update the interval. A caveat is that updating the interval doesn’t take effect until the previous interval’s action has already completed. I’m not sure if there is a way cancel a Process.send_after. There might be more robust ways of handling this.
defmodule UserSpecifiedInterval do
use GenServer
@default_interval 5 * 1000 # 5 seconds
#
# Client
#
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts)
end
def set_interval(pid, interval) do
GenServer.cast(pid, {:set_interval, interval})
end
#
# Server
#
def init(opts) do
interval = opts[:interval] || @default_interval
poll(interval)
{:ok, {%{arbitrary: "state"}, interval}}
end
def handle_cast({:set_interval, new_interval}, {state, _old_interval}) do
{:noreply, {state, new_interval}}
end
def handle_info(:do_thing, {state, interval}) do
# do whatever thing you do at the interval
IO.puts "Doing thing at interval #{interval}"
poll(interval)
{:noreply, {state, interval}}
end
defp poll(interval) do
IO.puts "Polling at interval #{interval}"
Process.send_after(self(), :do_thing, interval)
end
end
Honestly I’d just have a single genserver that holds the time until the ‘next’ thing to process and just timeouts until then, that way if a message comes in to add/remove then it can recalculate what is soonest again and repeat…