Periodic tasks with Elixir

No more Googling for Cron syntax

Ville
3 min readDec 14, 2017

I’d argue that most projects I’ve worked on over the years all have eventually included a periodic task of some sorts; daily reminder email, overnight processing task, polling an API that doesn’t provide a web hook etc.

With Ruby the answer was always a cron job that would invoke a rake task. This had couple of problems with it, first I could never remember the crontab format. Second was where to place the configuration and how to make sure it was up to date.

Gems like Whenever would help fix many of my grievances but with Elixir there’s in my opinion something even nicer.

Using receive/1 for timeouts

Elixir has a function receive/1 which is defined in the Kernel.SpecialForms module and is available for use anywhere in your code without the module prefix.

The receive/1 takes an optional after clause with a timeout value that gets executed if the process has not received a matching message in the given time. We can combine this with a recursive call to execute code at given intervals.

If that sounds cryptic I hope this helps

defmodule Example dodef process() do
receive do
after
5_000 ->
IO.puts "5 seconds elapsed"
process()
end
end
end

If you were to call the process/0 function here it would keep printing “5 seconds elapsed” every 5 seconds.

Creating a periodic Task module

The function above isn’t terribly useful by itself, for one it never returns and besides we’d want it to start when the application starts.

We can create a module that starts our process in a Task and can be added to the applications supervision tree

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

If you are on Elixir 1.5 or newer by using the use Task at the top of your module your Application supervisor can do something like this

defmodule Example.Application do
@moduledoc false
use Application def start(_type, _args) do
children = [
Example.BitcoinPriceUpdater
]
opts = [strategy: :one_for_one, name: Example.Supervisor]
Supervisor.start_link(children, opts)
end
end

If you are on an older version of Elixir you’ll have to specify the children with something like this instead

children = [
worker(Example.BitcoinPriceUpdater, [])
]

In any case what you have is a module as part of your Application supervision tree that is started automatically when your Application is started and it will keep calling the given piece of code periodically.

There you have it a simple little module that you can use to add periodic tasks to your applications with ease and no more worrying about updating the crontab.

In my experience the timeout is accurate enough for most tasks like this and I’ve had couple running in production for a year or so now and it’s not given me any trouble.

Having said that I wouldn’t rely on this for anything where accuracy is very important and also keep in mind that if your process crashes or you restart the app the timer starts from 0.

Please do click the 👏 icon below if you liked this post and follow me here or on Twitter @efexen to hear about new articles. If you haven’t already can I recommend checking out my Elixir Refactoring Series as well 👍

Next: Periodic tasks with Elixir — Part 2

--

--

Ville

Freelance Developer • Founder of @CodeHalf • Rubyist • Elixir Alchemist • Wannabe Data Scientist • Dad