TinyRedis

Fast, Simple, Unit Tested. Redis driver for D

Latest Version : v2.1.0

By Adil Baig

Introduction

TinyRedis is a Redis driver for the D programming language. It is intentionally minimal, fast, simple, intuitive, clean, unit tested, forward compatible, has no dependencies and makes working with Redis trivial.

It supports all of Redis's data types; keys, hashes, lists, sets etc. There's also support for :

TinyRedis is ..

intentionally minimal

With TinyRedis you leverage your knowledge of Redis commands transparently. This minimizes the ramp-up time from learning the library to writing your application specific code. It's also simpler to build libraries on top of it.

fast

simple local benchmarking suggests TinyRedis can do 40k+ GETs/second. With pipelining the numbers are orders of magnitudes higher. In practice, TinyRedis is fast enough to handle most real-world volumes. Watch the benchmarking space below.

simple

Run any Redis command using one simple call :

Response response = redis.send(string)

That's it. All the other features reduce (more or less) boiler-plate from your code.

intuitive

By leveraging D's magic operator overloading functions, TinyRedis aims to do what you expect.

clean

The encoder is separate from the parser, which is separate from the driver. In theory, we can mix and match and possibly introduce more backends easily.

unit tested

The parser and the driver have unit tests that ship with source.

forward compatible

Due to TinyRedis' minimalist approach, any new commands introduced by Redis can be queried using the same API (as long as the protocol remains intact).

has no dependencies

A modern D2 dmd compiler is all you need.

works with dub

DUB support was recently added, making it even simpler to integrate TinyRedis into your code.

makes working with Redis trivial.

Nuff said. Instructions are below.

Quick Example

Without further ado :


import tinyredis;
import std.stdio; 

void main(){

    auto redis = new Redis("localhost", 6379);
    redis.send("SET name Adil");
    writeln("The name's ", redis.send("GET name")); //The name's Adil
    
    //Add data to a set and read it
    redis.send("SADD", "fruits", ["apples", "oranges", "bananas"]);
    auto fruits = redis.send("SMEMBERS", "fruits");
    foreach(k, fruit; fruits)
        writeln(k, ") ", fruit);
    
    //Cast your results
    writefln("There are %s fruits", redis.send!(uint)("SCARD", fruits));
    
    //intelligent opCasts for responses!
    if(redis.send("EXISTS", fruits))
        writeln("Fruits have been defined!");
    
    //Lua scripting is supported! ..
    Response r1 = redis.eval("return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", ["key1", "key2"], ["first", "second"]);
    writeln(r1); // [key1, key2, first, second]
    
    // .. And pipelining
    redis.pipeline(["SET person1:name Adil", "SET person1:name Batman", "SET person1:name Robin"]);
    
    //.. And transactions
    redis.transaction(["SET ctr 0", "INCR ctr", "INCR ctr"]);
}

Collections

TinyRedis aims to create a series of OOP wrappers over Redis data structures. Currently, there is the Set class.


import tinyredis;
import std.stdio; 

void main(){

    auto conn = new Redis("localhost", 6379);
    auto set  = new Set(conn, "tinyRedisUnitTestSet");
    
    set = ["banana", "apple", "orange"]; // data can be assigned using opAssign
    set.put("grapes"); //the put() function appends a value
    assert(set.scard() == 4); // runs the SCARD command, presents a proper count of the set ON THE SERVER
    
    //opAssign resets the data
    set = ["guava", "pear"];
    assert(set.scard() == 2);
    
    set ~= "banana"; // implements opOpAssign so ~= appends data
    set ~= ["orange", "mango", "apple"];
    set -= "mango"; //Oops, not mango season!
    assert(set.count() == 5);
    
    import std.algorithm.searching : canFind;
    foreach(fruit; set.smembers())
        assert(["guava", "pear", "banana", "apple", "orange"].canFind(fruit.toString));
}

PubSub

PubSub is a lightweight messaging solution built on top of Redis. It allows any Redis client to broadcast and receive messages in an asynchronous fashion. TinyRedis supports PubSub using the Subscriber class. See examples below :


