Skip to content

Commit 17ba148

Browse files
author
Ben Buckman
committed
Revert "undo initial RedisShardingClient". decided to keep it but make simpler.
pulled in partial work from redis-sharding branch, will continue on master.
1 parent ea00678 commit 17ba148

File tree

6 files changed

+392
-17
lines changed

6 files changed

+392
-17
lines changed

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,4 @@ Now use `sentinelClient` as a regular client: `set`, `get`, `hmset`, etc.
7171

7272
# `RedisShardingClient`
7373

74-
**We decided to abandon this after starting to build it.** The concept was another abstraction layer that behaved transparently like a `RedisClient`, but handled sharding (on key) in the background between a ring of multiple clients.
75-
For our particular application (or any particular application), however, the complexity such an abstraction layer involves is not worth the trouble, and a much simpler application-specific abstraction makes a lot more sense.
76-
77-
If anyone is interested in continuing this line of development, see the [sharding-client](https://github.com/DocuSignDev/node_redis/tree/sharding-client) branch.
74+
To be continued...

index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,12 @@ var Sentinel = require('./lib/sentinel');
11131113
exports.RedisSentinelClient = Sentinel.RedisSentinelClient;
11141114

11151115

1116+
// sharding client. instantiate separately.
1117+
var Sharding = require('./lib/sharding');
1118+
exports.RedisShardingClient = Sharding.RedisShardingClient;
1119+
1120+
1121+
11161122
exports.createClient = function (port_arg, host_arg, options) {
11171123
var redis_client;
11181124
var net_client;

lib/sharding.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
[Ben Buckman @ DocuSign]
3+
4+
Adds Redis sharding support, via RedisShardingClient.
5+
Modeled on the RedisSentinelClient (also added in our fork).
6+
7+
(see readme for notes)
8+
*/
9+
10+
var RedisSingleClient = require('../index'),
11+
events = require('events'),
12+
util = require('util'),
13+
reply_to_object = require('./utils').reply_to_object,
14+
commands = RedisSingleClient.commands,
15+
to_array = require('./to_array');
16+
17+
18+
/*
19+
options:
20+
- `clients`: array of RedisClients (or RedisSentinelClients)
21+
- logger (e.g. winston)
22+
- debug (boolean)
23+
*/
24+
function RedisShardingClient(options) {
25+
events.EventEmitter.call(this);
26+
27+
this.options = options || {};
28+
29+
this.clients = this.options.clients;
30+
if (!this.clients || !Array.isArray(this.clients) || !this.clients.length) {
31+
throw new Error("RedisShardingClient needs an array of individual clients");
32+
}
33+
34+
// hashFunction should take (key, num) and return an integer between 0..(num-1)
35+
this.hashFunction = this.options.hashFunction;
36+
if (typeof this.hashFunction !== 'function') {
37+
throw new Error("RedisShardingClient needs a hashFunction.");
38+
}
39+
40+
// @todo pass thru methods, parsing out keys.
41+
42+
// @todo static numeric properties, pass thru the total of all clients?
43+
44+
this.waitForConnected();
45+
this.waitForReady();
46+
}
47+
48+
util.inherits(RedisShardingClient, events.EventEmitter);
49+
50+
51+
// helper, and for testing: get index of client to use, based on key.
52+
RedisShardingClient.prototype.clientIndexFor = function clientIndexFor(key) {
53+
return clientInd = this.hashFunction(key, this.clients.length);
54+
};
55+
56+
// get a single client based on key.
57+
RedisShardingClient.prototype.clientFor = function clientFor(key) {
58+
var clientInd = this.clientIndexFor(key);
59+
return this.clients[clientInd];
60+
};
61+
62+
63+
// helper to wait for all clients to be ready.
64+
RedisShardingClient.prototype.waitForReady = function waitForReady() {
65+
var self = this;
66+
var needCount = this.clients.length;
67+
68+
self.ready = false;
69+
70+
var onAllReady = function() {
71+
needCount--;
72+
if (needCount === 0) {
73+
self.debug("All sub-clients are ready");
74+
self.ready = true;
75+
self.emit("ready");
76+
}
77+
};
78+
79+
this.clients.forEach(function(client){
80+
if (client.ready === true) onAllReady();
81+
else client.on('ready', onAllReady);
82+
});
83+
84+
85+
// @todo follow subsequent ready=false states? bubble up?
86+
};
87+
88+
89+
// helper to wait for all clients to be ready.
90+
RedisShardingClient.prototype.waitForConnected = function waitForConnected() {
91+
var self = this;
92+
var needCount = this.clients.length;
93+
94+
self.connected = false;
95+
96+
var onAllConnected = function() {
97+
needCount--;
98+
if (needCount === 0) {
99+
self.debug("All sub-clients are connected");
100+
self.connected = true;
101+
self.emit("connect");
102+
}
103+
};
104+
105+
this.clients.forEach(function(client){
106+
if (client.connected === true) onAllConnected();
107+
else client.on('connect', onAllConnected);
108+
});
109+
110+
// @todo follow subsequent disconnect events?
111+
};
112+
113+
114+
// @todo implement 'end'?
115+
116+
117+
118+
119+
// @TODO this is duplicated between sentinal and sharding, consolidate.
120+
// log(type, msgs...)
121+
// type is 'info', 'error', 'debug' etc
122+
RedisShardingClient.prototype.log = function log() {
123+
var logger = this.options.logger || console;
124+
var args = Array.prototype.slice.call(arguments);
125+
logger.log.apply(logger, args);
126+
};
127+
// debug(msgs...)
128+
RedisShardingClient.prototype.debug = function debug() {
129+
if (this.options.debug) {
130+
var args = ['debug'].concat(Array.prototype.slice.call(arguments));
131+
this.log.apply(this, args);
132+
}
133+
};
134+
135+
136+
137+
exports.RedisShardingClient = RedisShardingClient;

test/test_sentinel.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,7 @@ var testUtils = require('./utils'),
2121
require_error = testUtils.require_error,
2222
require_number = testUtils.require_number;
2323

24-
25-
function MockLogger(){
26-
var self = this;
27-
this.msgs = [];
28-
this.toConsole = false;
29-
}
30-
MockLogger.prototype.log = function() {
31-
var args = Array.prototype.slice.call(arguments);
32-
this.msgs.push(args);
33-
if (this.toConsole) console.log.apply(this, args);
34-
};
35-
24+
var MockLogger = testUtils.MockLogger;
3625

3726

3827
describe('RedisSentinelClient', function(){

0 commit comments

Comments
 (0)