What is different between a supervisor and a worker?

Hi!

I read https://hexdocs.pm/elixir/1.4.2/Supervisor.Spec.html#supervisor/3 multiple times, but I still don’t understand what’s different between a worker and a supervisor? Is it just that the supervisor is used to supervise? And the worker should be doing work?

If that is the case, then the skeleton code below should be a worker rather than a supervisor?

defmodule Notifier do
  def init(state) do
    Notifier.schedule_send_push_notifications
    {:ok, state}
  end

  def handle_info(:send_push_notifications, state) do
    {:ok, pid} = Task.start_link(fn -> Notifier.send_push_notifications(self(), state[:last_notified_at]) end)
    %{state | notifier_task_pid: pid}
  end

  def schedule_send_push_notifications() do
    Process.send_after(self(), :send_push_notifications, 30*1000)
  end

  def send_push_notifications(from, last_notified_at) do
    # send push notifications
    # reply with correct last_notified_at
  end
end

The Notifier process is responsible for starting a task when it’s time to send the push notifications, but beyond that, this process holds some state and passively waits for messages. It feels like my understanding of workers vs supervisors is flawed.

Thanks!
François

1 Like

You can create supervised children in a Supervisor with worker/3 and supervisor/3.

While worker/3 is used to start generic processes, supervisor/3 is specifically for processes that are also Supervisors. Like this you can have Supervisors that are supervised by other Supervisors, eventually creating the Supervision tree.

The module you’ve posted here is not a Supervisor, so it should be started with worker/3.

1 Like

Yes, apart from supervising, supervisors should do as little as possible. While your Notifier module is pretty simple and could technically be implemented as a supervisor, the general mindset in the Erlang and Elixir community is to implement it as a worker.

I think the most important reasons are that a supervisor should not crash from buggy application code and that it should do as little work as possible in order to be able to react quickly to crashing workers.

Edit: forgot to mention that supervisors are implemented as gen_servers, so supervisors are internally no different than any other process in OTP

1 Like

In your notifier module, some methods call the Task.start_link. That essentially starts a new process linked to the main one. Starting this module as a supervisor is essentially to say that if these separately created processes crash, then restart them.