Estrategias para crear un Supervisor

Finalmente, la diferencia entre start_link/2 y start_link/3 y el uso de init/2 para desacoplar la creación de un Supervisor de la especificación de los procesos supervisados y de las opciones de supervisión.

Cuando trabajamos con árboles de supervisión en Elixir, es fundamental entender cómo desacoplar la configuración del supervisor del acto de iniciarlo. Esto nos permite crear sistemas más flexibles y organizados, especialmente cuando tenemos supervisores anidados.

Al principio, solemos lanzar un supervisor con Supervisor.start_link/2, pasando una lista de hijos y opciones como la estrategia de reinicio. Por ejemplo, podemos tener dos procesos hijos, como una calculadora y una pila, cada uno definido con su ChildSpec. Sin embargo, esta forma de iniciar el supervisor mezcla la configuración con el inicio, lo que puede complicar la composición de supervisores más complejos.

Para solucionar esto, Elixir nos ofrece la función init/1 dentro del módulo supervisor. Esta función nos permite separar la declaración de la configuración del supervisor del momento en que se inicia. En concreto, init/1 recibe los argumentos necesarios y devuelve la configuración del supervisor, incluyendo la estrategia y la lista de hijos. Así, el supervisor se configura en un lugar y se inicia en otro, facilitando la reutilización y composición.

Por ejemplo, podemos definir la función init/1 en nuestro módulo supervisor para que reciba los argumentos y devuelva la configuración:

def init(args) do
  children = [
    supervisame.calculadora(),
    supervisame.pila()
  ]

  Supervisor.init(children, strategy: :one_for_one)
end

Luego, en lugar de llamar directamente a Supervisor.start_link/2 con toda la configuración, usamos Supervisor.start_link/3 (también conocido como start_link/3 o start_link/2 con módulo y argumentos), que recibe el módulo supervisor, los argumentos para init/1 y las opciones de inicio. Esto se conoce como start_link/3 o start_link/2 con módulo y argumentos, y permite que el supervisor se inicie llamando internamente a init/1 para obtener su configuración.

Así, el inicio del supervisor queda más limpio y desacoplado:

Supervisor.start_link(supervisame, :ok, name: :supervísame)

Aquí, :ok es el argumento que se pasa a init/1, y name: :supervísame es una opción para registrar el supervisor con un nombre.

Esta separación es especialmente útil cuando queremos anidar supervisores, es decir, tener un supervisor que supervise a otros supervisores. Si cada supervisor tiene su propia función init/1 que devuelve su configuración, podemos combinar fácilmente estas configuraciones en un supervisor superior sin mezclar la lógica de inicio.

Para ilustrar esto, podemos crear un segundo supervisor que lance el primero como hijo, usando la forma tradicional de start_link/2 con la lista de hijos:

defmodule MainSupervisor do
  use Supervisor

  def start_link(args) do
    Supervisor.start_link(__MODULE__, args, name: :main_supervisor)
  end

  def init(_args) do
    children = [
      {supervisame, :ok}
    ]

    Supervisor.init(children, strategy: :one_for_one)
  end
end

En este ejemplo, MainSupervisor tiene como hijo al supervisor supervisame, que se inicia con el argumento :ok. Gracias a que supervisame implementa init/1, el supervisor principal puede iniciar a sus supervisores hijos sin preocuparse por su configuración interna.

Además, al registrar los supervisores con nombres, podemos referenciarlos fácilmente en otras partes de la aplicación, lo que facilita la gestión y el acceso a los procesos supervisados.

Es importante destacar que, aunque podemos omitir la implementación de init/1 y pasar toda la configuración directamente a start_link/2, seguir esta convención facilita la composición y el mantenimiento de sistemas con múltiples supervisores. Por eso, es recomendable implementar init/1 en supervisores descendientes y usar start_link/3 para iniciar supervisores con argumentos.

Finalmente, en la aplicación principal, que es el supervisor raíz, podemos usar la forma tradicional de iniciar el supervisor con start_link/2 y pasar la configuración directamente, ya que es el punto de entrada del sistema. Para los supervisores internos, en cambio, es mejor seguir la convención de init/1 y start_link/3 para mantener el desacoplamiento y la flexibilidad.

Con estas estrategias, podemos construir árboles de supervisión en Elixir que sean modulares, fáciles de mantener y que sigan las mejores prácticas recomendadas por la documentación oficial.

Lista de reproducción
  1. 1
    Cómo crear procesos
    11 minutos
  2. 2
    Cómo pasar mensajes entre procesos
    13 minutos
  3. 3
    Diccionario de un proceso y mantener un estado
    15 minutos
  4. 4
    Cómo enlazar procesos para detectar fallos
    13 minutos
  5. 5
    Cómo monitorizar procesos
    10 minutos
  6. 6
    ¿Qué es un GenServer?
    15 minutos
  7. 7
    Cómo enviar mensajes con un GenServer
    19 minutos
  8. 8
    Control de errores y gestión de un GenServer
    19 minutos
  9. 9
    Cómo renombrar procesos
    11 minutos
  10. 10
    Cómo crear un Supervisor Tree
    17 minutos
  11. 11
    Estrategias para trabajar con Supervisor
    18 minutos
  12. 12
    Estrategias para crear un Supervisor
    11 minutos
  13. 13
    Resumen sobre procesos OTP
    12 minutos
  14. 14
    Cómo usar Application
    12 minutos
  15. 15
    Ejemplo de Application con hijos
    14 minutos