{"id":27958038,"url":"https://github.com/ericsson/ered","last_synced_at":"2025-05-07T18:15:58.546Z","repository":{"id":37970769,"uuid":"501551631","full_name":"Ericsson/ered","owner":"Ericsson","description":"An Erlang client library for Valkey/Redis Cluster","archived":false,"fork":false,"pushed_at":"2025-05-05T09:36:21.000Z","size":302,"stargazers_count":23,"open_issues_count":5,"forks_count":8,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-07T18:15:53.086Z","etag":null,"topics":["redis","valkey"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Ericsson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-06-09T07:36:12.000Z","updated_at":"2025-05-05T09:36:17.000Z","dependencies_parsed_at":"2024-05-14T08:26:40.568Z","dependency_job_id":"abb0a90a-c17c-425b-a43a-6ebf4296db08","html_url":"https://github.com/Ericsson/ered","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ericsson%2Fered","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ericsson%2Fered/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ericsson%2Fered/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ericsson%2Fered/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ericsson","download_url":"https://codeload.github.com/Ericsson/ered/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252931507,"owners_count":21827112,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["redis","valkey"],"created_at":"2025-05-07T18:15:57.745Z","updated_at":"2025-05-07T18:15:58.504Z","avatar_url":"https://github.com/Ericsson.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"ered\n====\n\nAn Erlang client library for connecting to Valkey Cluster\naiming to replace [eredis](https://github.com/Nordix/eredis) and [eredis_cluster](https://github.com/Nordix/eredis_cluster).\n\nIt also works for open source versions of Redis Cluster, up to 7.2.\n\nStatus: Beta.\n\nFeatures:\n\n* status events\n* queuing and load shedding (to handle burst traffic)\n* pipelining of commands\n* [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) support\n* ASK redirection supporting hash tags\n\nUsage by example\n----------------\n\n```Erlang\n1\u003e {ok, _} = application:ensure_all_started(ered, temporary),\n2\u003e {ok, Pid} = ered:connect_cluster([{\"localhost\", 6379}], []).\n{ok,\u003c0.164.0\u003e}\n3\u003e ered:command(Pid, [\u003c\u003c\"SET\"\u003e\u003e, \u003c\u003c\"mykey\"\u003e\u003e, \u003c\u003c\"42\"\u003e\u003e], \u003c\u003c\"mykey\"\u003e\u003e, 5000).\n{ok,\u003c\u003c\"OK\"\u003e\u003e}\n4\u003e ered:command_async(Pid, [\u003c\u003c\"GET\"\u003e\u003e, \u003c\u003c\"mykey\"\u003e\u003e], \u003c\u003c\"mykey\"\u003e\u003e, fun(Reply) -\u003e io:format(\"Reply: ~p~n\", [Reply]) end).\nok\nReply: {ok,\u003c\u003c\"42\"\u003e\u003e}\n5\u003e ered:close(Pid).\nok\n```\n\nFunctions\n---------\n\n### `connect_cluster/2`\n\n```Erlang\nconnect_cluster([addr()], [opt()]) -\u003e {ok, server_ref()} | {error, term()}.\n```\n\nStart the main process. This will also start the cluster handling\nprocess which will set up clients to the provided addresses and\nfetch the cluster slot map. Once there is a complete slot map and\nall clients processes are connected to their respective nodes, this\nprocess is ready to serve requests. The processes are supervised by\nthe `ered` application, which needs to be started in advance.\n\nOne or more addresses, `addr() :: {inet:socket_address() | inet:hostname(),\ninet:port_number()}`, are used to discover the rest of the cluster.\n\nFor options, see [Options](#options) below.\n\n### `close/1`\n\n```Erlang\nclose(server_ref()) -\u003e ok.\n```\n\nStop the main process. This will also stop the cluster handling\nprocess and in turn disconnect and stop all clients.\n\n### `command/3,4`\n\n```Erlang\ncommand(server_ref(), command(), key()) -\u003e reply().\ncommand(server_ref(), command(), key(), timeout()) -\u003e reply().\n```\n\nSend a command to the cluster. The command will be routed to\nthe correct node client based on the provided key.\nIf the command is a single command then it is represented as a\nlist of binaries where the first binary is the command\nto execute and the rest of the binaries are the arguments.\nIf the command is a pipeline, e.g. multiple commands to executed\nthen they need to all map to the same slot for things to\nwork as expected.\n\n`command/3` is the same as setting the timeout to infinity.\n\n### `command_async/4`\n\n```Erlang\ncommand_async(server_ref(), command(), key(), fun((reply()) -\u003e any())) -\u003e ok.\n```\n\nLike command/3,4 but asynchronous. Instead of returning the reply, the reply\nfunction is applied to the reply when it is available. The reply function\nruns in an unspecified process.\n\n### `command_all/2,3`\n\n```Erlang\ncommand_all(server_ref(), command()) -\u003e [reply()].\ncommand_all(server_ref(), command(), timeout()) -\u003e [reply()].\n```\n\nSend the same command to all connected primary nodes.\n\n### `command_client/2,3`\n\n```Erlang\ncommand_client(client_ref(), command()) -\u003e reply().\ncommand_client(client_ref(), command(), timeout()) -\u003e reply().\n```\n\nSend the command to a specific client without any client routing.\n\n### `command_client_async/3`\n\n```Erlang\ncommand_client_async(client_ref(), command(), reply_fun()) -\u003e ok.\n```\n\nSend command to a specific client in asynchronous fashion. The\nprovided callback function will be called with the reply. Note that\nthe callback function will executing in the client process and\nshould not hang or perform any lengthy task.\n\n### `get_clients/1`\n\n```Erlang\nget_clients(server_ref()) -\u003e [client_ref()].\n```\n\nGet all primary node clients.\n\n### `get_addr_to_client_map/1`\n\n```Erlang\nget_addr_to_client_map(server_ref()) -\u003e #{addr() =\u003e client_ref()}.\n```\n\nGet the address to client mapping. This includes all clients.\n\n### `update_slots/1,2`\n\n```Erlang\nupdate_slots(server_ref()) -\u003e ok.\nupdate_slots(server_ref(), client_ref()) -\u003e ok.\n```\n\nManually trigger a slot mapping update. If a client pid or name is provided and\navailable, this client is used to fetch the slot map. Otherwise a random node is\nused.\n\nOptions\n-------\n\nThe following options can be passed to `connect_cluster/2`:\n\n* `{try_again_delay, non_neg_integer()}`\n\n  If there is a TRYAGAIN response from Valkey then wait this many milliseconds\n  before re-sending the command. Default 200.\n\n* `{redirect_attempts, non_neg_integer()}`\n\n  Only do these many retries or re-sends before giving up and returning the\n  result. This affects ASK, MOVED and TRYAGAIN responses. Default 10.\n\n* `{info_pid, [pid()]}`\n\n  List of pids to receive cluster info messages. See [Info\n  messages](#info-messages) below.\n\n* `{update_slot_wait, non_neg_integer()}`\n\n  CLUSTER SLOTS command is used to fetch slots from the cluster. This\n  value sets how long in milliseconds to wait before trying to send the command\n  again. Default 500.\n\n* `{client_opts, [ered_client:opt()]}`\n\n  Options passed to each client. See [Client options](#client-options) below.\n\n* `{min_replicas, non_neg_integer()}`\n\n  For each primary node, the min number of replicas for the cluster\n  to be considered OK. Default 0.\n\n* `{convergence_check_timeout, timeout()}`\n\n  If non-zero, a check that all primary nodes converge and report identical slot\n  maps, is performed before the cluster is considered OK and the 'cluster_ok'\n  info message is sent. The timeout is how long to wait for replies from all the\n  primary nodes. Default 1000. Set to zero to disable this check.\n\n* `{convergence_check_delay, timeout()}`\n\n  If non-zero, a check that all primary nodes converge and report identical slot\n  maps, is performed after a slot map update when the cluster is already\n  considered OK, but only after the specified delay. Default 5000. Set to zero\n  to disable this check. This option doesn't affect the convergence check\n  performed when the cluster is not yet considered OK.\n\n* `{close_wait, non_neg_integer()}`\n\n  How long to delay the closing of clients that are no longer part of the slot\n  map, in milliseconds. The delay is needed so that messages sent to the client\n  are not lost in transit. Although the closing of these clients is delayed, the\n  clients are put in a state so they respond immediately to commands with\n  `{error, node_deactivated}`. Default 10000.\n\n### Client options\n\nOptions passed to `connect_cluster/2` as the options `{client_opts, [...]}`.\n\n* `{connection_opts, [ered_connection:opt()]}`\n\n  Options passed to the connection module. See [Connection options](#connection-options) below.\n\n* `{max_waiting, non_neg_integer()}`\n\n  Max number of commands allowed to wait in queue. Default 5000.\n\n* `{max_pending, non_neg_integer()}`\n\n  Max number of commands to be pending, i.e. sent to client\n  and waiting for a response. Default 128.\n\n* `{queue_ok_level, non_neg_integer()}`\n\n  If the queue has been full then it is considered ok\n  again when it reaches this level. Default 2000.\n\n* `{reconnect_wait, non_neg_integer()}`\n\n  How long to wait to reconnect after a failed connect attempt. Default 1000.\n\n* `{info_pid, none | pid()}`\n\n  Pid to send status messages to.\n\n* `{resp_version, 2..3}`\n\n  What RESP (the serialization protocol) version to use. Default 3.\n\n* `{node_down_timeout, non_neg_integer()}`\n\n  If there is a connection problem, such as a timeout waiting for a response\n  from Valkey, the ered client tries to set up a new connection. If the\n  connection is not recovered within `node_down_timeout`, then the client\n  considers the node down and clears it's queue (commands get an error reply)\n  and starts rejecting all new commands until the connection is restored.\n  Default 2000.\n\n* `{use_cluster_id, boolean()}`\n\n  Set to true if the CLUSTER ID should be fetched and used in info messages.\n  This is set to true automatically when a client is started as part of a\n  cluster client and false otherwise. (Not useful if the client is used outside\n  of a cluster.)\n\n* `{auth, {Username :: binary(), Password :: binary()}}`\n\n  Username and password for Valkey authentication. If a password is configured\n  without a username, use `default` as the username.\n\n### Connection options\n\nOptions passed to `connect/2` as the options `{client_opts, [{connection_opts, [...]}]}`.\n\n* `{batch_size, non_neg_integer()}`\n\n  If commands are queued up in the process message queue, this is the maximum\n  number of messages that will be received and sent in one call. Default 16.\n\n* `{connect_timeout, timeout()}`\n\n  Timeout passed to `gen_tcp:connect/4` or `ssl:connect/4`. Default infinity.\n\n* `{tcp_options, [gen_tcp:connect_option()]}`\n\n  Options passed to `gen_tcp:connect/4`.\n\n* `{tcp_connect_timeout, timeout()}`\n\n  Timeout passed to `gen_tcp:connect/4`. Default infinity.\n  Deprecated. Replaced by `{connect_timeout, timeout()}`.\n\n* `{tls_options, [ssl:tls_client_option()]}`\n\n  Options passed to `ssl:connect/4`. If this config parameter is present\n  TLS will be used.\n\n* `{tls_connect_timeout, timeout()}`\n\n  Timeout passed to ssl:connect/4. Default infinity.\n  Deprecated. Replaced by `{connect_timeout, timeout()}`.\n\n* `{push_cb, push_cb()}`\n\n  Callback for push notifications.\n\n* `{response_timeout, non_neg_integer()}`\n\n  Timeout when waiting for a response from Valkey in milliseconds. Default 10000.\n\n  When a timeout happens, the connection is closed and the client attempts to\n  set up a new connection. See the client option `node_down_timeout` above.\n\nInfo messages\n-------------\n\nWhen one or more pids have been provided as the option `{info_pid, [pid()]}` to\n`connect/2`, these are the messages ered sends. All messages are maps with at\nleast the key `msg_type`.\n\nMessages about the cluster as a whole:\n\n* `#{msg_type := cluster_ok}` is sent when the cluster is up and running and\n  ered is connected to all nodes.\n\n* `#{msg_type := cluster_not_ok, reason := Reason}` is sent when something is\n  wrong, where Reason is one of the following:\n\n  * `master_down` if one of the primary nodes (formerly called masters) is down or unreachable.\n\n  * `master_queue_full` if one of the primary nodes (formerly called masters) is so busy that the maximum\n    number of queued commands for the node is reached and any further command to\n    that node would be rejected immediately.\n\n  * `pending` if ered has not yet established connections to all nodes.\n\n  * `not_all_slots_covered` if some cluster slot doesn't belong to any node in\n    the cluster.\n\n  * `too_few_replicas` if any of the primary nodes has fewer replicas than the\n    minimum number as specified using the option `{min_replicas,\n    non_neg_integer()`. See options above.\n\n* `#{msg_type := slot_map_updated, slot_map := list(), map_version :=\n  non_neg_integer(), addr := addr()}` is sent when the cluster slot-to-node\n  mapping has been updated.\n\n* `#{msg_type := cluster_slots_error_response, response := any(), addr :=\n  addr()}` is sent when there was an error updating the cluster slot-to-node\n  mapping. The `response` is either an error or the atom `empty` if the CLUSTER\n  SLOTS returned an empty list, which is treated like an error.\n\n* `#{msg_type := cluster_stopped, reason := any()}` when the ered cluster\n  instance is closing down.\n\nMessages about the connection to a specific node are in the following form:\n\n```Erlang\n#{msg_type := MsgType,\n  reason := Reason,\n  master := boolean(),\n  addr := addr(),\n  client_id := pid(),\n  node_id := string()}\n```\n\nThe field `msg_type` identifies what kind of event has happened and is described\nbelow. Reason depends on `msg_type`. Master describes whether the node is a\nprimary (formerly called master) or a replica. Addr is a tuple `{Host, Port}` to the Valkey node. Client id\nis the pid of the `ered_client` responsible for the connection. Node id is\nassigned by Valkey and is used to identify the node within the cluster.\n\nThe possible values of the `msg_type` field for connection events are as\nfollows:\n\n* `connected` when the connection to a node has been established. Reason is\n  `none`.\n\n* `connect_error` when connecting to the node fails or the TLS handshake fails.\n  Reason is as returned by `gen_tcp:connect/4` or `ssl:connect/4`.\n\n* `init_error` when one of the initial commands sent to Valkey has failed, such\n  as the HELLO command. Reason is a list of error reasons.\n\n* `socket_closed` when the connection has been closed, either by the peer or by\n  ered when an error has happened. Reason is `{recv_exit, RecvReason}` if\n  `gen_tcp:recv/3` or `ssl:recv/3` has failed. RecvReason is typically `timeout`\n  or one of the `inet:posix()` errors. Reason is `{send_exit, SendReason}` if\n  `gen_tcp:send/2` or `ssl:send/2` has failed.\n\n* `node_down_timeout` when the connection to a node is lost and the connection\n  has not been re-established within the specified time. Reason is `none`.\n\n* `node_deactivated` when the node is no longer part of the cluster but the\n  client has not yet been stopped. Reason is `none`.\n\n* `client_stopped` when a connection has been terminated, either because the\n  node is no longer part of the cluster or because the user is stopping ered.\n  Reason is the terminate reason of the `ered_client` process, typically the\n  atom `normal`.\n\n* `queue_full` when the command queue to the node is full. Reason `none`.\n\n* `queue_ok` when the command queue to the node is not full anymore. Reason is\n  `none`.\n\nValkey to Erlang Term Representation\n-----------------------------------\n\n| Valkey (RESP3)        | Erlang                                             |\n|-----------------------|----------------------------------------------------|\n| Simple string         | `binary()`                                         |\n| Bulk string           | `binary()`                                         |\n| Verbatim string       | `binary()`                                         |\n| Array (multi-bulk)    | `list()`                                           |\n| Map                   | `map()`                                            |\n| Set                   | `sets:set()` (version 2 in OTP 24+)                |\n| Null                  | `undefined`                                        |\n| Boolean               | `boolean()`                                        |\n| Integer               | `integer()`                                        |\n| Big number            | `integer()`                                        |\n| Float                 | `float()`, `inf`, `neg_inf`, `nan`                 |\n| Error                 | `{error, binary()}`                                |\n| Value with attributes | `{attribute, Value :: any(), Attributes :: map()}` |\n| Push (out-of-band)    | `{push, list()}`                                   |\n\nPub/sub\n-------\n\nEred supports pup/sub, including sharded pub/sub, when RESP3 is used. Pushed\nmessages are delivered to the push callback, so the connection option `push_cb`\nneeds to be provided. See [Connection options](#connection-options).\n\nFor the commands SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, SSUBSCRIBE\nand SUNSUBSCRIBE, `ered:command/3,4` returns `{ok, undefined}` on success and\none message for each channel, pattern or shard channel is delivered to the push\ncallback as a confirmation that subscribing or unsubscribing succeeded.\n\nSubscriptions are tied to a connection. If the connection is lost, ered\nreconnects automatically, but ered does not automatically subscribe to the same\nchannels again after a reconnect. If you want to subscribe again after\nreconnect, [info messages](#info-messages) can be used to detect when a\nconnection goes down or comes up.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericsson%2Fered","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericsson%2Fered","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericsson%2Fered/lists"}