DynamicSupervisor terminate Genserver state in transient restart method

Hi, I have a DynamicSupervisor and Genserver and I want to terminate a state, not all of them, so I send {:stop, :normal, state}, but it calls init again, and I don’t want it.

The document of the Supervisor

:transient - the child process is restarted only if it terminates abnormally, i.e., with an exit reason other than :normal, :shutdown, or {:shutdown, term}.

My code:

I start a state like this (dynamic_supervisor.ex) →

def start_job(args) do
    DynamicSupervisor.start_child(PluginStateOtpRunner, {PluginState, args})

My Genserver code:

defmodule MishkaInstaller.PluginState do
  use GenServer
  def stop(module: module_name) do
      GenServer.cast(pid, {:delete, :module})
  end
  
  def child_spec(process_name) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [process_name]},
      restart: :transient
    }
  end
  
  @impl true
  def handle_cast({:delete, :module}, state) do
    {:stop, :normal, state}
  end
  
  @impl true
  def terminate(reason, state) do
    Logger.warn(
      "#{Map.get(state, :name)} from #{Map.get(state, :event)} event of Plugins manager was Terminated,
      Reason of Terminate #{inspect(reason)}"
      )
  end
end

when I call stop function, it calls the init again, and I cannot delete the state

iex(45)> MishkaInstaller.PluginState.stop(module: "joomla_login")
[warning] joomla_login from after_login event of Plugins manager was Terminated,
      Reason of Terminate :normal
iex(46)> [info] joomla_login from after_login event of Plugins manager system `was started`

I changed use GenServer, restart: :transient to use GenServer, but it did not work for me

How can terminate a state and clean in on ram, thank you.

You could try GenServer.stop instead of sending a message to stop?

1 Like

It is same as {:stop, :normal, state} and it doesn’t work, after terminating it calls init again, I tried it before

  @impl true
  def init(state) do
    Logger.info("#{Map.get(state, :name)} from #{Map.get(state, :event)} event of Plugins manager system was started")
    {:ok, state}
  end

Example code

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

Specs

stop(server(), reason :: term(), timeout()) :: :ok

Synchronously stops the server with the given reason.

The terminate/2 callback of the given server will be invoked before exiting. This function returns :ok if the server terminates with the given reason; if it terminates with another reason, the call exits.

This function keeps OTP semantics regarding error reporting. If the reason is any other than :normal, :shutdown or {:shutdown, _}, an error report is logged.

What about stopping it from the supervisor?

https://hexdocs.pm/elixir/DynamicSupervisor.html#terminate_child/2

2 Likes

It works, but what is the difference between them?
Why does the DynamicSupervisor load it again when I try to clear or shutdown the state in Genserver?

In another topic, he said @kokolegorille kokolegorill:

Buggers me. I would create a small app that shows the issue and file a bug if you’re confident it is not working as expected/documented.

1 Like

Sounds good, if it is a bug, so it should be reported, thank you for your effort.

If you want to see my DynamicSupervisor, maybe I am creating it wrongly

link:

Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:no-native-stack]

Elixir 1.13.2 (compiled with Erlang/OTP 24

Did you try setting max_restarts: 0 in the supervisor?

Assuming a zero value even works…

1 Like

No, I did not, I should set it in my child_spec ?

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

Like this?

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

I think it effects on the other reason of terminating, and I don’t want it

When I set it like this

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

it doesn’t let the supervisor call init again, but as I said.

I think it effects on the other reason of terminating, and I don’t want it

I wonder, did you send an issue for this problem?

Nope. It’s yours to file.

1 Like

I created a sample of dynamic supervisor, and it works very well, I have the problem in my project, and it is not a bug. Unfortunate, I could not find the problem where is.
I made a wrong logic Somewhere.

The sample project, which has no problem:

can you test your termitate function ? maybe stsing interpolation fails and throws?

1 Like

For now, it works, I do not know why, I just down my Docker container and delete the build and recompile again. It works, or I did changed something, which I do not know.

Oh Docker then. You should probably test directly on the machine so there is no chance that you forget to rebuild or something like that.

2 Likes

You can also try several times with Docker’s --no-cache just to make sure it’s indeed the cache / previous build that was the problem.

1 Like

Thank you, this is the third time it pushed me to black hole, I re-compiled, but I needed to clean all the build folder after downing the container and restart it again, I am still not sure about the Supervisor, so these days later I want to test more and if there is no problem so push it.

If your container is not properly rebuilt after code changes then there’s something very wrong with your Dockerfile. Unless I am misunderstanding you.

1 Like

This docker-compose file is just for develop, so I made my elixir code as a volume in my system. sometimes it has like these problems, I do not know why, it needs to down and remove build folder and start again.