Skip to content

Instantly share code, notes, and snippets.

@randysecrist
Last active March 27, 2018 16:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save randysecrist/08ac740cee800d0b74bb13a388b2a063 to your computer and use it in GitHub Desktop.
Save randysecrist/08ac740cee800d0b74bb13a388b2a063 to your computer and use it in GitHub Desktop.
genserver_question
Quick question about a global process and restarts...
I have a genserver which is registered using {global, term} and I've been doing some testing using a 3 node cluster.
I can (from the terminal) kill the pid using whereis(<name>); and I see the process get restarted; usually on the same node it was running on.
If I kill the VM on the node the process is running on (using control-C), the other two nodes (the supervisors), the process does not get restarted. genserver:whereis returns nil at this point.
The process is a worker managed by a supervisor; (there are a few other workers) and the restart strategy is one_to_one.
So I'm trying to figure out how/where to tell something that I want the remaining nodes to restart the process. Perhaps I need to monitor the pid and messages myself? (I'm exploring the boundaries of OTP here).
defmodule FixedHistoricalRate do
use GenServer
def child_spec(%{name: name} = opts) when is_map(opts) do
%{
id: name,
start: {__MODULE__, :start_link, [opts]},
type: :worker,
restart: :transient,
shutdown: 500
}
end
def start_link(%{name: name} = opts) do
name = case opts[:global] do
true -> {:global, name}
false -> name
_ -> name
end
GenServer.start_link(__MODULE__, opts, [name: name])
end
def init(%{name: {:global, name}, max_size: max_size}) do
{:ok, %{} |> Map.put(name, {max_size, 0, []})}
end
def init(%{name: name, max_size: max_size}) do
{:ok, %{} |> Map.put(name, {max_size, 0, []})}
end
# Public API
def hit(name) do
args = {:hit, {name, 1, DateTime.to_unix(DateTime.utc_now(), :microseconds)}}
maybe_global(&GenServer.cast/2, name, args)
end
def calculate(name) do
args = {:calculate, name}
maybe_global(&GenServer.call/2, name, args)
end
# Private API
defp maybe_global(fun, name, args) do
pid = GenServer.whereis(name)
case is_pid(pid) do
true -> pid |> fun.(args)
false -> {:global, name} |> fun.(args)
end
end
def handle_cast({:hit, {name, increment, microseconds}}, state) do
{max_size, count, history} = case state |> Map.get(name) do
nil -> {600, 0, []}
rate_stats -> rate_stats
end
history1 = case length(history) >= max_size do
true ->
# keep state a fixed size
[_head | tail] = history
tail ++ [microseconds]
false -> history ++ [microseconds]
end
state1 = state |> Map.put(name, {max_size, count + increment, history1})
{:noreply, state1}
end
def handle_call({:calculate, name}, _from, state) do
{_max_size, _count, history} = state |> Map.get(name)
case history do
[] -> {:reply, {0, 0, 0}, state}
history ->
size = length(history)
elapsed_time = DateTime.to_unix(DateTime.utc_now() , :microseconds) - List.first(history)
hps = 1000000 / (elapsed_time / size)
{:reply, {hps, elapsed_time, size}, state}
end
end
def handle_call(:view, _from, state) do
{:reply, state, state}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment