ICouch v0.6.2 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.
Additional Note
When setting the doc_ids
option, any given filter
option will be ignored.
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()
View Sourcedebug() :: [:trace | :log | :statistics | {:log_to_file, Path.t()}]
gen_option()
View Sourcegen_option() :: {:debug, debug()} | {:name, name()} | {:timeout, timeout()} | {:spawn_opt, Process.spawn_opt()}
option()
View Sourceoption() :: ICouch.open_changes_option() | {:longpoll, boolean()} | {:heartbeat, integer()} | {:timeout, integer()}
Link to this section Functions
call(changes_follower, request, timeout \\ 5000)
View Sourcecall(changes_follower(), term(), timeout()) :: term()
cast(changes_follower, request)
View Sourcecast(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.
This callback is optional.
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 that it also sets a timeout.
See the "Timeouts" section in the module documentation for more information.
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.
Returning {:reply, reply, new_state, {:continue, continue}}
is similar to
{:reply, reply, new_state}
except c:handle_continue/2
will be invoked
immediately after with the value continue
as first argument.
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 | {:continue, continue}}
is similar to {:noreply, new_state}
except a timeout, hibernation or continue
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.
This callback is optional. If one is not implemented, the server will fail if a call is performed against it.
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 that it also sets a timeout. See the "Timeouts" section in the module
documentation for more information.
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 {:noreply, new_state, {:continue, continue}}
is similar to
{:noreply, new_state}
except c:handle_continue/2
will be invoked
immediately after with the value continue
as first argument.
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
.
This callback is optional. If one is not implemented, the server will fail if a cast is performed against it.
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
.
This callback is optional. If one is not implemented, the received message will be logged.
Callback implementation for GenServer.handle_info/2
.
Invoked when the server is started. start_link/3
or start/3
will
block until it returns.
init_arg
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 that it also sets a timeout. See the "Timeouts" section
in the module documentation for more information.
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 {:ok, state, {:continue, continue}}
is similar to
{:ok, state}
except that immediately after entering the loop
the c:handle_continue/2
callback will be invoked with the value
continue
as first argument.
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 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, args, options \\ [])
View Sourcestart(module(), any(), options :: [gen_option()]) :: on_start()
start_link(module, args, options \\ [])
View Sourcestart_link(module(), any(), options :: [gen_option()]) :: on_start()
stop(changes_follower, reason \\ :normal, timeout \\ :infinity)
View Sourcestop(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
) does one of the
following:
- returns a
:stop
tuple - raises
- calls
Kernel.exit/1
- returns an invalid value
- the
GenServer
traps exits (usingProcess.flag/2
) and the parent process sends an exit signal
If part of a supervision tree, a GenServer
will receive an exit
signal when the tree is shutting down. The exit signal is based on
the shutdown strategy in the child's specification, where this
value can be:
:brutal_kill
: theGenServer
is killed and soterminate/2
is not called.a timeout value, where the supervisor will send the exit signal
:shutdown
and theGenServer
will have the duration of the timeout to terminate. If after duration of this timeout the process is still alive, it will be killed immediately.
For a more in-depth explanation, please read the "Shutdown values (:shutdown)"
section in the Supervisor
module.
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. There is no cleanup needed when the GenServer
controls a port
(e.g.
:gen_tcp.socket
) or File.io_device/0
, because these will be closed on
receiving a GenServer
's exit signal and do not need to be closed manually
in terminate/2
.
If reason
is neither :normal
, :shutdown
, nor {:shutdown, term}
an error is
logged.
This callback is optional.
Callback implementation for GenServer.terminate/2
.
whereis(changes_follower)
View Sourcewhereis(changes_follower()) :: pid() | {atom(), node()} | nil
Link to this section Callbacks
handle_call(request, from, state)
View Sourcehandle_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()
init(args)
View Sourceinit(args :: term()) :: {:ok, db :: ICouch.DB.t(), state} | {:ok, db :: ICouch.DB.t(), opts :: [option()], state} | {:ok, db :: ICouch.DB.t(), opts :: [option()], state, timeout() | :hibernate} | :ignore | {:stop, reason :: any()} when state: term()