• Stars
    star
    623
  • Rank 69,240 (Top 2 %)
  • Language
    Erlang
  • License
    MIT License
  • Created about 13 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Erlang Redis client

eredis

Non-blocking Redis client with focus on performance and robustness.

Supported Redis features:

  • Any command, through eredis:q/2
  • Transactions
  • Pipelining
  • Authentication & multiple dbs
  • Pubsub

Example

If you have Redis running on localhost, with default settings, you may copy and paste the following into a shell to try out Eredis:

git clone git://github.com/wooga/eredis.git
cd eredis
./rebar compile
erl -pa ebin/
{ok, C} = eredis:start_link().
{ok, <<"OK">>} = eredis:q(C, ["SET", "foo", "bar"]).
{ok, <<"bar">>} = eredis:q(C, ["GET", "foo"]).

To connect to a Redis instance listening on a Unix domain socket:

{ok, C1} = eredis:start_link({local, "/var/run/redis.sock"}, 0).

MSET and MGET:

KeyValuePairs = ["key1", "value1", "key2", "value2", "key3", "value3"].
{ok, <<"OK">>} = eredis:q(C, ["MSET" | KeyValuePairs]).
{ok, Values} = eredis:q(C, ["MGET" | ["key1", "key2", "key3"]]).

HASH

HashObj = ["id", "objectId", "message", "message", "receiver", "receiver", "status", "read"].
eredis:q(C, ["HMSET", "key" | HashObj]).
{ok, Values} = eredis:q(C, ["HGETALL", "key"]).

LIST

eredis:q(C, ["LPUSH", "keylist", "value"]).
eredis:q(C, ["RPUSH", "keylist", "value"]).
eredis:q(C, ["LRANGE", "keylist",0,-1]).

Transactions:

{ok, <<"OK">>} = eredis:q(C, ["MULTI"]).
{ok, <<"QUEUED">>} = eredis:q(C, ["SET", "foo", "bar"]).
{ok, <<"QUEUED">>} = eredis:q(C, ["SET", "bar", "baz"]).
{ok, [<<"OK">>, <<"OK">>]} = eredis:q(C, ["EXEC"]).

Pipelining:

P1 = [["SET", a, "1"],
      ["LPUSH", b, "3"],
      ["LPUSH", b, "2"]].
[{ok, <<"OK">>}, {ok, <<"1">>}, {ok, <<"2">>}] = eredis:qp(C, P1).

Pubsub:

1> eredis_sub:sub_example().
received {subscribed,<<"foo">>,<0.34.0>}
{<0.34.0>,<0.37.0>}
2> eredis_sub:pub_example().
received {message,<<"foo">>,<<"bar">>,<0.34.0>}

Pattern Subscribe:

1> eredis_sub:psub_example(). 
received {subscribed,<<"foo*">>,<0.33.0>}
{<0.33.0>,<0.36.0>}
2> eredis_sub:ppub_example().
received {pmessage,<<"foo*">>,<<"foo123">>,<<"bar">>,<0.33.0>}
ok
3> 

EUnit tests:

./rebar eunit

Commands

Eredis has one main function to interact with redis, which is eredis:q(Client::pid(), Command::iolist()). The response will either be {ok, Value::binary() | [binary()]} or {error, Message::binary()}. The value is always the exact value returned by Redis, without any type conversion. If Redis returns a list of values, this list is returned in the exact same order without any type conversion.

To send multiple requests to redis in a batch, aka. pipelining requests, you may use eredis:qp(Client::pid(), [Command::iolist()]). This function returns {ok, [Value::binary()]} where the values are the redis responses in the same order as the commands you provided.

To start the client, use any of the eredis:start_link/0,1,2,3,4,5,6,7 functions. They all include sensible defaults. start_link/7 takes the following arguments:

  • Host, dns name or ip adress as string; or unix domain socket as {local, Path} (available in OTP 19+)
  • Port, integer, default is 6379
  • Database, integer or 0 for default database
  • Password, string or empty string([]) for no password
  • Reconnect sleep, integer of milliseconds to sleep between reconnect attempts
  • Connect timeout, timeout value in milliseconds to use in gen_tcp:connect, default is 5000
  • Socket options, proplist of options to be sent to gen_tcp:connect, default is ?SOCKET_OPTS

Reconnecting on Redis down / network failure / timeout / etc

When Eredis for some reason looses the connection to Redis, Eredis will keep trying to reconnect until a connection is successfully established, which includes the AUTH and SELECT calls. The sleep time between attempts to reconnect can be set in the eredis:start_link/5 call.

As long as the connection is down, Eredis will respond to any request immediately with {error, no_connection} without actually trying to connect. This serves as a kind of circuit breaker and prevents a stampede of clients just waiting for a failed connection attempt or gen_server:call timeout.

Note: If Eredis is starting up and cannot connect, it will fail immediately with {connection_error, Reason}.

Pubsub

Thanks to Dave Peticolas (jdavisp3), eredis supports pubsub. eredis_sub offers a separate client that will forward channel messages from Redis to an Erlang process in a "active-once" pattern similar to gen_tcp sockets. After every message sent, the controlling process must acknowledge receipt using eredis_sub:ack_message/1.

If the controlling process does not process messages fast enough, eredis will queue the messages up to a certain queue size controlled by configuration. When the max size is reached, eredis will either drop messages or crash, also based on configuration.

