Coder Social home page Coder Social logo

redispp's Introduction

Another C++ client for Redis Build Status

  • Supports pipelining, using the same functions as synchronous requests
  • The included performance test runs about 5 times faster with pipelining than with synchronous requests (single client/thread, on my laptop, to localhost)
  • Depends on boost library
  • Tested on Linux (clang-4.0 and g++-6), Windows (VC++ 2010), Mac (g++, OS X 10.6.5)
  • Includes makefile, bjam jamfiles, and VC++ project
  • Written against Redis 2.0.4 (and currently tested against docker redis 3.2.9)

Performance

This client's pipelining allows it to really push the redis server, even with one client. I ran the included performance benchmark on my Ubuntu 9.10 64-bit virtual machine. The physical machine is quad-core @ 3Ghz with 8GB RAM @ 800Mhz. At 256 requests 'on-the-wire', I reached approximately 230k writes per second with one connection (a write is: conn.set("somemediumkey2", "somemediumvalue")). I was able to reach nearly 260k writes per second with 4096 requests 'on-the-wire'. Below is test/perf.cpp's output for various amounts of outstanding requests.

256 requests on the wire:

bwatling@ubuntu:~/Desktop/redispp$ for cur in `seq 1 10`; do ./test/bin/perf.test/gcc-4.4.1/release/perf 6379 1000000; done
1000000 writes in 4451367 usecs ~= 224650 requests per second
1000000 writes in 4301082 usecs ~= 232500 requests per second
1000000 writes in 4294144 usecs ~= 232875 requests per second
1000000 writes in 4255403 usecs ~= 234995 requests per second
1000000 writes in 4272437 usecs ~= 234058 requests per second
1000000 writes in 4273374 usecs ~= 234007 requests per second
1000000 writes in 4251377 usecs ~= 235218 requests per second
1000000 writes in 4288723 usecs ~= 233170 requests per second
1000000 writes in 4247717 usecs ~= 235421 requests per second
1000000 writes in 4257261 usecs ~= 234893 requests per second

4096 requests on the wire:

bwatling@ubuntu:~/Desktop/redispp$ for cur in `seq 1 10`; do ./test/bin/perf.test/gcc-4.4.1/release/perf 6379 1000000; done
1000000 writes in 4035970 usecs ~= 247772 requests per second
1000000 writes in 3855737 usecs ~= 259354 requests per second
1000000 writes in 3876598 usecs ~= 257958 requests per second
1000000 writes in 3867489 usecs ~= 258566 requests per second
1000000 writes in 3887749 usecs ~= 257218 requests per second
1000000 writes in 3826811 usecs ~= 261314 requests per second
1000000 writes in 3864827 usecs ~= 258744 requests per second
1000000 writes in 3893552 usecs ~= 256835 requests per second
1000000 writes in 3881562 usecs ~= 257628 requests per second
1000000 writes in 3869083 usecs ~= 258459 requests per second

In comparison, with a single client and no pipelining this machine could handle approximately 50k writes per second (using redispp and the same for credis).

Simple example

redispp::Connection conn("127.0.0.1", "6379", "password", false);
conn.set("hello", "world");

Pipelining Example

  • Reply objects take care of reading the response lazily, on demand
  • The response is read in either the destructor or when the return value is used
  • The objects can be nested/scoped in any order. All outstanding replies are read and cached for later when a newer request's response is used.
  • See test/perf.cpp or test/test.cpp for more examples

Up to 64 requests 'on the wire':

VoidReply replies[64];

for(size_t i = 0; i < count; ++i)
{
    replies[i & 63] = conn.set(keys[i], values[i]);
}

Save an object using pipelining. ~BoolReply takes care of reading the responses in order.

{
    BoolReply a = conn.hset("computer", "os", "linux");
    BoolReply b = conn.hset("computer", "speed", "3Ghz");
    BoolReply c = conn.hset("computer", "RAM", "8GB");
    BoolReply d = conn.hset("computer", "cores", "4");
}
//here all the replies have been cleared off conn's socket

Start loading a value, then use it later:

StringReply value = conn.get("world");
//do stuff
std::string theValue = value;

These are resolved immediately:

int hlen = conn.hlen("computer");
std::string value = conn.get("world");

This demonstrates arbitrary nesting/scoping and works as expected (see test/test.cpp). There's no problems caused by a and readA outliving b and c:

{
    VoidReply a = conn.set("one", "a");
    StringReply readA = conn.get("one");
    {
        BoolReply b = conn.hset("two", "two", "b");
        VoidReply c = conn.set("three", "c");
    }
    BOOST_CHECK(readA.result() == "a");
}

Multi Bulk Replies

Request that have multi-bulk replies supply a MultiBulkEnumerator as the return type. The MultiBulkEnumerator will read the data lazily as requested.

