ptaoussanis/carmine — pull request #73 by bpoweski (2014-02-03 03:00:34+00:00)
Title: Redis Cluster support

Good day Peter!

Given the accelerated progress of Redis Cluster (http://antirez.com/news/70), I've taken it upon myself to experiment with adding (limited) support to Carmine to catalyze your thoughts on how support might be achieved.

#### High Level Design

The approach taken was to layer support atop the existing the existing functionality without necessitating much change to the core. My goal was to make it as much of a drop-in replacement as possible. Accomplishing this presented a challenge, as Redis Cluster expects requests are routed to the correct instance based on the computed keyslot value (similar to the memcache/Couchbase vBucket design).

The solution I came up with was to change ```taoensso.carmine.protocol/send-request``` to a dynamic var and add an alternate [version](https://github.com/bpoweski/carmine/blob/cluster/src/taoensso/carmine/cluster.clj#L104) that invokes ```wcar``` for each Redis command. The consequence is that pipelining isn't maintained.

This approach does maintain support for custom command functions (at least in the trivial examples I tested, YMMV). This provides a unique benefit in that multi-key commands (e.g. ```MGET```, ```MSET```), which are not supported in Redis Cluster, could be faked by swapping a Clojure implementation that decomposes the request into its respective primitive operations.

However, pipelining support is very important to minimize latency. The best way I could think of to achieve both goals is to change the command functions to return a "plan" of what commands/args to execute (in a form such as what ```taoensso.carmine/redis-call``` expects) to allow the commands to be grouped by keys and sent to Redis (possibly in parallel). Any moved keys could then be retried with the updated host.

#### Reference Material

* Ruby driver - https://github.com/antirez/redis-rb-cluster/blob/master/cluster.rb
* Cluster management - https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb
* Specification - http://redis.io/topics/cluster-spec
* Cluster setup tutorial - http://redis.io/topics/cluster-tutorial

#### Useful Features

Additional features to support Redis Cluster I think would be useful to the project beyond basic support:

* Pipelining support
* Pre-conditions to prevent use of invalid commands (surprisingly, ```MGET``` works with 1 key but fails if you give it two keys)
* Clojure implementations of multi-key commands (```MGET```, ```MSET```, ```BITOP```, etc)
* Eagerly fetching the keyslot cache (really a must, I believe) using the ```CLUSTER NODES``` Redis command
* Periodic updates of the keyslot cache to minimize MOVED errors
* Commands for management of nodes

In conclusion, I hope you find some of this useful. And thank you for your work on Carmine (we use it getaroom.com). While Redis Cluster is likely to not be the majority use case, I do believe adding support would be worthwhile for the project.