ICouch v0.2.1 ChangesFollower behaviour View Source
A behaviour module for following live changes in a CouchDB.
It is basically an extended GenServer
and also inherits all of GenServer
’s
callbacks and semantics. The main difference is the expected return value of
init/1
and the new handle_change/2
callback.
Resilence
ChangesFollower
tries to be as resilent as possible, automatically
restarting closed connections and resuming on last known sequence numbers for
certain kinds of errors. It also checks if the server actually sends
heartbeats within the expected timeframe and resets the connection on failures.
Callbacks
There are 7 callbacks required to be implemented in a ChangesFollower
. By
adding use ChangesFollower
to your module, Elixir will automatically define
all 7 callbacks for you, leaving it up to you to implement the ones you want
to customize.
Implementation Details
Internally, an ibrowse worker is spawned and monitored. Therefore a
ChangesFollower
is not part of a load balancing pool.
Link to this section Summary
Functions
Invoked to change the state of the GenServer
when a different version of a
module is loaded (hot code swapping) and the state’s term structure should be
changed
Invoked to handle asynchronous cast/2
messages
Invoked to handle all other messages
Invoked when the server is started. start_link/3
or start/3
will
block until it returns
Invoked when the server is about to exit. It should do any cleanup required
Link to this section Types
debug() :: [:trace | :log | :statistics | {:log_to_file, Path.t}]
gen_option :: {:debug, debug} | {:name, name} | {:timeout, timeout} | {:spawn_opt, Process.spawn_opt}
on_start :: {:ok, pid} | :ignore | {:error, {:already_started, pid} | term}
Link to this section Functions
call(changes_follower, term, timeout) :: term
cast(changes_follower, term) :: :ok
Invoked to change the state of the GenServer
when a different version of a
module is loaded (hot code swapping) and the state’s term structure should be
changed.
old_vsn
is the previous version of the module (defined by the @vsn
attribute) when upgrading. When downgrading the previous version is wrapped in
a 2-tuple with first element :down
. state
is the current state of the
GenServer
and extra
is any extra data required to change the state.
Returning {:ok, new_state}
changes the state to new_state
and the code
change is successful.
Returning {:error, reason}
fails the code change with reason reason
and
the state remains as the previous state.
If code_change/3
raises the code change fails and the loop will continue
with its previous state. Therefore this callback does not usually contain side effects.
Callback implementation for GenServer.code_change/3
.
Invoked to handle synchronous call/3
messages. call/3
will block until a
reply is received (unless the call times out or nodes are disconnected).
request
is the request message sent by a call/3
, from
is a 2-tuple
containing the caller’s PID and a term that uniquely identifies the call, and
state
is the current state of the GenServer
.
Returning {:reply, reply, new_state}
sends the response reply
to the
caller and continues the loop with new state new_state
.
Returning {:reply, reply, new_state, timeout}
is similar to
{:reply, reply, new_state}
except handle_info(:timeout, new_state)
will be
called after timeout
milliseconds if no messages are received.
Returning {:reply, reply, new_state, :hibernate}
is similar to
{:reply, reply, new_state}
except the process is hibernated and will
continue the loop once a message is in its message queue. If a message is
already in the message queue this will be immediately. Hibernating a
GenServer
causes garbage collection and leaves a continuous heap that
minimises the memory used by the process.
Hibernating should not be used aggressively as too much time could be spent garbage collecting. Normally it should only be used when a message is not expected soon and minimising the memory of the process is shown to be beneficial.
Returning {:noreply, new_state}
does not send a response to the caller and
continues the loop with new state new_state
. The response must be sent with
reply/2
.
There are three main use cases for not replying using the return value:
- To reply before returning from the callback because the response is known before calling a slow function.
- To reply after returning from the callback because the response is not yet available.
- To reply from another process, such as a task.
When replying from another process the GenServer
should exit if the other
process exits without replying as the caller will be blocking awaiting a
reply.
Returning {:noreply, new_state, timeout | :hibernate}
is similar to
{:noreply, new_state}
except a timeout or hibernation occurs as with a
:reply
tuple.
Returning {:stop, reason, reply, new_state}
stops the loop and terminate/2
is called with reason reason
and state new_state
. Then the reply
is sent
as the response to call and the process exits with reason reason
.
Returning {:stop, reason, new_state}
is similar to
{:stop, reason, reply, new_state}
except a reply is not sent.
If this callback is not implemented, the default implementation by
use GenServer
will return {:stop, {:bad_call, request}, state}
.
Callback implementation for GenServer.handle_call/3
.
Invoked to handle asynchronous cast/2
messages.
request
is the request message sent by a cast/2
and state
is the current
state of the GenServer
.
Returning {:noreply, new_state}
continues the loop with new state new_state
.
Returning {:noreply, new_state, timeout}
is similar to
{:noreply, new_state}
except handle_info(:timeout, new_state)
will be
called after timeout
milliseconds if no messages are received.
Returning {:noreply, new_state, :hibernate}
is similar to
{:noreply, new_state}
except the process is hibernated before continuing the
loop. See handle_call/3
for more information.
Returning {:stop, reason, new_state}
stops the loop and terminate/2
is
called with the reason reason
and state new_state
. The process exits with
reason reason
.
If this callback is not implemented, the default implementation by
use GenServer
will return {:stop, {:bad_cast, request}, state}
.
Callback implementation for GenServer.handle_cast/2
.
Invoked to handle all other messages.
msg
is the message and state
is the current state of the GenServer
. When
a timeout occurs the message is :timeout
.
Return values are the same as handle_cast/2
.
If this callback is not implemented, the default implementation by
use GenServer
will return {:noreply, state}
.
Callback implementation for GenServer.handle_info/2
.
Invoked when the server is started. start_link/3
or start/3
will
block until it returns.
args
is the argument term (second argument) passed to start_link/3
.
Returning {:ok, state}
will cause start_link/3
to return
{:ok, pid}
and the process to enter its loop.
Returning {:ok, state, timeout}
is similar to {:ok, state}
except handle_info(:timeout, state)
will be called after timeout
milliseconds if no messages are received within the timeout.
Returning {:ok, state, :hibernate}
is similar to
{:ok, state}
except the process is hibernated before entering the loop. See
handle_call/3
for more information on hibernation.
Returning :ignore
will cause start_link/3
to return :ignore
and the
process will exit normally without entering the loop or calling terminate/2
.
If used when part of a supervision tree the parent supervisor will not fail
to start nor immediately try to restart the GenServer
. The remainder of the
supervision tree will be (re)started and so the GenServer
should not be
required by other processes. It can be started later with
Supervisor.restart_child/2
as the child specification is saved in the parent
supervisor. The main use cases for this are:
- The
GenServer
is disabled by configuration but might be enabled later. - An error occurred and it will be handled by a different mechanism than the
Supervisor
. Likely this approach involves callingSupervisor.restart_child/2
after a delay to attempt a restart.
Returning {:stop, reason}
will cause start_link/3
to return
{:error, reason}
and the process to exit with reason reason
without
entering the loop or calling terminate/2
.
Callback implementation for GenServer.init/1
.
start(module, any, options :: [gen_option]) :: on_start
start_link(module, any, options :: [gen_option]) :: on_start
stop(changes_follower, reason :: term, timeout) :: :ok
Invoked when the server is about to exit. It should do any cleanup required.
reason
is exit reason and state
is the current state of the GenServer
.
The return value is ignored.
terminate/2
is called if a callback (except init/1
) returns a :stop
tuple, raises, calls Kernel.exit/1
or returns an invalid value. It may also
be called if the GenServer
traps exits using Process.flag/2
and the
parent process sends an exit signal.
If part of a supervision tree a GenServer
’s Supervisor
will send an exit
signal when shutting it down. The exit signal is based on the shutdown
strategy in the child’s specification. If it is :brutal_kill
the GenServer
is killed and so terminate/2
is not called. However if it is a timeout the
Supervisor
will send the exit signal :shutdown
and the GenServer
will
have the duration of the timeout to call terminate/2
- if the process is
still alive after the timeout it is killed.
If the GenServer
receives an exit signal (that is not :normal
) from any
process when it is not trapping exits it will exit abruptly with the same
reason and so not call terminate/2
. Note that a process does NOT trap
exits by default and an exit signal is sent when a linked process exits or its
node is disconnected.
Therefore it is not guaranteed that terminate/2
is called when a GenServer
exits. For such reasons, we usually recommend important clean-up rules to
happen in separated processes either by use of monitoring or by links
themselves. For example if the GenServer
controls a port
(e.g.
:gen_tcp.socket
) or File.io_device/0
, they will be closed on receiving a
GenServer
’s exit signal and do not need to be closed in terminate/2
.
If reason
is not :normal
, :shutdown
nor {:shutdown, term}
an error is
logged.
Callback implementation for GenServer.terminate/2
.
whereis(changes_follower) :: pid | {atom, node} | nil
Link to this section Callbacks
code_change(old_vsn, state :: term, extra :: term) :: {:ok, new_state :: term} | {:error, reason :: term} when old_vsn: term | {:down, term}
handle_call(request :: term, from, state :: term) :: {:reply, reply, new_state} | {:reply, reply, new_state, timeout | :hibernate} | {:noreply, new_state} | {:noreply, new_state, timeout | :hibernate} | {:stop, reason, reply, new_state} | {:stop, reason, new_state} when reply: term, new_state: term, reason: term
handle_cast(request :: term, state :: term) :: {:noreply, new_state} | {:noreply, new_state, timeout | :hibernate} | {:stop, reason :: term, new_state} when new_state: term
handle_change(change :: %{}, state :: term) :: {:ok, new_state} | {:ok, new_state, timeout | :hibernate} | {:stop, reason :: term, new_state} when new_state: term
handle_info(msg :: :timeout | term, state :: term) :: {:noreply, new_state} | {:noreply, new_state, timeout | :hibernate} | {:stop, reason :: term, new_state} when new_state: term
init(args :: term) :: {:ok, db :: ICouch.DB.t, state} | {:ok, db :: ICouch.DB.t, opts :: [], state} | {:ok, db :: ICouch.DB.t, opts :: [], state, timeout | :hibernate} | :ignore | {:stop, reason :: any} when state: term
terminate(reason, state :: term) :: term when reason: :normal | :shutdown | {:shutdown, term} | term