-
-
Save randysecrist/08ac740cee800d0b74bb13a388b2a063 to your computer and use it in GitHub Desktop.
genserver_question
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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