Peeper (peeper v0.2.0)
View SourcePeeper.GenServer
is an almost drop-in replacement for GenServer
that preserves the state
between crashes. All the callbacks from GenServer
are supported.
Internally it creates a sub-supervision tree with an actual worker that delegates to the implementation and a state keeper. That said, it creates three processes instead of one, and this should be considered when building a very concurrent applications.
The main use-case would be a long-lived GenServer
process with no error path handling at all
(yeah, that famous Let It Crash.) Using this abstraction, one is free to send unexpected
messages to the process, raise from its handle_×××
clauses and whatnot.
There are two differencies compared to bare GenServer
. init/1
callback cannot return anything
but {:ok, state}
tuple (this might have changed in the future,) and
Peeper.{call/3,cast/2,send/2}
is to be used instead of GenServer.{call/3,cast/2}
and
Kernel.send/2
(this is not gonna change.)
Please note, that whatever is set in init/1
callback as a state, will be overriden by
the latest state available upon restarts. That being said, init/1
would play its role
in setting the state during the first run only.
Instead of using Peeper
’s counterparts, one might either name the process and use Name.GenServer
as a name of the underlying GenServer
or get the GenServer
’s pid
via Peeper.gen_server/1
and use GenServer.{call/3,cast/2}
with it.
Example
iex> {:ok, pid} = Peeper.Impls.Full.start_link(state: 0, name: Counter)
...> Peeper.call(pid, :state)
0
iex> Peeper.cast(pid, :inc)
:ok
iex> GenServer.call(Counter.GenServer, :state)
1
iex> Peeper.call(pid, :state)
1
iex> # emulate crash
...> Process.exit(Peeper.Supervisor.worker(pid), :raise)
...> %{} = Peeper.Supervisor.which_children(pid)
iex> Peeper.call(Counter, :state)
1
iex> Peeper.send(pid, :inc)
:inc
iex> Peeper.call(Counter, :state)
2
start_link/1
The function receives either an initial state
, or a keyword having keys
state
and (optionally) name
. Also this keyword might have a configuration
for the top supervisor (keys: [:strategy, :max_restarts, :max_seconds, :auto_shutdown]
.)
All other values passed would be re-passed to the underlying GenServer
’s options.
Listener
One might pass listener: MyListener
key to PeeperImpl.start_link/1
where MyListener
should be an implementation of Peeper.Listener
behaviour. The callbacks will be called
when the state of the underlying behaviour is changed and on termination respectively.
That might be a good place to attach telemetry events, or logs, or whatnot.
Summary
Functions
The counterpart for GenServer.call/3
. Uses the very same semantics.
The counterpart for GenServer.cast/2
. Uses the very same semantics.
Returns the pid
of the actual GenServer
. Might be used to
avoid the necessity of calling other functions in this module.
Tries to produce a name for the underlying GenServer
process.
Succeeds if the name passed to start_link/1
of the implementation
is an atom()
, {:global, atom()}
, or {:via, module(), atom()}
.
The counterpart for Kernel.send/2
. Uses the very same semantics.
Functions
The counterpart for GenServer.call/3
. Uses the very same semantics.
The counterpart for GenServer.cast/2
. Uses the very same semantics.
Returns the pid
of the actual GenServer
. Might be used to
avoid the necessity of calling other functions in this module.
The pid
returned might be used directly in calls to
GenServer.{call/3,cast/2}
and/or Kernel.send/2
Tries to produce a name for the underlying GenServer
process.
Succeeds if the name passed to start_link/1
of the implementation
is an atom()
, {:global, atom()}
, or {:via, module(), atom()}
.
In such a case, the underlying GenServer
module receives the name
Module.concat(name, GenServer)
and might be used as such.
The counterpart for Kernel.send/2
. Uses the very same semantics.