Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Supervision

supervision.livemd

Supervision

Bringing the dead back to life

One of the most unsual parts of Elixir is the underlying idea of processes and of supervising these processes, so you can do interesting thing like recover from errors and have a level over control over how things get restarted.

We are going to look at an example of writing a GenServer (see other doc) that will count down from an initial value, once it hits zero (0) it will intentionally raise an error (killing the process).

We will look at how we can resurrect this server to come back to life and continue working.

Example below taken from Programming Phoenix 1.4 by Chris McCord, Bruce Tate and Jose Valim

defmodule CrashCode do
  use GenServer

  def start_link(initial_val) do
    GenServer.start_link(__MODULE__, initial_val)
  end

  def init(initial_val) do
    Process.send_after(self(), :tick, 1000)
    {:ok, initial_val}
  end

  def handle_info(:tick, val) when val <= 0, do: raise("boom!")

  def handle_info(:tick, val) do
    IO.puts("tick #{val}")
    Process.send_after(self(), :tick, 500)
    {:noreply, val - 1}
  end
end

This is where the magic happens, we create what is known as a Supervisor, this is a process whos job is to observer its children and if something unexpcted happens deals with it.

See Docs

This is a big topic and worth more indepth study, but the below code will allow you to play with a supervisor and child process. Note the opts there are a number of ways you can setup your supervisor that will be helpful in different scenarios.

defmodule InfoSys.Application do
  @moduledoc false

  use Application

  def start do
    start(nil, nil)
  end

  def start(_type, _args) do
    children = [
      # new counter worker
      {CrashCode, 3}
    ]

    # We can pass params to define how often we restart, either keep trying or set number of tries in a given
    # time frame - in this case only allow 2 restarts in a 10 second window (for our example we will run 3 times)
    # We can extend these params to keep the process running forever
    opts = [name: InfoSys.Supervisor, strategy: :one_for_one, max_restarts: 2, max_seconds: 10]
    Supervisor.start_link(children, opts)
  end
end
InfoSys.Application.start()

A little note - I tried for a while to setup a similar supervision with restarts with simple Tasks. I didn’t have much luck, then came across this post

> This module defines a supervisor which can be used to dynamically supervise tasks. Behind the scenes, this module is implemented as a :simple_one_for_one supervisor where the workers are temporary (i.e. they are not restarted after they die).

and

> You should be looking into GenServer and building a pool of workers