Help needed with Supervisor initializing child state

Hi, new to the forums here and to elixir in general.

I have an application that will connect to an MQTT server, subscribe for topics and persist information to postgres, the prototype works fine as long as “nothing bad happens”. The application was starting the MQTT Client Genserver and calling the connect and subscribe functions of the MQTT Client Genserver. But if the child died and was restarted, the configuration is lost.

Things I have tried so far:

  1. Moving the setup to the MQTT Genserver, this is an issue as the the calls were making call to the current thread and elixir wasn’t happy.
  2. Created a Supervisor for the MQTT GenServer, however online the init in the Application, the init in the Supervisor uses supervise and the MQTT GenServer process is not running yet so initialization fails.
  3. Added a send_after to the init in the supervisor, but the function I declared never gets called and the supervisor reports “Supervisor Received unexpected message”

This is my current supervisor code:

defmodule Persist.Supervisor do
   use Supervisor

   def start_link do
     Supervisor.start_link(__MODULE__, :ok)
   end

   def init(:ok) do
     children = [
       worker(Persist.Mqtt, [:mymqtt, %{}])
     ]

     status = supervise(children, strategy: :one_for_one)
     Process.send_after(self(), {:started, %{}}, 0)
     status
   end

   defp connect_to_mqtt(name) do
     pid = Process.whereis(name)
     IO.inspect pid

     pid |> Persist.Mqtt.connect([client_id: "elixir-client", host: "10.0.1.200", port: 1883])
   end

   defp setup_subscriptions(name) do
     pid = Process.whereis(name)

     # Retrieve the topics from ./config/config.exs !!!
     topics = Application.get_env(:persist, :topics)

     Enum.each(topics, fn(topic) -> Persist.Mqtt.subscribe(pid, [id: 1234, topics: [topic], qoses: [0]]) end)
   end

   def handle_cast({:started, %{}}, state) do
   # ...
     IO.puts "in handle_info"
     {:noreply, state}
   end
 end

p.s. I tried every combination of handle_xx I could think of, cast, call, info, and varied the params

Thanks in advance, Gary

1 Like

Have you seen https://elixirforum.com/t/how-to-start-dynamic-supervisor-workers-on-application-startup? Might be similar to your problem.

I would move setup to a separate Task, which would be called after the children it helps to setup (i.e. it would come later in the children list passed to Supervisor.Spec.supervise/2).

defmodule Persist.Supervisor do
  use Supervisor

  def start_link do
    Supervisor.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
    children = [
      worker(Persist.Mqtt, [:mymqtt, %{}]),
      worker(Task, [&setup/0], restart: :transient)
    ]

    supervise(children, strategy: :one_for_one)
  end


  defp setup do
    # your setup code here
  end
end
2 Likes

Thanks for the response,

I played around a bit and created a separate task module which is started by my MQTT GenServer, this also allowed me to implement a periodic ping, I will keep your solution in mind for the future though,

again, thanks for the help, much appreciated.
Gary

2 Likes

The setup task would have to keep track of the workers it is setting up so that it can redo the setup when a new one is restarted.

2 Likes

Yeah, but since there is only one worker, in this particular example I would just choose :one_for_all strategy for the supervisor.

1 Like

Yes, but the when the server dies it will also kill the setup process. The question is then why have a separate setup process at all and not just do the setup inside the server. I was assuming there was some reason special reason the setup process should be persistent, maybe so it could reinitialise state.

This is not an uncommon method if you want persistent ETS tables which can be accessed by multiple workers and not lose data when they crash. You have an extra process which owns the tables and manages them.

2 Likes

Thanks for all the comments,

in the end I updated the supervisor to :one_for_all, I have a separate module that implements a small delay and then reads the configuration and sends it to the MQTT Client, the timeout of the loop also invokes the PING to keep the connection alive, without this the MQTT Client was being disconnected and things got hung up.

Everything is working now and I’m happy! Thanks got the help.

Gary

1 Like