Skip to content

Commit

Permalink
feat: upgrade JavaScript Parser and drop the hiredis parser
Browse files Browse the repository at this point in the history
The new JavaScript Parser has a better performance than the old version and even hiredis. Also, this pull request introduce the denque module for better queuing performance.

BREAKING CHANGE: Although the interface doesn't change after upgrading the js parser, there may be still some potential internal differences that may break the applications which rely on them. Also, force a major version bump emphasizes the dropping of the hiredis.
  • Loading branch information
luin committed Jan 25, 2017
1 parent 865219c commit 2820de7
Show file tree
Hide file tree
Showing 10 changed files with 23 additions and 59 deletions.
5 changes: 2 additions & 3 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ Creates a Redis instance
| [options.connectionName] | <code>string</code> | <code>null</code> | Connection name. |
| [options.db] | <code>number</code> | <code>0</code> | Database index to use. |
| [options.password] | <code>string</code> | <code>null</code> | If set, client will send AUTH command with the value of this option when connected. |
| [options.parser] | <code>string</code> | <code>null</code> | Either "hiredis" or "javascript". If not set, "hiredis" parser will be used if it's installed (`npm install hiredis`), otherwise "javascript" parser will be used. |
| [options.dropBufferSupport] | <code>boolean</code> | <code>false</code> | Drop the buffer support for better performance. This option is recommended to be enabled when "hiredis" parser is used. Refer to https://github.com/luin/ioredis/wiki/Improve-Performance for more details. |
| [options.dropBufferSupport] | <code>boolean</code> | <code>false</code> | Drop the buffer support for better performance. This option is recommended to be enabled when handling large array response and you don't need the buffer support. |
| [options.enableReadyCheck] | <code>boolean</code> | <code>true</code> | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server not respond to any commands. To work around this, when this option is `true`, ioredis will check the status of the Redis server, and when the Redis server is able to process commands, a `ready` event will be emitted. |
| [options.enableOfflineQueue] | <code>boolean</code> | <code>true</code> | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection is "ready" (when `enableReadyCheck` is `true`, "ready" means the Redis server has loaded the database from disk, otherwise means the connection to the Redis server has been established). If this option is false, when execute the command when the connection isn't ready, an error will be returned. |
| [options.connectTimeout] | <code>number</code> | <code>10000</code> | The milliseconds before a timeout occurs during the initial connection to the Redis server. |
Expand All @@ -72,7 +71,7 @@ Creates a Redis instance
| [options.retryStrategy] | <code>function</code> | | See "Quick Start" section |
| [options.reconnectOnError] | <code>function</code> | | See "Quick Start" section |
| [options.readOnly] | <code>boolean</code> | <code>false</code> | Enable READONLY mode for the connection. Only available for cluster mode. |
| [options.stringNumbers] | <code>boolean</code> | <code>false</code> | Force numbers to be always returned as JavaScript strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range). Notice that when this option is enabled, the JavaScript parser will be used even "hiredis" is specified because only JavaScript parser supports this feature for the time being. |
| [options.stringNumbers] | <code>boolean</code> | <code>false</code> | Force numbers to be always returned as JavaScript strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range). |

**Example**
```js
Expand Down
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -839,10 +839,6 @@ var cluster = new Redis.Cluster([
});
```

## Improve Performance
ioredis supports two parsers, "hiredis" and "javascript". Refer to https://github.com/luin/ioredis/wiki/Improve-Performance
for details about the differences between them in terms of performance.

<hr>

# Error Handling
Expand Down
30 changes: 4 additions & 26 deletions benchmarks/single_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,23 @@ console.log('node version: ' + process.version);
console.log('current commit: ' + childProcess.execSync('git rev-parse --short HEAD'));
console.log('==========================');

var redisJD, redisJ, redisBD, redisB;
var redisJD, redisJ;
var waitReady = function (next) {
var pending = 4;
var pending = 2;
function check() {
if (!--pending) {
next();
}
}
redisJD = new Redis({ parser: 'javascript', dropBufferSupport: true });
redisJ = new Redis({ parser: 'javascript', dropBufferSupport: false });
redisBD = new Redis({ parser: 'hiredis', dropBufferSupport: true });
redisB = new Redis({ parser: 'hiredis', dropBufferSupport: false });
redisJD.on('ready', check);
redisJ.on('ready', check);
redisBD.on('ready', check);
redisB.on('ready', check);
};

var quit = function () {
redisJD.quit();
redisJ.quit();
redisBD.quit();
redisB.quit();
};