Subscriptions are managed using eredis_sub:subscribe/2 and eredis_sub:unsubscribe/2. When Redis acknowledges the change in subscription, a message is sent to the controlling process for each channel.

eredis also supports Pattern Subscribe using eredis_sub:psubscribe/2 and eredis_sub:unsubscribe/2. As with normal subscriptions, a message is sent to the controlling process for each channel.

As of v1.0.7 the controlling process will be notified in case of reconnection attempts or failures. See test/eredis_sub_tests for details.

AUTH and SELECT

Eredis also implements the AUTH and SELECT calls for you. When the client is started with something else than default values for password and database, it will issue the AUTH and SELECT commands appropriately, even when reconnecting after a timeout.

Benchmarking

Using basho_bench(https://github.com/basho/basho_bench/) you may benchmark Eredis on your own hardware using the provided config and driver. See priv/basho_bench_driver_eredis.config and src/basho_bench_driver_eredis.erl.

Queueing

Eredis uses the same queueing mechanism as Erldis. eredis:q/2 uses gen_server:call/2 to do a blocking call to the client gen_server. The client will immediately send the request to Redis, add the caller to the queue and reply with noreply. This frees the gen_server up to accept new requests and parse responses as they come on the socket.

When data is received on the socket, we call eredis_parser:parse/2 until it returns a value, we then use gen_server:reply/2 to reply to the first process waiting in the queue.

This queueing mechanism works because Redis guarantees that the response will be in the same order as the requests.

Response parsing

The response parser is the biggest difference between Eredis and other libraries like Erldis, redis-erl and redis_pool. The common approach is to either directly block or use active once to get the first part of the response, then repeatedly use gen_tcp:recv/2 to get more data when needed. Profiling identified this as a bottleneck, in particular for MGET and HMGET.

To be as fast as possible, Eredis takes a different approach. The socket is always set to active once, which will let us receive data fast without blocking the gen_server. The tradeoff is that we must parse partial responses, which makes the parser more complex.

In order to make multibulk responses more efficient, the parser will parse all data available and continue where it left off when more data is available.

Future improvements

When the parser is accumulating data, a new binary is generated for every call to parse/2. This might create binaries that will be reference counted. This could be improved by replacing it with an iolist.

When parsing bulk replies, the parser knows the size of the bulk. If the bulk is big and would come in many chunks, this could improved by having the client explicitly use gen_tcp:recv/2 to fetch the entire bulk at once.

Credits

Although this project is almost a complete rewrite, many patterns are the same as you find in Erldis, most notably the queueing of requests.

create_multibulk/1 and to_binary/1 were taken verbatim from Erldis.

More Repositories

1

locker

Atomic distributed "check and set" for short-lived keys
Erlang
152
star
2

etest

A lightweight, convention over configuration test framework for Erlang
Erlang
70
star
3

etest_http

etest Assertions around HTTP (client-side)
Erlang
52
star
4

homebrew-unityversions

Unity versions casks
Ruby
51
star
5

eflatbuffers

Elixir/Erlang flatbuffers implementation
Elixir
51
star
6

Paket.Unity3D

An extension for the Paket dependency manager that enables the integration of NuGet dependencies into Unity3D projects.
F#
42
star
7

kafkaesque

A JRuby-based event stream processing framework for Kafka.
Ruby
27
star
8

bagger

Ruby
18
star
9

atlas-unity

a Unity 3D gradle plugin
Groovy
13
star
10

facebook-signed-request

Ruby Gem which parses and validates Facebook signed requests
Ruby
11
star
11

erlang_prelude

A collection of Erlang modules solving often-encountered problems
Erlang
11
star
12

unity-version-manager

Tool that just manipulates a link to the current unity version
Ruby
8
star
13

circuit_breaker

Basic circuit breaker in Ruby to prevent long running external calls from blocking an application
Ruby
6
star
14

ebloomd

Bloom Filter Server in Erlang
Erlang
5
star
15

nils

Elixir migration orchestration
Elixir
4
star
16

elli_access_log

Erlang
3
star
17

atlas-appcenter

Gradle plugin for HockeyApp uploads
Groovy
2
star
18

play-deliver

Upload screenshots, metadata of your app to the Play Store using a single command
Python
2
star
19

atlas-build-unity

a gradle companion plugin for the wooga internal unity build system
Groovy
2
star
20

reconnaissance

Erlang
2
star
21

atlas-slack

a slack plugin for Gradle.
Groovy
2
star
22

atlas-rust

A simple gradle plugin to build rust library crates
Groovy
2
star
23

atlas-jenkins-pipeline

Jenkins Pipeline shared Library
Groovy
1
star
24

elli_gzip_request

Elli middleware to accept request with a gzip content-encoding.
Erlang
1
star
25

helpshift.gem

Rubygem to interface with the Helpshift API
Ruby
1
star
26

atlas-build-unity-ios

ios build plugin for unity exported Xcode projects
Groovy
1
star
27

spock-github-extension

Spock github test repository extension
Groovy
1
star
28

jenkins-metrics

A simple CLI tool to calculate basic Jenkins Job KPI's
Rust
1
star
29

homebrew-unityversions-beta

Unity versions beta casks
Ruby
1
star
30

atlas-unity-version-manager

a gradle plugin to manage unity version installation
Groovy
1
star
31

atlas-github

Gradle plugin to publish artifacts to github
Groovy
1
star
32

NUnit3-to-NUnit2-Format-Converter

Python Script to convert NUnit3 report xml files to NUnit2 format
Python
1
star