Read out a list:

conn.lpush("hello", "a")
conn.lpush("hello", "b")
conn.lpush("hello", "c")
MultiBulkEnumerator result = conn.lrange("hello", 1, 3);
std::string result;
while(result.next(&result))
    std::cout << result << std::endl;

Transactions

The client has basic support for transactions. It currently can open a MULTI and close it with an EXEC. Closing with a DISCARD is not supported yet. WATCH and UNWATCH may also come soon. Here's an example of how to use transactions. Note: it's very important to use the defered reply objects with transactions, or else the connection will be corrupted. (see trans.cpp for more detail).

Transaction trans(&conn);
VoidReply one = conn.set("x", "1");
VoidReply two = conn.set("y", "21");
StringReply three = conn.get("x");
trans.commit();
//access one, two, and three here

Building

  • You should be able to build libredispp.a and libredispp.so by typing 'make'
  • Bjam users can type 'bjam'
  • Windows can use the included VC++ 2010 project file. Be warned I've set it up to simply call bjam. It should be fairly simple to create a regular project or include the source in your own.
  • WARNING The unit tests will not pass unless you change TEST_PORT in test/test.cpp. The entire redis database will be cleared
  • You should run the unit and performance tests with a temporary database, with no production data
  • The performance test will not run unless you start it with a port (ie ./perf 6379)

TODO

  • add a way to listen for messages after subscribing to a channel
  • fill in the missing requests
  • cleanup code, move stuff out of the header to the .cpp file
  • implement a clean method for watch and related functions (using transaction objects)
  • write a consistent hashing wrapper?

License

See LICENSE.md. Credit is appreciated (but not required), and I would like to hear about how you use it (but again, not required).

Contributors

  • Brian Watling
  • Alex Ianus

redispp's People

Contributors

aianus avatar cbascom avatar kevinkjt2000 avatar zed-0xff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redispp's Issues

Need a way to return null from readBulkReply

Currently, Connection::readBulkReply throws an exception on a null bulk reply. The redis protocol page at http://redis.io/topics/protocol states "The client library API should not return an empty string, but a nil object, when the requested object does not exist.".

To this end, what do you think about using shared_ptr or boost::optional instead of just string?

Support subscribe a channel

Current version can not subscribe a channel.

If this client can support this feature , it would be the best client for redis.

Could there be a clearer indication of Public Domain or some other license?

The Open Source Initiative (OSI) does not recommend this method of licensing software. See https://opensource.org/faq#public-domain.

Wikipedia has this article on Public-domain software. Of particular interest is the following:

Under the Berne Convention, which most countries have signed, an author automatically obtains the exclusive copyright to anything they have written, and local law may similarly grant copyright, patent, or trademark rights by default. The Berne Convention also covers programs. Therefore, a program is automatically subject to a copyright, and if it is to be placed in the public domain, the author must explicitly disclaim the copyright and other rights on it in some way, e.g. by a waiver statement.[1] In some Jurisdictions, some rights (in particular moral rights) cannot be disclaimed: for instance, civil law tradition-based German law's "Urheberrecht" differs here from the Anglo-Saxon common law tradition's "copyright" concept.

So this non-license method of placing software into the public domain is a bit problematical, since in order to actually place it in the public domain there should have been a waiver statement disclaiming the copyright and other rights in the software.

Any thoughts on this? Have licenses such as Apache or CC3.0 been considered in the past?

"Buffer is full" exception

Hi.
I use redispp as follows and it works like charm:

#declaration of my RedisAdapter class
using namespace redispp;
class RedisAdapter
{
  RedisAdapter()
  {
    Connection rconn("127.0.0.1", "6379", "");
    conn = &rconn;
    conn->set("one", "first");
  };
  Connection *conn;
};
#good experience from RedisAdapter usage
int main()
{
  RedisAdapter ra;
}

And this:

#declaration of my RedisAdapter class
using namespace redispp;
class RedisAdapter
{
  RedisAdapter()
  {
    Connection rconn("127.0.0.1", "6379", "");
    conn = &rconn;
  };
  Connection *conn;
};
#bad experience from RedisAdapter usage
int main()
{
  RedisAdapter ra;
  ra.conn->set("two", "second");
}

throws me:

terminate called after throwing an instance of 'std::runtime_error'
what():  buffer is full: spot + needed >= end

I understand that this exception means that there is some buffer overflow. Could you please give me some kind of an advice. Maybe i passed something from view.

Causes app to crash when a get is executed on a list.

Causes app to crash when a get is executed on a list.

    std::string keyStd = key.toUtf8().constData();
    std::string valueStd = value.toUtf8().constData();
    this->conn->lpush(keyStd, valueStd);
this->conn->get(keyStd)

abort 6.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.