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 synchronous call/3 messages. call/3 will block until a reply is received (unless the call times out or nodes are disconnected)

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

Link to this type changes_follower() View Source
changes_follower() :: pid | name | {atom, node}
Link to this type debug() View Source
debug() :: [:trace | :log | :statistics | {:log_to_file, Path.t}]
Link to this type from() View Source
from() :: {pid, tag :: term}
Link to this type gen_option() View Source
gen_option ::
  {:debug, debug} |
  {:name, name} |
  {:timeout, timeout} |
  {:spawn_opt, Process.spawn_opt}
Link to this type name() View Source
name() :: atom | {:global, term} | {:via, module, term}
Link to this type on_start() View Source
on_start ::
  {:ok, pid} |
  :ignore |
  {:error, {:already_started, pid} | term}
Link to this type option() View Source
option ::
  {:longpoll, boolean} |
  {:heartbeat, integer} |
  {:timeout, integer} |
  {:include_docs, boolean} |
  {:filter, String.t} |
  {:view, String.t} |
  {:since, integer | String.t} |
  {:doc_ids, [String.t]} |
  {:query_params, map}

Link to this section Functions

Link to this function call(changes_follower, request, timeout \\ 5000) View Source
call(changes_follower, term, timeout) :: term
Link to this function cast(changes_follower, request) View Source
cast(changes_follower, term) :: :ok
Link to this function code_change(old_vsn, state, extra) View Source

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.

Link to this function handle_call(request, from, state) View Source

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.

Link to this function handle_cast(request, state) View Source

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 calling Supervisor.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.

Link to this function reply(client, reply) View Source
reply(from, term) :: :ok
Link to this function start(module, args, options \\ []) View Source
start(module, any, options :: [gen_option]) :: on_start
Link to this function start_link(module, args, options \\ []) View Source
start_link(module, any, options :: [gen_option]) :: on_start
Link to this function stop(changes_follower, reason \\ :normal, timeout \\ :infinity) View Source
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.

Link to this function whereis(changes_follower) View Source
whereis(changes_follower) :: pid | {atom, node} | nil

Link to this section Callbacks

Link to this callback code_change(old_vsn, state, extra) View Source
code_change(old_vsn, state :: term, extra :: term) ::
  {:ok, new_state :: term} |
  {:error, reason :: term} when old_vsn: term | {:down, term}
Link to this callback handle_call(request, from, state) View Source
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
Link to this callback handle_cast(request, state) View Source
handle_cast(request :: term, state :: term) ::
  {:noreply, new_state} |
  {:noreply, new_state, timeout | :hibernate} |
  {:stop, reason :: term, new_state} when new_state: term
Link to this callback handle_change(change, state) View Source
handle_change(change :: %{}, state :: term) ::
  {:ok, new_state} |
  {:ok, new_state, timeout | :hibernate} |
  {:stop, reason :: term, new_state} when new_state: term
Link to this callback handle_info(msg, state) View Source
handle_info(msg :: :timeout | term, state :: term) ::
  {:noreply, new_state} |
  {:noreply, new_state, timeout | :hibernate} |
  {:stop, reason :: term, new_state} when new_state: term
Link to this callback init(args) View Source
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
Link to this callback terminate(reason, state) View Source
terminate(reason, state :: term) :: term when reason: :normal | :shutdown | {:shutdown, term} | term