Ericsson / ered

An Erlang client library for Valkey/Redis Cluster
MIT License
11 stars 6 forks source link
redis valkey

ered

An Erlang client library for connecting to Valkey Cluster aiming to replace eredis and eredis_cluster.

It also works for open source versions of Redis Cluster, up to 7.2.

Status: Beta.

Features:

Usage by example

1> {ok, Pid} = ered:start_link([{"localhost", 6379}], []).
{ok,<0.164.0>}
2> ered:command(Pid, [<<"SET">>, <<"mykey">>, <<"42">>], <<"mykey">>, 5000).
{ok,<<"OK">>}
3> ered:command_async(Pid, [<<"GET">>, <<"mykey">>], <<"mykey">>, fun(Reply) -> io:format("Reply: ~p~n", [Reply]) end).
ok
Reply: {ok,<<"42">>}
4> ered:stop(Pid).
ok

Functions

start_link/2

start_link([addr()], [opt()]) -> {ok, server_ref()} | {error, term()}.

Start the main process. This will also start the cluster handling process which will set up clients to the provided addresses and fetch the cluster slot map. Once there is a complete slot map and all Valkey node clients are connected this process is ready to serve requests.

One or more addresses, addr() :: {inet:socket_address() | inet:hostname(), inet:port_number()}, is used to discover the rest of the cluster.

For options, see Options below.

stop/1

stop(server_ref()) -> ok.

Stop the main process. This will also stop the cluster handling process and in turn disconnect and stop all clients.

command/3,4

command(server_ref(), command(), key()) -> reply().
command(server_ref(), command(), key(), timeout()) -> reply().

Send a command to the cluster. The command will be routed to the correct node client based on the provided key. If the command is a single command then it is represented as a list of binaries where the first binary is the command to execute and the rest of the binaries are the arguments. If the command is a pipeline, e.g. multiple commands to executed then they need to all map to the same slot for things to work as expected.

command/3 is the same as setting the timeout to infinity.

command_async/4

command_async(server_ref(), command(), key(), fun((reply()) -> any())) -> ok.

Like command/3,4 but asynchronous. Instead of returning the reply, the reply function is applied to the reply when it is available. The reply function runs in an unspecified process.

command_all/2,3

command_all(server_ref(), command()) -> [reply()].
command_all(server_ref(), command(), timeout()) -> [reply()].

Send the same command to all connected primary nodes.

command_client/2,3

command_client(client_ref(), command()) -> reply().
command_client(client_ref(), command(), timeout()) -> reply().

Send the command to a specific client without any client routing.

command_client_async/3

command_client_async(client_ref(), command(), reply_fun()) -> ok.

Send command to a specific client in asynchronous fashion. The provided callback function will be called with the reply. Note that the callback function will executing in the client process and should not hang or perform any lengthy task.

get_clients/1

get_clients(server_ref()) -> [client_ref()].

Get all primary node clients.

get_addr_to_client_map/1

get_addr_to_client_map(server_ref()) -> #{addr() => client_ref()}.

Get the address to client mapping. This includes all clients.

update_slots/1,2

update_slots(server_ref()) -> ok.
update_slots(server_ref(), client_ref()) -> ok.

Manually trigger a slot mapping update. If a client pid or name is provided and available, this client is used to fetch the slot map. Otherwise a random node is used.

Options

The following options can be passed to start_link/2:

Client options

Options passed to start_link/2 as the options {client_opts, [...]}.

Connection options

Options passed to start_link/2 as the options {client_opts, [{connection_opts, [...]}]}.

Info messages

When one or more pids have been provided as the option {info_pid, [pid()]} to start_link/2, these are the messages ered sends. All messages are maps with at least the key msg_type.

Messages about the cluster as a whole:

Messages about the connection to a specific node are in the following form:

#{msg_type := MsgType,
  reason := Reason,
  master := boolean(),
  addr := addr(),
  client_id := pid(),
  node_id := string()}

The field msg_type identifies what kind of event has happened and is described below. Reason depends on msg_type. Master describes whether the node is a primary (formerly called master) or a replica. Addr is a tuple {Host, Port} to the Valkey node. Client id is the pid of the ered_client responsible for the connection. Node id is assigned by Valkey and is used to identify the node within the cluster.

The possible values of the msg_type field for connection events are as follows:

Valkey to Erlang Term Representation

Valkey (RESP3) Erlang
Simple string binary()
Bulk string binary()
Verbatim string binary()
Array (multi-bulk) list()
Map map()
Set sets:set() (version 2 in OTP 24+)
Null undefined
Boolean boolean()
Integer integer()
Big number integer()
Float float(), inf, neg_inf, nan
Error {error, binary()}
Value with attributes {attribute, Value :: any(), Attributes :: map()}
Push (out-of-band) {push, list()}

Pub/sub

Ered supports pup/sub, including sharded pub/sub, when RESP3 is used. Pushed messages are delivered to the push callback, so the connection option push_cb needs to be provided. See Connection options.

For the commands SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, SSUBSCRIBE and SUNSUBSCRIBE, ered:command/3,4 returns {ok, undefined} on success and one message for each channel, pattern or shard channel is delivered to the push callback as a confirmation that subscribing or unsubscribing succeeded.

Subscriptions are tied to a connection. If the connection is lost, ered reconnects automatically, but ered does not automatically subscribe to the same channels again after a reconnect. If you want to subscribe again after reconnect, info messages can be used to detect when a connection goes down or comes up.