Help with GenServers and Supervisors

I’ve implemented my first GenServer and Supervisor, here is the code

defmodule Spare.DataCenter do

  use GenServer

  #Client
  def start_link(_opts \\ []) do
    GenServer.start_link(__MODULE__, [
      {:ets_table_name, :link_cache_table},
      {:log_limit, 1_000_000}
    ], name: :data_center)
  end

  def add_question(question) do
    GenServer.cast(:data_center, question)
  end

  def view_questions() do
    GenServer.call(:data_center, :view_questions)
  end

  def get_question(question) do
    GenServer.call(:data_center, {:view_questions, question})
  end


  def remove_question(question) do
    GenServer.cast(:data_center, {:remove_question, question})
  end


  #Server
  def init(args) do
    [{:ets_table_name, ets_table_name}, {:log_limit, log_limit}] = args
    :ets.new(ets_table_name, [:named_table, :set, :protected])
    {:ok, %{log_limit: log_limit, ets_table_name: ets_table_name}}
  end

  def handle_cast({:remove_question, question}, state) do
    %{ets_table_name: ets_table_name} = state
    :ets.delete(ets_table_name, question)
    {:noreply, state}
  end

  def handle_cast(question, state) do
    %{ets_table_name: ets_table_name} = state
    :ets.insert_new(ets_table_name, question)
    {:noreply, state}
  end


  def handle_call(:view_questions, _from, state) do
    %{ets_table_name: ets_table_name} = state
    questions_list = :ets.tab2list(ets_table_name)
    {:reply, questions_list, state}
  end

  def handle_call({:get_question, question}, _from, state) do
    %{ets_table_name: ets_table_name} = state
    questions = :ets.lookup(ets_table_name, question)
    {:reply, questions, state}
  end


end

defmodule Spare.DataCenter.Supervisor do

  use Supervisor

  def start_link(_opts \\ [], _any \\ []) do
    Supervisor.start_link(__MODULE__, :ok, name: :data_center)
  end

  def init(:ok) do
    children = [
      worker(Spare.DataCenter, [[name: :data_center]])
    ]

    supervise(children, strategy: :one_for_one)
  end

end

and I added my Supervisor to the Application children like this

def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Start the Ecto repository
      Spare.Repo,
      # Start the endpoint when the application starts
      SpareWeb.Endpoint,
      # Starts a worker by calling: Spare.Worker.start_link(arg)
      # {Spare.Worker, arg},
      Spare.DataCenter.Supervisor,
    ]

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

However everytime I start my app with iex -S mix I get the following error;

Generated spare app
** (Mix) Could not start application spare: Spare.Application.start(:normal, []) returned an error: shutdown: failed to start child: Spare.Supervisor
    ** (EXIT) shutdown: failed to start child: Spare.DataCenter
        ** (EXIT) already started: #PID<0.352.0>

any help with that?
Thanks in advance.

Giving the same name twice for multiple workers/supervisor is not going to work.

Processes should have a unique name :slight_smile:

When there is a unique GenServer, I often use name: __ MODULE __ (no space… just here to avoid Markdown format)

But if You need to spawn the same GenServer multiple times, then You need a registry.

3 Likes

Ohhkaaaay, I thought I should write the name of the Genserver in the supervisor start_link, what a noob?! :smile:
Thank you so much!