Elixir Learnings #1
This article was originally published on Medium.
TL;DR: Use -- no-halt
with mix run to keep an app with non-blocking processes running!
Recently I re-read Programming Elixir by Dave Thomas, a book that I read over a year ago when it was in an earlier state. At the time when I first read it, I remember being extremely excited by Elixir, and sharing my excitement with colleagues around me. Unfortunately Elixir wasn’t well known in my community at that point, and although at the time I played with Elixir for a while (pretty bad slack/IRC relay here: https://github.com/aspett/slackirx), I soon lost motivation, without opportunity to work with it during the day.
I attended RubyConf AU this year, and Elixir was popping up in most of the talks, both as a solution to some problems that people have been facing in Ruby world, and as a quip about so many people moving to Elixir. This resparked some discussion about Elixir in the local community, and as such I decided to re-read the Programming Elixir book and get back into it. One of the first things I’ve tried tackling since, is rewriting a little Ruby script I wrote a couple of months ago. The script is simple; every minute, call an API, filter the results, and output some formatted data to a CSV file.
My Elixir (still work in progress) solution, after reading about all this OTP (Open Telecom Platform) goodness, is as follows:
- A supervisor which supervises two workers
- A ticker task
- A GenServer class which handles ticks
Which looks a bit like
defmodule App do
use Application
def start(_type, _args) do
import Supervisor.Spec
children = [
worker(App.Worker, []),
worker(Task, [App.Worker.tick/0])
]
opts = [strategy: :one_for_one]
Supervisor.start_link(children, opts)
end
end
Pretty simple — the task is just calling my tick function, as follows
def tick do
GenServer.cast(__MODULE__, :tick)
:timer.sleep(@interval)
tick
end
where @interval
is a module attribute with a value of 60_000
(60 seconds).
And finally, the function which handles tick calls to the process, a bit like:
def handle_cast(:tick, state) do
fetch_data
|> filter_data # A list of things to be written out
|> Enum.each(&write_to_file/1)
{:noreply, state}
end
Awesome! Time to boot it up with mix run. Huh. Why doesn’t it do anything? It just compiles, and exits.
Well, here’s my learning! If nothing else, this is a note to self. While developing small OTP apps like this locally, there’s a key option to mix run, which is —no-halt! The need for this stems out of having no blocking processes, resulting in the execution of the application stopping immediately after startup, even though there is a timer running.
- Andrew