Hi everyone, I have a gen server that is started as part of a supervision tree. The gen server is then called from another app which is a part of my umbrella project. Now my question is how do I know when the gen server has started and is ready to handle my gen server calls. On its init callback my gen server has to go over a directory of files and load the files, it might take some time. So when calling the gen server I face errors like…
iex(7)> [error] GenServer #PID<0.730.0> terminating
** (stop) exited in: GenServer.call(Csv2sql.Observer, :get_stats, 5000)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
This is the code in my gen server, I have removed the parts which are irrelevant
defmodule Csv2sql.Observer do
use GenServer
@status_list [:pending, :infer_schema, :insert_schema, :insert_data, :finish]
@stage_list [:waiting, :working, :validation, :finish]
def get_stats do
GenServer.call(__MODULE__, :get_stats)
end
def start_link(_) do
GenServer.start_link(__MODULE__, :no_args, name: __MODULE__)
end
def init(_) do
{files_map, files_to_process} = get_file_list()
{:ok,
%{
start_time: DateTime.utc_now(),
file_list: files_map,
files_to_process: files_to_process,
stage: :working,
active_worker_count: Application.get_env(:csv2sql, Csv2sql.MainServer)[:worker_count]
}}
end
def handle_call(
:get_stats,
_from,
state
) do
{:reply, state, state}
end
def get_file_list() do
source_dir = Application.get_env(:csv2sql, Csv2sql.MainServer)[:source_csv_directory]
source_dir
|> File.ls!()
|> Enum.reject(fn file ->
extension =
file
|> String.slice(-4..-1)
|> String.downcase()
extension != ".csv"
end)
|> Enum.reduce({%{}, []}, fn file, {file_map, file_list} ->
path = "#{source_dir}/#{file}"
%{size: size} = File.stat!(path)
file_struct = %Csv2sql.File{
name: String.slice(file, 0..-5),
path: path,
raw_size: size,
humanised_size: Sizeable.filesize(size),
row_count: Csv2sql.ImportValidator.get_count_from_csv(path),
status: :pending
}
{Map.put(file_map, path, file_struct), file_list ++ [path]}
end)
end
end
My gen server gets called from another app in the umbrella project like this…
%{
start_time: start_time,
file_list: file_list,
stage: stage,
active_worker_count: active_worker_count
} = Csv2sql.Observer.get_stats()
My supervision tree does not start automatically I start and stop it manually like…
// Some other code...
{:ok, sup_pid} = Csv2sql.Application.start(:no_args, :no_args)
wait_for_finish()
Supervisor.stop(sup_pid)
end
defp wait_for_finish() do
Csv2sql.Observer.get_stage()
|> case do
:finish ->
# Finish and stop supervisors after a second
:timer.sleep(1000)
_ ->
wait_for_finish()
end
My supervision tree code is like…
children =
[]
|> Kernel.++(repo_supervisor)
|> Kernel.++([
Csv2sql.Observer,
Csv2sql.JobQueueServer,
Csv2sql.DbWorkerSupervisor,
Csv2sql.WorkerSupervisor,
Csv2sql.MainServer
])
opts = [strategy: :one_for_one, name: Csv2sql.Supervisor]
Supervisor.start_link(children, opts)
I need to somehow wait for the gen server to start before making calls to it, the only solution I have found till now is to wait for some time like Process.send_after(self(), :tick, 2000)
here tick will call the gen server later, I make the call after 2 seconds.
The other solution is I catch the error [error] GenServer #PID<0.730.0> terminating
and request the gen server again and keep doing so until the gen server answers.
But I am unhappy with both the above solution, can anyone help me out ?