Packages
redix
0.3.4
1.6.0
1.5.3
1.5.2
1.5.1
1.5.0
1.4.2
1.4.1
1.4.0
1.3.0
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
retired
1.1.0
1.0.0
0.11.2
0.11.1
0.11.0
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.0
Fast, pipelined, resilient Redis driver for Elixir.
Current section
Files
Jump to
Current section
Files
lib/redix/connection.ex
defmodule Redix.Connection do
@moduledoc false
use Connection
alias Redix.Protocol
alias Redix.Utils
alias Redix.Connection.Receiver
require Logger
@type state :: %{}
@initial_state %{
# The TCP socket that holds the connection to Redis
socket: nil,
# Options passed when the connection is started
opts: nil,
# The number of times a reconnection has been attempted
reconnection_attempts: 0,
# The receiver process
receiver: nil,
# TODO remove but used by Auth right now
tail: "",
}
## Callbacks
@doc false
def init(opts) do
{:connect, :init, Dict.merge(@initial_state, opts: opts)}
end
@doc false
def connect(info, state)
def connect(info, state) do
case Utils.connect(info, state) do
{:ok, state} ->
state = start_receiver_and_hand_socket(state)
{:ok, state}
other ->
other
end
end
@doc false
def disconnect(reason, state)
def disconnect(:stop, state) do
{:stop, :normal, state}
end
def disconnect({:error, reason} = _error, state) do
Logger.error ["Disconnected from Redis (#{Utils.format_host(state)}): ",
:inet.format_error(reason)]
:gen_tcp.close(state.socket)
# Backoff with 0 ms as the backoff time to churn through all the commands in
# the mailbox before reconnecting.
state
|> reset_state
|> Utils.backoff_or_stop(0, reason)
end
@doc false
def handle_call(operation, from, state)
def handle_call(_operation, _from, %{socket: nil} = state) do
{:reply, {:error, :closed}, state}
end
def handle_call({:commands, commands}, from, state) do
:ok = Receiver.enqueue(state.receiver, {:commands, from, length(commands)})
Utils.send_noreply(state, Enum.map(commands, &Protocol.pack/1))
end
@doc false
def handle_cast(operation, state)
def handle_cast(:stop, state) do
{:disconnect, :stop, state}
end
@doc false
def handle_info(msg, state)
def handle_info({:receiver, pid, msg}, %{receiver: pid} = state) do
handle_msg_from_receiver(msg, state)
end
## Helper functions
defp reset_state(state) do
%{state | socket: nil}
end
defp start_receiver_and_hand_socket(%{socket: socket, tail: tail, receiver: receiver} = state) do
if receiver && Process.alive?(receiver) do
raise "there already is a receiver: #{inspect receiver}"
end
{:ok, receiver} = Receiver.start_link(sender: self(), socket: socket, tail: tail)
:ok = :gen_tcp.controlling_process(socket, receiver)
%{state | receiver: receiver, tail: ""}
end
defp handle_msg_from_receiver({:tcp_closed, socket}, %{socket: socket} = state) do
{:disconnect, {:error, :tcp_closed}, state}
end
defp handle_msg_from_receiver({:tcp_error, socket, reason}, %{socket: socket} = state) do
{:disconnect, {:error, reason}, state}
end
end