import tinyredis;
import std.stdio; 
import std.socket;

/**
 * Callback function for SUBSCRIBE messages.
 */
void handleMessage(string channel, string message)
{
    writefln("Channel '%s': %s", channel, message);
}

/**
 * Callback function for PSUBSCRIBE messages.
 */
void handlePatternMessage(string pattern, string channel, string message)
{
    writefln("Channel '%s' (matching '%s'): %s", channel, pattern, message);
}

void main(){

    auto redis = new Redis();
    auto sub = new Subscriber();
    
    // Let's SUBCRIBE to a channel, MYROOM
    sub.subscribe("MYROOM", toDelegate(&handleMessage)));
    
    // Now, let's publish something
    redis.send("PUBLISH", "Hello RoomMates!"); 
    sub.unsubscribe("MYROOM");

    /* 
    * Subscribe to glob-style patterns in order to 
    * receive all the messages sent to channel names matching a given pattern    
    * Here we subscribe to for 'news.*'
    */
    sub.psubscribe("news.*", toDelegate(&handleMessage))); // Does a PSUBSCRIBE
    
    redis.send("news.international", "International News");
    redis.send("news.domestic", "Something local"); 
    
    sub.punsubscribe("news.*");
    
}

Documentation

TinyRedis has a very shallow learning curve. You learn best by using it. The following links should help you get started.

Benchmarks

Benchmarks are heavily dependent on your machine's hardware, Linux resource limits, your Redis server version and whether you're running across a network. You may want to run the benchmarking program on your PC/Server to get a real feel for your speed.

Here are the results (after warm-up) from running the benchmarking program on a Ubuntu 14.04 x64 laptop. Intel core i5 4200M 2.5Ghz, 8GB DDR3 RAM

./benchmark -n100000
====== GET ======
100000 requests completed in 2.297 seconds
43535 requests per second

====== SET ======
100000 requests completed in 2.453 seconds
40766 requests per second
For comparison, here are results on the same hardware with hiredis, the official redis driver in C.
redis-benchmark -c 1 -t get,set -n 100000
====== SET ======
  100000 requests completed in 2.85 seconds
  1 parallel clients
  3 bytes payload
  keep alive: 1

100.00% <= 2 milliseconds
100.00% <= 3 milliseconds
35124.70 requests per second

====== GET ======
  100000 requests completed in 2.77 seconds
  1 parallel clients
  3 bytes payload
  keep alive: 1

100.00% <= 1 milliseconds
100.00% <= 2 milliseconds
100.00% <= 4 milliseconds
36127.17 requests per second 

Installation Instructions

Using Dub

Instructions are on the TinyRedis DUB page

Using Source

Download the latest release and compile source/tinyredis/* with your source files. Ex:

dmd YOUR_SOURCE.d source/tinyredis/*

Other programs

A couple of sample programs are included, to demonstrate API usage. Before running any of them, ensure you have a Redis server running on "localhost:6379" (default Redis settings). Running these examples requires either Make or dub.

The Example Program

This program is only useful when read as source. It demonstrates the API. Run:

make example; // or 'dub --config=example;'

The output is not interesting, look at the code.

The Interactive Console

The interactive console works like redis-cli. It is meant to show the simplicity of the driver, and is not a replacement for redis-cli. Run:

make console; // or 'dub --config=console;'

The Benchmarking Program

A simple benchmark is included. To see how redis performs on your PC/Server, Run:

make benchmark; // or 'dub --config=benchmark;'

Unit Tests Included

make test; // or 'dub test'

Dependencies

None, except a standard D2 compiler. Updated to work with DMD 2.071. Tested on Linux x64.

Support and Feedback

For feedback, bug reports or feature requests create a support ticket on github. Feel free to suggest code improvements, new features or anything else. Thanks!

Adil Baig
Blog : thoughtsimproved.wordpress.com
Twitter : @aidezigns

fork TinyRedis on github