7
votes

I'm very new on elixir/phoenix. I'm working on an previously created app that have multiples repositories and today I see an example that makes me wonder what means that configuration

I think I dont know how to search that is the reason I can't find the right answer on documentation

first the app I'm working have something like

defmodule RestApi do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      supervisor(RestApi.Endpoint, []),
      supervisor(RestApi.Repo, []),]),
      supervisor(RestApi.OtherRepo, []),]),
    ]

    opts = [strategy: :one_for_one, name: RestApi.Supervisor]
    Supervisor.start_link(children, opts)
  end

  def config_change(changed, _new, removed) do
    RestApi.Endpoint.config_change(changed, removed)
    :ok
  end
end

they use the function Supervisor.Spec.supervisor/3 to start/manage everything

later I found an example

defmodule RestApi do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      supervisor(RestApi.Endpoint, []),
      worker(RestApi.Repo, []),
    ]

    opts = [strategy: :one_for_one, name: RestApi.Supervisor]
    Supervisor.start_link(children, opts)
  end

  def config_change(changed, _new, removed) do
    RestApi.Endpoint.config_change(changed, removed)
    :ok
  end
end

in the example they use Supervisor.Spec.worker/3 to start/manage the repo

What is the difference on this? I mean how affects the app (performance, memory consumption, etc)

2
Note that things were recently changed in elixir 1.5, and looks simpler. Take a look at the changelog github.com/elixir-lang/elixir/blob/v1.5/CHANGELOG.md and the Supervisor docs hexdocs.pm/elixir/Supervisor.html.Nagasaki45

2 Answers

12
votes

Think of supervisor being a branch in the supervision tree, while the worker is a leaf.

Each supervisor is a worker, while not each worker is a supervisor. Although the supervisor has a bunch of functions specifically dedicated to manage children processes, it in fact has very low impact on productivity compared against generic gen_server. This excerpt from OTP design principles explains what supervisor is supposed to be for:

A supervisor is responsible for starting, stopping, and monitoring its child processes. The basic idea of a supervisor is that it is to keep its child processes alive by restarting them when necessary.
Which child processes to start and monitor is specified by a list of child specifications. The child processes are started in the order specified by this list, and terminated in the reversed order.

Besides that it is “the worker.”

That said, there is a rule of thumb that is easy to adopt: when the process manages children processes, it’s a supervisor, it is a worker otherwise.

In the mentioned example:

children = [
  supervisor(RestApi.Endpoint, []),
  worker(RestApi.Repo, []),
]

RestApi.Endpoint manages children processes, and RestApi.Repo does not. Besides that, both are plain old good gen_servers.

2
votes

You use supervisor to start processes which are Supervisors, and worker to start workers (basically anything which is not a supervisor). You can find more on this in the Supervisor and Supervisor.Spec module docs.

It appears either the process type changed between versions, or one of those examples has a bug, but I believe it's supposed to be a worker process, but I would check the Ecto docs.

As for how it affects the application, there is no meaningful difference other than documentation of the type of process the child is - it is in fact optional in Erlang. Supervisors manage their own set of child processes, forming a tree of supervisors. You use this to isolate the effects of crashes to sub branches of that tree so that unrelated portions of the application can continue to function while the faulted branch is restarted by its supervisor. This is the source of fault tolerance in the language and is a fundamental building block of OTP.

Workers are by far the most common type of process, they do not manage child processes, and are designed to crash and be restarted by their supervisor when things go wrong, the idea being to start over from a clean, known state. Too many failures cause the parent supervisor to crash, which in turn is restarted by its supervisor - if this bubbles up to the top level supervisor of your application, the application will crash.

Hopefully that helps :)