DEV Community

Vinicius Moreira
Vinicius Moreira

Posted on

Testando os tipos de Strategy do Supervisor do Elixir

Vamos testar o comportamento dos 3 tipos de Strategy:

  • :one_for_one (Se um processo filho terminar, apenas este processo será reiniciado)
  • :one_for_all (Se um processo filho terminar, todos os outros filhos serão terminados e depois, todos os processos seráo reiniciados, inclusive, o processo que terminou)
  • :rest_for_one (Se um processo filho terminar, todos os filhos que foram criados depois dele serão finalizados e os mesmos serão reiniciados, inclusive, o processo que terminou)

mix new test_otp --sup

Image description

Testando :one_for_one

Vamos configurar no application para os processos sejam criados nessa orderm:

ProcessA
ProcessB
ProcessC

Vamos iniciar cada processo com o estado: [0]

Por padrão, o strategy: já vem como :one_for_one

test_otp/application.ex

defmodule TestOtp.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  @impl true
  def start(_type, _args) do
    children = [
      # Starts a worker by calling: TestOtp.Worker.start_link(arg)
      # {TestOtp.Worker, arg}
      Core.ProcessA,
      Core.ProcessB,
      Core.ProcessC
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: TestOtp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Enter fullscreen mode Exit fullscreen mode

criar o ProcessA

test_otp/core/process_a.ex

defmodule Core.ProcessA do
  use GenServer

  # Server

  @impl true
  def init(state) do
    {:ok, state}
  end

  @impl true
  def handle_call(:get, _from, state) do
    {:reply, state, state}
  end

  @impl true
  def handle_cast({:add, items}, state) do
    {:noreply, state ++ items}
  end

  # Client

  def start_link(_) do
    GenServer.start_link(__MODULE__, [0], name: ProcessA)
  end

  def get() do
    GenServer.call(ProcessA, :get)
  end

  def add() do
    GenServer.cast(ProcessA, {:add, [1,2,3])
  end
end

Enter fullscreen mode Exit fullscreen mode

criar o ProcessB

test_otp/core/process_b.ex

defmodule Core.ProcessB do
  use GenServer

  # Server

  @impl true
  def init(state) do
    {:ok, state}
  end

  @impl true
  def handle_call(:get, _from, state) do
    {:reply, state, state}
  end

  @impl true
  def handle_cast({:add, items}, state) do
    {:noreply, state ++ items}
  end

  # Client

  def start_link(_) do
    GenServer.start_link(__MODULE__, [0], name: ProcessB)
  end

  def get() do
    GenServer.call(ProcessB, :get)
  end

  def add() do
    GenServer.cast(ProcessB, {:add, [4,5,6])
  end
end

Enter fullscreen mode Exit fullscreen mode

criar o ProcessC

test_otp/core/process_c.ex

defmodule Core.ProcessC do
  use GenServer

  # Server

  @impl true
  def init(state) do
    {:ok, state}
  end

  @impl true
  def handle_call(:get, _from, state) do
    {:reply, state, state}
  end

  @impl true
  def handle_cast({:add, items}, state) do
    {:noreply, state ++ items}
  end

  # Client

  def start_link(_) do
    GenServer.start_link(__MODULE__, [0], name: ProcessC)
  end

  def get() do
    GenServer.call(ProcessC, :get)
  end

  def add() do
    GenServer.cast(ProcessC, {:add, [4,5,6])
  end
end

Enter fullscreen mode Exit fullscreen mode

Vamos iniciar a aplicaçao com o IEX

iex -S mix

Dentro do IEX, vamos digitar

Core.ProcessA.add
Core.ProcessB.add
Core.ProcessC.add

Depois, vamos conferir o estado dos processos:

Core.ProcessA.get
[0,1,2,3]
Core.ProcessB.add
[0,4,5,6]
Core.ProcessC.add
[0,7,8,9]

Image description

Agora, vamos parar o ProcessB

Digite no IEX

GenServer.stop(ProcessB)

Agora, vamos conferir o estado dos processos:

Core.ProcessA.get
[0,1,2,3]
Core.ProcessB.add
[0]
Core.ProcessC.add
[0,7,8,9]

Image description

Percebemos que apenas o ProcessB voltou ao estado inicial, ou seja, apenas ele foi reiniciado.

Testando :one_for_all

Para isso, vamos mudar apenas o strategy: no arquivo test_otp/application.ex, vamos colocar strategy: :one_for_all

test_otp/application.ex

defmodule TestOtp.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  @impl true
  def start(_type, _args) do
    children = [
      # Starts a worker by calling: TestOtp.Worker.start_link(arg)
      # {TestOtp.Worker, arg}
      Core.ProcessA,
      Core.ProcessB,
      Core.ProcessC
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_all, name: TestOtp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Enter fullscreen mode Exit fullscreen mode

Vamos iniciar a aplicaçao com o IEX

iex -S mix

Dentro do IEX, vamos digitar

Core.ProcessA.add
Core.ProcessB.add
Core.ProcessC.add

Depois, vamos conferir o estado dos processos:

Core.ProcessA.get
[0,1,2,3]
Core.ProcessB.add
[0,4,5,6]
Core.ProcessC.add
[0,7,8,9]

Image description

Agora, vamos parar o ProcessB

Digite no IEX

GenServer.stop(ProcessB)

Agora, vamos conferir o estado dos processos:

Core.ProcessA.get
[0]
Core.ProcessB.add
[0]
Core.ProcessC.add
[0]

Image description

Percebemos que todos os processos voltaram ao estado inicial, ou seja, todos foram finalizados e reiniciados.

Testando :rest_for_one

Vamos mudar apenas o strategy: no arquivo test_otp/application.ex, vamos colocar strategy: :rest_for_one

test_otp/application.ex

defmodule TestOtp.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  @impl true
  def start(_type, _args) do
    children = [
      # Starts a worker by calling: TestOtp.Worker.start_link(arg)
      # {TestOtp.Worker, arg}
      Core.ProcessA,
      Core.ProcessB,
      Core.ProcessC
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :rest_for_one, name: TestOtp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Enter fullscreen mode Exit fullscreen mode

Vamos iniciar a aplicaçao com o IEX

iex -S mix

Dentro do IEX, vamos digitar

Core.ProcessA.add
Core.ProcessB.add
Core.ProcessC.add

Depois, vamos conferir o estado dos processos:

Core.ProcessA.get
[0,1,2,3]
Core.ProcessB.add
[0,4,5,6]
Core.ProcessC.add
[0,7,8,9]

Image description

Agora, vamos parar o ProcessB

Digite no IEX

GenServer.stop(ProcessB)

Agora, vamos conferir o estado dos processos:

Core.ProcessA.get
[0,1,2,3]
Core.ProcessB.add
[0]
Core.ProcessC.add
[0]

Image description

Percebemos que apenas o ProcessB e o ProcessC) voltaram ao estado inicial. O ProcessB foi parado, por isso, foi reiniciado, já o ProcessC, que foi criado depois do ProcessB, vejam no arquivo application.ex, foi finalizado e reiniciado. E o ProcessA, que foi criado antes do ProcessB, manteve o seu estado, ou seja, não foi reiniciado.

Latest comments (2)

Collapse
 
lcezermf profile image
Luiz Cezer

Simples e assertivo na explicação, muito bom!

Collapse
 
postelxpro profile image
Elxpro

Excelente artigo!! Excelente forma de apresentar a ideia, mais simples que isso. Quase impossivel :D