shahryarjb

shahryarjb

Hi folks,
I have a DynamicSupervisor and under it, I have a Genserver. Each state, when wants to be started, checks Postgres if there is a record which same as the ID, it loads it to init so if there is not, it creates a simple map.

Imagine something happens and the Genserver is terminated, like pushing to state or something else.

  def terminate(reason, %PluginState{} = state) do
    MishkaInstaller.plugin_activity("read", state, "high", "throw")
    # TODO: Introduce a strategy for preparing again ( load from database, disk ?)
    Logger.warn(
      "#{Map.get(state, :name)} from #{Map.get(state, :event)} event of Plugins manager was Terminated,
      Reason of Terminate #{inspect(reason)}"
    )
  end

First option

Hence, I need to re-init or re-bind this state again, so I get the ID of record from the state is passed in the terminate function and call the DynamicSupervisor.start_child again to create a new state which loads from database.


Second option

If terminate is not :normal I call a function inside another Genserver which loads my DynamicSupervisor registry and check which key in my database does not exist in registry so if there is a record, so it creates a new state again.


Third option

I create a new normal Genserver as Cache which keeps all the {pid, id} of the DynamicSupervisor and if my terminate function is run, so it sends a request to this Cache Genserver and it checks which key or PID is missing in my registry, so after finding it, it creates a new state again.


Which strategy do you suggest me? If your suggestion is not there, please let me know what you prefer to do in this situation.

Thank you

Showing Posts 1 to 8

shahryarjb

shahryarjb OP

Do you have any suggestion about Genserver? :pensive:

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Hey @shahryarjb I guess I’m a bit confused about your question.

When your GenServer is terminated, it’s going to die. I don’t understand what you mean by re-initing its state in terminate, that state is about to be discarded when it dies.

Depending on your restart strategy, the supervisor is going to restart the genserver and then init simply gets called again.

shahryarjb

shahryarjb OP

Hi Dear @benwilson512,
I am sorry because of confusing. Some Unknown errors maybe down or terminate my state, so if a problem happens I want to restart my state, not drop it completely, and bind it with my database data again. I am using strategy: :one_for_one.

I test a function like this:

@impl true
def handle_call({:pop, :module}, _from, %PluginState{} = state) do
  raise "Raising!"
end

I did not find a way to break a state, after it shows me a warning like this (I prefer to see an error instead of warning, but I could not force it to show an error instead of warning):

[warning] test_plug from joomla event of Plugins manager was Terminated,
      Reason of Terminate {%RuntimeError{message: "Raising!"}, [{MishkaInstaller.PluginState, :handle_call, 3, [file: 'lib/plugin_manager/state/plugin_state.ex', line: 94, error_info: %{module: Exception}]}, {:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 721]}, {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 750]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 226]}]}

So after that you can’t access to the state because it is going to be deleted completely, but I have stored all the previous data on my PSQL, so I want to start the state again with the data I saved in my database before.

Unfortunately, with the one_for_one strategy it doesn’t restart the state and call init again, it is the problem I have, and I want it to try 5 times and after that I need it to make a timeout and try 5 times again, after that it skips and do not try if it is not succeed.

Please if you require a more explanation let me know.
Thank you in advance.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Can you show your child spec you’re using for your genserver? The default value is restart: :permanent which should mean that the genserver is automatically re-created.

shahryarjb

shahryarjb OP

Thank you

My Application

defmodule MishkaInstaller.Application do
  use Application
  @impl true
  def start(_type, _args) do
    plugin_runner_config = [
      strategy: :one_for_one,
      name: MishkaInstaller.Cache.PluginStateOtpRunner
    ]
    children = [
      {Registry, keys: :unique, name: MishkaInstaller.PluginStateRegistry},
      {DynamicSupervisor, plugin_runner_config}
    ]
    opts = [strategy: :one_for_one, name: MishkaInstaller.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

My Dynamic Supervisor

defmodule MishkaInstaller.PluginStateDynamicSupervisor do
  @spec start_job(%{id: atom(), type: atom()}) :: :ignore | {:error, any} | {:ok, :add | :edit, pid}
  def start_job(args) do
    DynamicSupervisor.start_child(MishkaInstaller.Cache.PluginStateOtpRunner, {MishkaInstaller.PluginState, args})
    |> case do
      {:ok, pid} -> {:ok, :add, pid}
      {:ok, pid, _any} -> {:ok, :add, pid}
      {:error, {:already_started, pid}} -> {:ok, :edit, pid}
      {:error, result} -> {:error, result}
    end
  end
end

It should be noted, even the record of my registry is deleted after the warning

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Can you show how you’re using the registry? Your children should be getting restarted, but they’ll have a different pid. Are you using the registry to associate children with db ids or something?

shahryarjb

shahryarjb OP

Hi @benwilson512.

Yes, each of my records has a unique ID

def start_link(args) do
  {id, type} = {Map.get(args, :id), Map.get(args, :type)}
  GenServer.start_link(__MODULE__, default(id, type), name: via(id, type))
end

defp via(id, value) do
  {:via, Registry, {MishkaInstaller.PluginStateRegistry, id, value}}
end

IDs like :test_plugin_content or :test_plugin_login, example of registry

[%{id: :test_event, pid: #PID<0.7149.0>, type: :joomla}]

After a problem happens I can’t search by ID, and I lose my Registry record (one of them which has a problem not all of them, just a record)


Search in Registry

def get_plugin_pid(module_name) do
  case Registry.lookup(MishkaInstaller.PluginStateRegistry, module_name) do
    [] -> {:error, :get_plugin_pid}
    [{pid, _type}] -> {:ok, :get_plugin_pid, pid}
  end
end

Genserver function

def get(module: module_name) do
  case PSupervisor.get_plugin_pid(module_name) do
    {:ok, :get_plugin_pid, pid} -> GenServer.call(pid, {:pop, :module})
    {:error, :get_plugin_pid} -> {:error, :get, :not_found}
  end
end

def handle_call({:pop, :module}, _from, %PluginState{} = state) do
  {:reply, state, state}
end

Refs

shahryarjb

shahryarjb OP

I added the below code in my Genserver, and now when I have a problem it calls the init again.

def child_spec(process_name) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [process_name]},
      restart: :permanent
    }
end

I copy this code from this post:
https://levelup.gitconnected.com/genserver-dynamicsupervisor-and-registry-the-elixir-triad-to-manage-processes-a65d4c3351c1

Is it right? Or I am in the wrong way?

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews