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:

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)

Pretty simple — the task is just calling my tick function, as follows

def tick do
  GenServer.cast(__MODULE__, :tick)

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
  |> filter_data # A list of things to be written out
  |> Enum.each(&write_to_file/1)
  {:noreply, state}

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