4 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
12 var crypto = require('crypto')
13 , Store = require('../store')
14 , assert = require('assert');
17 * Exports the constructor.
20 exports = module.exports = Redis;
21 Redis.Client = Client;
26 * - nodeId (fn) gets an id that uniquely identifies this node
27 * - redis (fn) redis constructor, defaults to redis
28 * - redisPub (object) options to pass to the pub redis client
29 * - redisSub (object) options to pass to the sub redis client
30 * - redisClient (object) options to pass to the general redis client
31 * - pack (fn) custom packing, defaults to JSON or msgpack if installed
32 * - unpack (fn) custom packing, defaults to JSON or msgpack if installed
37 function Redis (opts) {
40 // node id to uniquely identify this node
41 var nodeId = opts.nodeId || function () {
42 // by default, we generate a random id
43 return Math.abs(Math.random() * Math.random() * Date.now() | 0);
46 this.nodeId = nodeId();
48 // packing / unpacking mechanism
50 this.pack = opts.pack;
51 this.unpack = opts.unpack;
54 var msgpack = require('msgpack');
55 this.pack = msgpack.pack;
56 this.unpack = msgpack.unpack;
58 this.pack = JSON.stringify;
59 this.unpack = JSON.parse;
63 var redis = opts.redis || require('redis')
64 , RedisClient = redis.RedisClient;
66 // initialize a pubsub client and a regular client
67 if (opts.redisPub instanceof RedisClient) {
68 this.pub = opts.redisPub;
70 opts.redisPub || (opts.redisPub = {});
71 this.pub = redis.createClient(opts.redisPub.port, opts.redisPub.host, opts.redisPub);
73 if (opts.redisSub instanceof RedisClient) {
74 this.sub = opts.redisSub;
76 opts.redisSub || (opts.redisSub = {});
77 this.sub = redis.createClient(opts.redisSub.port, opts.redisSub.host, opts.redisSub);
79 if (opts.redisClient instanceof RedisClient) {
80 this.cmd = opts.redisClient;
82 opts.redisClient || (opts.redisClient = {});
83 this.cmd = redis.createClient(opts.redisClient.port, opts.redisClient.host, opts.redisClient);
86 Store.call(this, opts);
88 this.sub.setMaxListeners(0);
89 this.setMaxListeners(0);
93 * Inherits from Store.
96 Redis.prototype.__proto__ = Store.prototype;
99 * Publishes a message.
104 Redis.prototype.publish = function (name) {
105 var args = Array.prototype.slice.call(arguments, 1);
106 this.pub.publish(name, this.pack({ nodeId: this.nodeId, args: args }));
107 this.emit.apply(this, ['publish', name].concat(args));
111 * Subscribes to a channel
116 Redis.prototype.subscribe = function (name, consumer, fn) {
117 this.sub.subscribe(name);
119 if (consumer || fn) {
122 self.sub.on('subscribe', function subscribe (ch) {
124 function message (ch, msg) {
126 msg = self.unpack(msg);
128 // we check that the message consumed wasnt emitted by this node
129 if (self.nodeId != msg.nodeId) {
130 consumer.apply(null, msg.args);
135 self.sub.on('message', message);
137 self.on('unsubscribe', function unsubscribe (ch) {
139 self.sub.removeListener('message', message);
140 self.removeListener('unsubscribe', unsubscribe);
144 self.sub.removeListener('subscribe', subscribe);
151 this.emit('subscribe', name, consumer, fn);
160 Redis.prototype.unsubscribe = function (name, fn) {
161 this.sub.unsubscribe(name);
164 var client = this.sub;
166 client.on('unsubscribe', function unsubscribe (ch) {
169 client.removeListener('unsubscribe', unsubscribe);
174 this.emit('unsubscribe', name, fn);
183 Redis.prototype.destroy = function () {
184 Store.prototype.destroy.call(this);
197 function Client (store, id) {
198 Store.Client.call(this, store, id);
202 * Inherits from Store.Client
205 Client.prototype.__proto__ = Store.Client;
213 Client.prototype.get = function (key, fn) {
214 this.store.cmd.hget(this.id, key, fn);
224 Client.prototype.set = function (key, value, fn) {
225 this.store.cmd.hset(this.id, key, value, fn);
235 Client.prototype.del = function (key, fn) {
236 this.store.cmd.hdel(this.id, key, fn);
246 Client.prototype.has = function (key, fn) {
247 this.store.cmd.hexists(this.id, key, function (err, has) {
248 if (err) return fn(err);
257 * @param {Number} number of seconds to expire data
261 Client.prototype.destroy = function (expiration) {
262 if ('number' != typeof expiration) {
263 this.store.cmd.del(this.id);
265 this.store.cmd.expire(this.id, expiration);