Skip to content

Commit ba12e47

Browse files
committed
feat: revisit of .to(nodeGroup) command
Provides ability to send arbitrary commands too all nodes in a requested group or just a handy helper to select nodes from such a group Supports 'all', 'masters' and 'slaves' groups exposing `call` and `callBuffer` command semantics similar to Commander
1 parent 1e26631 commit ba12e47

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

README.md

+37
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,43 @@ but a few so that if one is unreachable the client will try the next one, and th
664664
* `retryDelayOnClusterDown`: When a cluster is down, all commands will be rejected with the error of `CLUSTERDOWN`. If this option is a number (by default, it is 1000), the client
665665
will resend the commands after the specified time (in ms).
666666

667+
### Running same operation on multiple nodes
668+
669+
Some operations, such as `flushdb` and `flushall`, can only be performed on a single node.
670+
There is a helper function for this case:
671+
672+
```javascript
673+
var redis = new redis.Cluster();
674+
675+
// groups are: all, masters and slaves
676+
// returns object or throws an error on invalid group
677+
// {
678+
// nodes: Array,
679+
// call: Function,
680+
// callBuffer: function,
681+
// }
682+
redis.to('group')
683+
684+
// will be performed on all master nodes
685+
Promise.map(redis.to('masters').nodes, function(node) {
686+
return node.flushdb();
687+
});
688+
689+
// exactly the same as above
690+
redis.to('masters').call('flushdb').then(function (results) {
691+
//
692+
})
693+
694+
redis.to('slaves').call('flushdb', function (err, results) {
695+
696+
});
697+
698+
// in case of buffer
699+
redis.to('slaves').callBuffer('get', 'key').catch(function (err) {
700+
// likely rejected, because operations would only succeed partially due to slot | moved error
701+
});
702+
```
703+
667704
### Transaction and pipeline in Cluster mode
668705
Almost all features that are supported by `Redis` are also supported by `Redis.Cluster`, e.g. custom commands, transaction and pipeline.
669706
However there are some differences when using transaction and pipeline in Cluster mode:

lib/cluster.js

+53
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,59 @@ Cluster.prototype.executeClusterDownCommands = function () {
399399
}
400400
};
401401

402+
Cluster.prototype.to = function (name) {
403+
var fnName = '_select' + name[0].toUpperCase() + name.slice(1);
404+
var fn = typeof this[fnName];
405+
if (typeof fn !== 'function') {
406+
// programmatic error, can't happen in prod, so throw
407+
throw new Error('to ' + name + ' is not a valid group of nodes');
408+
}
409+
410+
// could be 0 nodes just as well
411+
var nodes = fn();
412+
return {
413+
nodes: nodes,
414+
call: this._generateCallNodes(nodes, 'call'),
415+
callBuffer: this._generateCallNodes(nodes, 'callBuffer')
416+
};
417+
};
418+
419+
Cluster.prototype._generateCallNodes = function (nodes, op, _opts) {
420+
var opts = _opts || {};
421+
422+
return function callNode() {
423+
var argLength = arguments.length;
424+
var hasCb = typeof arguments[argLength - 1] === 'function';
425+
var args = new Array(argLength);
426+
for (var i = 0; i < argLength; ++i) {
427+
args[i] = arguments[i];
428+
}
429+
430+
var callback = hasCb ? args.pop() : null;
431+
var promise = Promise.map(function (node) {
432+
return node[op].apply(node, args);
433+
}, opts);
434+
435+
if (callback) {
436+
return promise.nodeify(callback);
437+
}
438+
439+
return promise;
440+
};
441+
};
442+
443+
Cluster.prototype._selectAll = function () {
444+
return _.values(this.nodes);
445+
};
446+
447+
Cluster.prototype._selectMasters = function () {
448+
return _.values(this.masterNodes);
449+
};
450+
451+
Cluster.prototype._selectSlaves = function () {
452+
return _.difference(this._selectAll(), this._selectMasters());
453+
};
454+
402455
Cluster.prototype.sendCommand = function (command, stream, node) {
403456
if (this.status === 'end') {
404457
command.reject(new Error('Connection is closed.'));

0 commit comments

Comments
 (0)