suite('SET foo bar', function () {
Expand All @@ -49,15 +43,7 @@ suite('SET foo bar', function () {
});

bench('javascript parser', function (next) {
redisJ.setBuffer('foo', 'bar', next);
});

bench('hiredis parser + dropBufferSupport: true', function (next) {
redisBD.set('foo', 'bar', next);
});

bench('hiredis parser', function (next) {
redisB.setBuffer('foo', 'bar', next);
redisJ.set('foo', 'bar', next);
});

after(quit);
Expand All @@ -83,15 +69,7 @@ suite('LRANGE foo 0 99', function () {
});

bench('javascript parser', function (next) {
redisJ.lrangeBuffer('foo', 0, 99, next);
});

bench('hiredis parser + dropBufferSupport: true', function (next) {
redisBD.lrange('foo', 0, 99, next);
});

bench('hiredis parser', function (next) {
redisB.lrangeBuffer('foo', 0, 99, next);
redisJ.lrange('foo', 0, 99, next);
});

after(quit);
Expand Down
2 changes: 1 addition & 1 deletion lib/cluster/delay_queue.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

var Deque = require('double-ended-queue');
var Deque = require('denque');
var debug = require('debug')('ioredis:delayqueue');

function DelayQueue() {
Expand Down
3 changes: 2 additions & 1 deletion lib/cluster/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

var Promise = require('bluebird');
var Deque = require('double-ended-queue');
var Deque = require('denque');
var Redis = require('../redis');
var utils = require('../utils');
var util = require('util');
Expand Down Expand Up @@ -179,6 +179,7 @@ Cluster.prototype.connect = function () {

/**
* Called when closed to check whether a reconnection should be made
*
* @private
*/
Cluster.prototype._handleCloseEvent = function () {
Expand Down
11 changes: 3 additions & 8 deletions lib/redis.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var _ = require('lodash');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var Promise = require('bluebird');
var Deque = require('double-ended-queue');
var Deque = require('denque');
var Command = require('./command');
var Commander = require('./commander');
var utils = require('./utils');
Expand Down Expand Up @@ -39,11 +39,9 @@ var commands = require('redis-commands');
* @param {number} [options.db=0] - Database index to use.
* @param {string} [options.password=null] - If set, client will send AUTH command
* with the value of this option when connected.
* @param {string} [options.parser=null] - Either "hiredis" or "javascript". If not set, "hiredis" parser
* will be used if it's installed (`npm install hiredis`), otherwise "javascript" parser will be used.
* @param {boolean} [options.dropBufferSupport=false] - Drop the buffer support for better performance.
* This option is recommended to be enabled when "hiredis" parser is used.
* Refer to https://github.com/luin/ioredis/wiki/Improve-Performance for more details.
* This option is recommended to be enabled when
* handling large array response and you don't need the buffer support.
* @param {boolean} [options.enableReadyCheck=true] - When a connection is established to
* the Redis server, the server might still be loading the database from disk.
* While loading, the server not respond to any commands.
Expand Down Expand Up @@ -84,8 +82,6 @@ var commands = require('redis-commands');
* Only available for cluster mode.
* @param {boolean} [options.stringNumbers=false] - Force numbers to be always returned as JavaScript
* strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range).
* Notice that when this option is enabled, the JavaScript parser will be used even "hiredis" is specified
* because only JavaScript parser supports this feature for the time being.
* @extends [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)
* @extends Commander
* @example
Expand Down Expand Up @@ -174,7 +170,6 @@ Redis.defaultOptions = {
password: null,
db: 0,
// Others
parser: null,
dropBufferSupport: false,
enableOfflineQueue: true,
enableReadyCheck: true,
Expand Down
7 changes: 0 additions & 7 deletions lib/redis/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ exports.initParser = function () {
var _this = this;

this.replyParser = new Parser({
name: this.options.parser,
stringNumbers: this.options.stringNumbers,
returnBuffers: !this.options.dropBufferSupport,
returnError: function (err) {
Expand All @@ -33,12 +32,6 @@ exports.initParser = function () {
_this.disconnect(true);
}
});

if (this.replyParser.name === 'hiredis' && !this.options.dropBufferSupport) {
console.warn('[WARN] ioredis is using hiredis parser, however "dropBufferSupport" is disabled. ' +
'It\'s highly recommended to enable this option. ' +
'Refer to https://github.com/luin/ioredis/wiki/Improve-Performance for more details.');
}
};

exports.returnError = function (err) {
Expand Down
9 changes: 6 additions & 3 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@ exports.convertBufferToString = function (value, encoding) {
return value.toString(encoding);
}
if (Array.isArray(value)) {
var res = [];
for (var i = 0; i < value.length; ++i) {
res[i] = exports.convertBufferToString(value[i], encoding);
var length = value.length;
var res = Array(length);
for (var i = 0; i < length; ++i) {
res[i] = value[i] instanceof Buffer && encoding === 'utf8'
? value[i].toString()
: exports.convertBufferToString(value[i], encoding);
}
return res;
}
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,21 @@
"bluebird": "^3.3.4",
"cluster-key-slot": "^1.0.6",
"debug": "^2.2.0",
"double-ended-queue": "^2.1.0-0",
"denque": "^1.1.0",
"flexbuffer": "0.0.6",
"lodash": "^4.8.2",
"redis-commands": "^1.2.0",
"redis-parser": "^1.3.0"
"redis-parser": "^2.3.0"
},
"devDependencies": {
"chai": "^3.5.0",
"codeclimate-test-reporter": "0.3.1",
"codeclimate-test-reporter": "0.4.0",
"cz-conventional-changelog": "^1.1.5",
"istanbul": "^0.4.2",
"jsdoc": "^3.4.0",
"jsdoc-to-markdown": "^1.3.3",
"jsdoc-to-markdown": "^2.0.0",
"matcha": "^0.7.0",
"mocha": "^2.4.5",
"mocha": "^3.1.1",
"redis": "^2.4.2",
"server-destroy": "^1.0.1",
"sinon": "^1.17.3"
Expand Down
1 change: 0 additions & 1 deletion test/helpers/mock_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ MockServer.prototype.connect = function () {
});

var parser = new Parser({
name: 'javascript',
returnBuffers: true,
returnReply: function (reply) {
reply = utils.convertBufferToString(reply);
Expand Down

0 comments on commit 2820de7

Please sign in to comment.