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:
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.
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.
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
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
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
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.
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.