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

Untitled notebook

supervisor_and_gen_server_elixir.livemd

Untitled notebook

Section

defmodule SimpleQueue do
  use GenServer

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

  def new(state) do
    GenServer.start_link(__MODULE__, state, name: __MODULE__)
  end

  def handle_call(:queue, _from, state) do
    {:reply, state, state}
  end

  def handle_call(:dequeue, _from, []), do: {:reply, nil, []}

  def handle_call(:dequeue, _from, state) do
    last = state |> List.last()
    state = state |> List.delete(last)
    {:reply, state, state}
  end

  def handle_cast({:enqueue, val}, state) do
    {:noreply, state ++ [val]}
  end

  def dequeue do
    GenServer.call(__MODULE__, :dequeue)
  end

  def enqueue(val) do
    GenServer.cast(__MODULE__, {:enqueue, val})
  end

  def queue do
    GenServer.call(__MODULE__, :queue)
  end
end

sq = SimpleQueue.new([21, 22, 23, 24, 25, 26, 27, 28, 29])
SimpleQueue.queue()
# SimpleQueue.enqueue(222)
SimpleQueue.dequeue() |> IO.inspect()
SimpleQueue.dequeue() |> IO.inspect()
# Supervisor 
# Supervisor.start_link/2 => Starts our supervisor and children, it allows us to define the strategy our supervisor uses for managing child processes.
# Supervisor strategies
# :one_for_one -> Only restart the failed child process.
# :one_for_all -> Restart all child processes in the event of a failure.
# :rest_for_one  -> Restart the failed process and any process started after it.
# Child Specification
# After the supervisor has started it must know how to start/stop/restart its children.
# Each child module should have a child_spec/1 function to define these behaviors.
# The use GenServer, use Supervisor, and use Agent macros automatically define this method for us (SimpleQueue has use GenServer, so we do not need to modify the module), but if you need to define it yourself child_spec/1 should return a map of options:
defmodule Example do
  def child_spec(opts) do
    %{
      id: SimpleQueue,
      start: {__MODULE__, :start_link, [opts]},
      shutdown: 5_000,
      restart: :permanent,
      type: :worker
    }
  end

  # id -> Required key. Used by the supervisor to identify the child specification.
  # start -> Required key. The Module/Function/Arguments to call when started by the supervisor
  # shutdown ->  Optional key. Defines child’s behavior during shutdown.
  # :brutal_kill -> Child is stopped immediately
  # 0 or a positive integer ->  time in milliseconds supervisor will wait before killing child process. If the process is a :worker type, shutdown defaults to 5000.
  # :infinity -> Supervisor will wait indefinitely before killing child process. Default for :supervisor process type. Not recommended for :worker type
  # restart -> Optional key
  # :permanent -> Child is always restarted. Default for all processes
  # :temporary ->  Child process is never restarted.
  # :transient ->  Child process is restarted only if it terminates abnormally.
  # type -> Optional key. Processes can be either :worker or :supervisor. Defaults to :worker.
end
# DynamicSupervisor
# Supervisors normally start with a list of children to start when the app starts.
# However, sometimes the supervised children will not be known when our app starts 
# up (for example, we may have a web app that starts a new process to handle a user
#  connecting to our site). For these cases we will want a supervisor where the children
#  can be started on demand. The DynamicSupervisor is used to handle this case.

# Since we will not specify children, we only need to define the runtime options for the supervisor. The DynamicSupervisor only supports the :one_for_one supervision strategy
options = [
  name: SimpleQueue.Supervisor,
  strategy: :one_for_one
]

DynamicSupervisor.start_link(options)

# Then, to start a new SimpleQueue dynamically we’ll use start_child/2 which takes a supervisor and the child specification (again, SimpleQueue uses use GenServer so the child specification is already defined):
:Cap === :Cap
# Capitalized un-colonized" atoms just aliases to Elixir.AtomName
Cap == Elixir.Cap
Cap == :"Elixir.Cap"
# Task Supervisor
# Tasks have their own specialized Supervisor, the Task.Supervisor. Designed for dynamically created tasks, the supervisor uses DynamicSupervisor under the hood.
children = [
  # restart is deprecated in Task.Supervisor.start_link //Please pass those options on start_child/3
  {Task.Supervisor, name: ExampleApp.TaskSupervisor}
]

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

# The major difference between Supervisor and Task.Supervisor is that its default restart strategy is :temporary (tasks would never be restarted).
# Supervised Tasks
# With the supervisor started we can use the start_child/2 function to create a supervised task:
# DynamicSupervisor.on_start_child()
Process.alive?(pid) |> IO.puts()

pid =
  Task.Supervisor.start_child(
    ExampleApp.TaskSupervisor,
    fn ->
      :timer.sleep(1000)
      IO.puts("Background work done")
    end,
    restart: :transient
  )
System.version()