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 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() ::
  ICouch.open_changes_option()
  | {:longpoll, boolean()}
  | {:heartbeat, integer()}
  | {:timeout, integer()}

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.

This callback is optional.

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

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 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 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

retry_now(changes_follower)

View Source
retry_now(changes_follower()) :: :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
Link to this function

terminate(reason, changes_follower)

View Source

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:

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: the GenServer is killed and so terminate/2 is not called.

  • a timeout value, where the supervisor will send the exit signal :shutdown and the GenServer 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.

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_error(error, state)

View Source
handle_error(error :: term(), state :: term()) ::
  {:retry, new_state}
  | {:retry, wait_time :: integer() | :infinity, new_state}
  | {: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 :: [option()], state}
  | {:ok, db :: ICouch.DB.t(), opts :: [option()], 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()