Socket
Socket
Sign inDemoInstall

ioredis

Package Overview
Dependencies
Maintainers
1
Versions
228
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ioredis - npm Package Compare versions

Comparing version 1.2.3 to 1.2.4

77

API.md

@@ -5,2 +5,4 @@ ## Classes

<dd></dd>
<dt><a href="#Cluster">Cluster</a> ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code></dt>
<dd></dd>
<dt><a href="#Commander">Commander</a></dt>

@@ -14,2 +16,5 @@ <dd></dd>

</dd>
<dt><a href="#defaultOptions">defaultOptions</a></dt>
<dd><p>Default options</p>
</dd>
</dl>

@@ -179,2 +184,68 @@ <a name="Redis"></a>

**Kind**: static method of <code>[Redis](#Redis)</code>
<a name="Cluster"></a>
## Cluster ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
**Kind**: global class
**Extends:** <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>, <code>[Commander](#Commander)</code>
* [Cluster](#Cluster) ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
* [new Cluster(startupNodes, options)](#new_Cluster_new)
* [.disconnect()](#Cluster#disconnect)
* [.getBuiltinCommands()](#Commander#getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
* [.createBuiltinCommand(commandName)](#Commander#createBuiltinCommand) ⇒ <code>object</code>
* [.defineCommand(name, definition)](#Commander#defineCommand)
<a name="new_Cluster_new"></a>
### new Cluster(startupNodes, options)
Creates a Redis Cluster instance
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| startupNodes | <code>Array.&lt;Object&gt;</code> | | An array of nodes in the cluster, [{ port: number, host: string }] |
| options | <code>Object</code> | | |
| [options.enableOfflineQueue] | <code>boolean</code> | <code>true</code> | See Redis class |
| [options.lazyConnect] | <code>boolean</code> | <code>false</code> | See Redis class |
| [options.maxRedirections] | <code>number</code> | <code>16</code> | When a MOVED or ASK error is received, client will redirect the command to another node. This option limits the max redirections allowed to send a command. |
| [options.clusterRetryStrategy] | <code>function</code> | | See "Quick Start" section |
| [options.retryDelayOnFailover] | <code>number</code> | <code>2000</code> | When an error is received when sending a command(e.g. "Connection is closed." when the target Redis node is down), |
| [options.retryDelayOnClusterDown] | <code>number</code> | <code>1000</code> | When a CLUSTERDOWN error is received, client will retry if `retryDelayOnClusterDown` is valid delay time. |
<a name="Cluster#disconnect"></a>
### cluster.disconnect()
Disconnect from every node in the cluster.
**Kind**: instance method of <code>[Cluster](#Cluster)</code>
**Access:** public
<a name="Commander#getBuiltinCommands"></a>
### cluster.getBuiltinCommands() ⇒ <code>Array.&lt;string&gt;</code>
Return supported builtin commands
**Kind**: instance method of <code>[Cluster](#Cluster)</code>
**Returns**: <code>Array.&lt;string&gt;</code> - command list
**Access:** public
<a name="Commander#createBuiltinCommand"></a>
### cluster.createBuiltinCommand(commandName) ⇒ <code>object</code>
Create a builtin command
**Kind**: instance method of <code>[Cluster](#Cluster)</code>
**Returns**: <code>object</code> - functions
**Access:** public
| Param | Type | Description |
| --- | --- | --- |
| commandName | <code>string</code> | command name |
<a name="Commander#defineCommand"></a>
### cluster.defineCommand(name, definition)
Define a custom command using lua script
**Kind**: instance method of <code>[Cluster](#Cluster)</code>
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| name | <code>string</code> | | the command name |
| definition | <code>object</code> | | |
| definition.lua | <code>string</code> | | the lua code |
| [definition.numberOfKeys] | <code>number</code> | <code></code> | the number of keys. If omit, you have to pass the number of keys as the first argument every time you invoke the command |
<a name="Commander"></a>

@@ -237,1 +308,7 @@ ## Commander

**Access:** protected
<a name="defaultOptions"></a>
## defaultOptions
Default options
**Kind**: global variable
**Access:** protected

@@ -5,2 +5,8 @@ ## Changelog

### v1.2.4 - May 9, 2015
* Try a random node when the target slot isn't served by the cluster.
* Remove `refreshAfterFails` option.
* Try random node when refresh slots.
### v1.2.3 - May 9, 2015

@@ -7,0 +13,0 @@

109

lib/cluster.js

@@ -19,4 +19,2 @@ 'use strict';

* @param {boolean} [options.lazyConnect=false] - See Redis class
* @param {number} [options.refreshAfterFails=4] - When `MOVED` error is received more times than `refreshAfterFails`, client will call CLUSTER SLOTS
command to refresh the slot cache.
* @param {number} [options.maxRedirections=16] - When a MOVED or ASK error is received, client will redirect the

@@ -42,3 +40,2 @@ * command to another node. This option limits the max redirections allowed to send a command.

this.connections = {};
this.fails = 0;
this.retryAttempts = 0;

@@ -58,3 +55,2 @@ this.options = _.defaults(options || {}, this.options || {}, Cluster.defaultOptions);

Cluster.defaultOptions = _.assign({}, Redis.defaultOptions, {
refreshAfterFails: 4,
maxRedirections: 16,

@@ -75,16 +71,10 @@ retryDelayOnFailover: 2000,

var _this = this;
this.startupNodes.forEach(function (options) {
_this.createNode(options.port, options.host);
this.once('refresh', function () {
_this.setStatus('connect');
});
this.refreshSlotsCache(function (err) {
if (err && _this.status !== 'end') {
// Failed to refresh slots, try to reconnect
_this.disconnect(true);
} else if (!err) {
_this.retryAttempts = 0;
_this.manuallyClosing = false;
_this.setStatus('connect');
_this.executeOfflineCommands();
_this.setStatus('ready');
}
this.once('connect', function () {
_this.retryAttempts = 0;
_this.manuallyClosing = false;
_this.setStatus('ready');
_this.executeOfflineCommands();
});

@@ -106,2 +96,7 @@ this.once('end', function () {

});
this.startupNodes.forEach(function (options) {
_this.createNode(options.port, options.host);
});
this.refreshSlotsCache();
};

@@ -160,3 +155,8 @@

if (this.isRefreshing) {
return setTimeout(callback, 50);
if (typeof callback === 'function') {
process.nextTick(function () {
callback();
});
}
return;
}

@@ -171,3 +171,3 @@ this.isRefreshing = true;

var keys = Object.keys(this.nodes);
var keys = _.shuffle(Object.keys(this.nodes));

@@ -189,2 +189,3 @@ var _this = this;

} else {
_this.emit('refresh');
wrapper();

@@ -245,10 +246,4 @@ }

_this.slots[errv[1]] = node;
if (++_this.fails < _this.options.refreshAfterFails) {
tryConnection();
} else {
_this.fails = 0;
_this.refreshSlotsCache(function () {
tryConnection();
});
}
tryConnection();
_this.refreshSlotsCache();
} else {

@@ -283,12 +278,17 @@ debug('command %s is required to ask %s:%s', command.name, hostPort[0], hostPort[1]);

function tryConnection (random, asking) {
var redis;
if (random || typeof targetSlot !== 'number') {
redis = _this.nodes[_.sample(Object.keys(_this.nodes))];
} else if (asking) {
redis = asking;
redis.asking();
} else {
redis = _this.slots[targetSlot];
if (_this.status === 'end') {
command.reject(new Error('Cluster is ended.'));
return;
}
if (redis) {
if (_this.status === 'ready') {
var redis;
if (typeof targetSlot === 'number') {
redis = _this.slots[targetSlot];
} else if (asking && !random) {
redis = asking;
redis.asking();
}
if (random || typeof targetSlot !== 'number' || !redis) {
redis = _this.nodes[_.sample(Object.keys(_this.nodes))];
}
redis.sendCommand(command, stream);

@@ -302,3 +302,3 @@ } else if (_this.options.enableOfflineQueue) {

} else {
command.reject(new Error('Cluster isn\'t connected and enableOfflineQueue options is false'));
command.reject(new Error('Cluster isn\'t ready and enableOfflineQueue options is false'));
}

@@ -345,2 +345,37 @@ }

var multi = Cluster.prototype.multi;
Cluster.prototype.multi = function (options) {
if (options && options.pipeline === false) {
return multi.call(this, options);
}
var ttl = this.options.maxRedirections;
var pipeline = multi.call(this, options);
var exec = pipeline.exec;
pipeline.exec = function (callback) {
var sampleKey;
for (var i = 0; i < this._queue.length; ++i) {
sampleKey = this._queue[i].getKeys()[0];
if (sampleKey) {
break;
}
}
this.slot = sampleKey ? utils.calcSlot(sampleKey) : Math.random() * 16384 | 0;
this.targetRedis = this.redis.slots[this.slot];
var promise = exec.call(pipeline).catch(function (err) {
console.log(err.message);
// if (err.message )
ttl -= 1;
if (ttl <= 0) {
throw new Error('Too many Cluster redirections. Last error: ' + err);
}
return pipeline.exec();
});
return promise.nodeify(callback);
};
return pipeline;
};
module.exports = Cluster;
'use strict';
var _ = require('lodash');
var Command = require('./command');
var Commander = require('./commander');

@@ -10,6 +9,10 @@ var fbuffer = require('flexbuffer');

function Pipeline(redis) {
function Pipeline(redis, options) {
Commander.call(this);
if (!options) {
options = {};
}
this.redis = redis;
this.targetRedis = redis;
this._queue = [];

@@ -70,12 +73,6 @@ this._result = [];

}
var isCluster = this.redis.constructor.name === 'Cluster';
var sampleKey;
// Check whether scripts exists and get a sampleKey.
// Check whether scripts exists
var scripts = [];
for (var i = 0; i < this._queue.length; ++i) {
var item = this._queue[i];
if (isCluster && !sampleKey) {
sampleKey = item.getKeys()[0];
}
if (item.name !== 'evalsha') {

@@ -90,10 +87,2 @@ continue;

}
var sampleSlot;
if (isCluster) {
if (sampleKey) {
sampleSlot = utils.calcSlot(sampleKey);
} else {
sampleSlot = Math.random() * 16384 | 0;
}
}

@@ -103,9 +92,3 @@ var promise;

if (scripts.length) {
var redis;
if (isCluster) {
redis = this.redis.slots[sampleSlot];
} else {
redis = this.redis;
}
promise = redis.script('exists', scripts.map(function (item) {
promise = this.targetRedis.script('exists', scripts.map(function (item) {
return item.sha;

@@ -120,3 +103,3 @@ })).then(function (results) {

return Promise.all(pending.map(function (script) {
return redis.script('load', script.lua);
return _this.redis.script('load', script.lua);
}));

@@ -152,7 +135,3 @@ });

}
if (isCluster) {
_this.redis.slots[sampleSlot].stream.write(data);
} else {
_this.redis.stream.write(data);
}
_this.targetRedis.stream.write(data);
}

@@ -163,3 +142,3 @@ }

for (var i = 0; i < _this._queue.length; ++i) {
_this.redis.sendCommand(_this._queue[i], stream, sampleSlot);
_this.redis.sendCommand(_this._queue[i], stream, _this.slot);
}

@@ -166,0 +145,0 @@ return _this.promise;

{
"name": "ioredis",
"version": "1.2.3",
"version": "1.2.4",
"description": "A delightful, performance-focused Redis client for Node and io.js",

@@ -9,3 +9,3 @@ "main": "index.js",

"test:cov": "NODE_ENV=test DEBUG=ioredis:* node ./node_modules/istanbul/lib/cli.js cover --preserve-comments ./node_modules/mocha/bin/_mocha -- -R spec",
"generate-docs": "jsdoc2md lib/redis.js lib/redis_cluster.js lib/commander.js > API.md",
"generate-docs": "jsdoc2md lib/redis.js lib/cluster.js lib/commander.js > API.md",
"bench": "matcha benchmarks/*.js"

@@ -12,0 +12,0 @@ },

@@ -494,4 +494,2 @@ # ioredis

* `refreshAfterFails`: When `MOVED` errors are received more times than `refreshAfterFails`, client will call CLUSTER SLOTS
command to refresh the slot cache. The default value is `4`.
* `maxRedirections`: When a `MOVED` or `ASK` error is received, client will redirect the

@@ -498,0 +496,0 @@ command to another node. This option limits the max redirections allowed when sending a command. The default value is `16`.

@@ -113,9 +113,11 @@ 'use strict';

var times = 0;
var slotTable = [
[0, 1, ['127.0.0.1', 30001]],
[2, 16383, ['127.0.0.1', 30002]]
];
var node1 = new MockServer(30001, function (argv) {
if (argv[0] === 'cluster' && argv[1] === 'slots') {
return [
[0, 1, ['127.0.0.1', 30001]],
[2, 16383, ['127.0.0.1', 30002]]
];
} else if (argv[0] === 'get' && argv[1] === 'foo') {
return slotTable;
}
if (argv[0] === 'get' && argv[1] === 'foo') {
if (times++ === 1) {

@@ -131,2 +133,5 @@ expect(moved).to.eql(true);

var node2 = new MockServer(30002, function (argv) {
if (argv[0] === 'cluster' && argv[1] === 'slots') {
return slotTable;
}
if (argv[0] === 'get' && argv[1] === 'foo') {

@@ -146,2 +151,42 @@ expect(moved).to.eql(false);

});
it('should auto redirect the command within a pipeline', function (done) {
var moved = false;
var times = 0;
var slotTable = [
[0, 1, ['127.0.0.1', 30001]],
[2, 16383, ['127.0.0.1', 30002]]
];
var node1 = new MockServer(30001, function (argv) {
if (argv[0] === 'cluster' && argv[1] === 'slots') {
return slotTable;
}
if (argv[0] === 'get' && argv[1] === 'foo') {
if (times++ === 1) {
expect(moved).to.eql(true);
process.nextTick(function () {
cluster.disconnect();
disconnect([node1, node2], done);
});
}
}
});
var node2 = new MockServer(30002, function (argv) {
if (argv[0] === 'cluster' && argv[1] === 'slots') {
return slotTable;
}
if (argv[0] === 'get' && argv[1] === 'foo') {
expect(moved).to.eql(false);
moved = true;
return new Error('MOVED ' + utils.calcSlot('foo') + ' 127.0.0.1:30001');
}
});
var cluster = new Redis.Cluster([
{ host: '127.0.0.1', port: '30001' }
], { lazyConnect: false });
cluster.get('foo', function () {
cluster.get('foo');
});
});
});

@@ -153,9 +198,11 @@

var times = 0;
var slotTable = [
[0, 1, ['127.0.0.1', 30001]],
[2, 16383, ['127.0.0.1', 30002]]
];
var node1 = new MockServer(30001, function (argv) {
if (argv[0] === 'cluster' && argv[1] === 'slots') {
return [
[0, 1, ['127.0.0.1', 30001]],
[2, 16383, ['127.0.0.1', 30002]]
];
} else if (argv[0] === 'get' && argv[1] === 'foo') {
return slotTable;
}
if (argv[0] === 'get' && argv[1] === 'foo') {
expect(asked).to.eql(true);

@@ -167,2 +214,5 @@ } else if (argv[0] === 'asking') {

var node2 = new MockServer(30002, function (argv) {
if (argv[0] === 'cluster' && argv[1] === 'slots') {
return slotTable;
}
if (argv[0] === 'get' && argv[1] === 'foo') {

@@ -169,0 +219,0 @@ if (++times === 2) {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc