Connection

A behaviour module for implementing connection processes.

The Connection behaviour is a superset of the GenServer behaviour. The additional return values and callbacks are designed to aid building a connection process that can handle a peer being (temporarily) unavailable.

An example Connection process:

defmodule TCPConnection do

  use Connection

  def start_link(host, port, opts, timeout \\ 5000) do
    Connection.start_link(__MODULE__, {host, port, opts, timeout})
  end

  def send(conn, data), do: Connection.call(conn, {:send, data})

  def recv(conn, bytes, timeout \\ 3000) do
    Connection.call(conn, {:recv, bytes, timeout})
  end

  def close(conn), do: Connection.call(conn, :close)

  def init({host, port, opts, timeout}) do
    s = %{host: host, port: port, opts: opts, timeout: timeout, sock: nil}
    {:connect, :init, s}
  end

  def connect(_, %{sock: nil, host: host, port: port, opts: opts,
  timeout: timeout} = s) do
    case :gen_tcp.connect(host, port, [active: false] ++ opts, timeout) do
      {:ok, sock} ->
        {:ok, %{s | sock: sock}}
      {:error, _} ->
        {:backoff, 1000, s}
    end
  end

  def disconnect(info, %{sock: sock} = s) do
    :ok = :gen_tcp.close(sock)
    case info do
      {:close, from} ->
        Connection.reply(from, :ok)
      {:error, :closed} ->
        :error_logger.format("Connection closed~n", [])
      {:error, reason} ->
        reason = :inet.format_error(reason)
        :error_logger.format("Connection error: ~s~n", [reason])
    end
    {:connect, :reconnect, %{s | sock: nil}}
  end

  def handle_call(_, _, %{sock: nil} = s) do
    {:reply, {:error, :closed}, s}
  end

  def handle_call({:send, data}, _, %{sock: sock} = s) do
    case :gen_tcp.send(sock, data) do
      :ok ->
       {:reply, :ok, s}
      {:error, _} = error ->
        {:disconnect, error, error, s}
    end
  end
  def handle_call({:recv, bytes, timeout}, _, %{sock: sock} = s) do
    case :gen_tcp.recv(sock, bytes, timeout) do
      {:ok, _} = ok ->
        {:reply, ok, s}
      {:error, :timeout} = timeout ->
        {:reply, timeout, s}
      {:error, _} = error ->
        {:disconnect, error, error, s}
    end
  end
  def handle_call(:close, from, s) do
    {:disconnect, {:close, from}, s}
  end
end

The example above follows a common pattern. Try to connect immediately, if that fails backoff and retry after a delay. If a retry fails make another attempt after another delay. If the process disconnects a reconnection attempt is made immediately, if that fails backoff begins.

Importantly when backing off requests will still be received by the process, which will need to be handled. In the above example the process replies with {:error, :closed} when it is disconnected.

Source

Summary

call(conn, req)

Sends a synchronous call to the Connection process and waits for a reply

call(conn, req, timeout)

Sends a synchronous request to the Connection process and waits for a reply

cast(conn, req)

Sends a asynchronous request to the Connection process

reply(from, response)

Sends a reply to a request sent by call/3

start(mod, args, opts \\ [])

Starts a Connection process without links (outside of a supervision tree)

start_link(mod, args, opts \\ [])

Starts a Connection process linked to the current process

Functions

call(conn, req)

Sends a synchronous call to the Connection process and waits for a reply.

See GenServer.call/2 for more information.

Source
call(conn, req, timeout)

Sends a synchronous request to the Connection process and waits for a reply.

See GenServer.call/3 for more information.

Source
cast(conn, req)

Sends a asynchronous request to the Connection process.

See GenServer.cast/2 for more information.

Source
reply(from, response)

Sends a reply to a request sent by call/3.

See GenServer.reply/2 for more information.

Source
start(mod, args, opts \\ [])

Specs:

Starts a Connection process without links (outside of a supervision tree).

See start_link/3 for more information.

Source
start_link(mod, args, opts \\ [])

Specs:

Starts a Connection process linked to the current process.

This function is used to start a Connection process in a supervision tree. The process will be started by calling init/1 in the callback module with the given argument.

This function will return after init/1 has returned in the spawned process. The return values are controlled by the init/1 callback.

See GenServer.start_link/3 for more information.

Source