Comparing version 2.3.1 to 2.4.0
Changelog | ||
========= | ||
## v.2.3.1 - xx Nov, 2015 | ||
## v.2.4.0 - 25 Nov, 2015 | ||
Features | ||
- Added `tls` option to iniate a connection to a redis server behind a TLS proxy. Thanks ([@paddybyers](https://github.com/paddybyers)) | ||
- Added `prefix` option to auto key prefix any command with the provided prefix ([@luin](https://github.com/luin) & [@BridgeAR](https://github.com/BridgeAR)) | ||
- Added `url` option to pass the connection url with the options object ([@BridgeAR](https://github.com/BridgeAR)) | ||
- Added `client.duplicate([options])` to duplicate the current client and return a new one with the same options ([@BridgeAR](https://github.com/BridgeAR)) | ||
- Improve performance by up to 20% on almost all use cases ([@BridgeAR](https://github.com/BridgeAR)) | ||
Bugfixes | ||
- Fixed js parser handling big values slow ([@BridgeAR](https://github.com/BridgeAR)) | ||
- The speed is now on par with the hiredis parser. | ||
## v.2.3.1 - 18 Nov, 2015 | ||
Bugfixes | ||
- Fixed saving buffers with charsets other than utf-8 while using multi ([@BridgeAR](https://github.com/BridgeAR)) | ||
@@ -9,0 +24,0 @@ - Fixed js parser handling big values very slow ([@BridgeAR](https://github.com/BridgeAR)) |
150
index.js
'use strict'; | ||
var net = require('net'); | ||
var tls = require('tls'); | ||
var URL = require('url'); | ||
@@ -11,20 +12,14 @@ var util = require('util'); | ||
var parsers = []; | ||
// This static list of commands is updated from time to time. | ||
// ./lib/commands.js can be updated with generate_commands.js | ||
var commands = require('./lib/commands'); | ||
var commands = require('redis-commands'); | ||
var connection_id = 0; | ||
var default_port = 6379; | ||
var default_host = '127.0.0.1'; | ||
var debug = function(msg) { | ||
if (exports.debug_mode) { | ||
console.error(msg); | ||
} | ||
}; | ||
function noop () {} | ||
function clone (obj) { return JSON.parse(JSON.stringify(obj || {})); } | ||
function debug (msg) { if (exports.debug_mode) { console.error(msg); } } | ||
exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG); | ||
// hiredis might not be installed | ||
// Hiredis might not be installed | ||
try { | ||
@@ -39,7 +34,6 @@ parsers.push(require('./lib/parsers/hiredis')); | ||
function RedisClient(options) { | ||
function RedisClient (options) { | ||
// Copy the options so they are not mutated | ||
options = clone(options); | ||
events.EventEmitter.call(this); | ||
var self = this; | ||
var cnx_options = {}; | ||
@@ -50,8 +44,12 @@ if (options.path) { | ||
} else { | ||
cnx_options.port = options.port || default_port; | ||
cnx_options.port = +options.port || default_port; | ||
cnx_options.host = options.host || default_host; | ||
cnx_options.family = options.family === 'IPv6' ? 6 : 4; | ||
cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4); | ||
this.address = cnx_options.host + ':' + cnx_options.port; | ||
} | ||
this.connection_option = cnx_options; | ||
/* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ | ||
for (var tls_option in options.tls) { // jshint ignore: line | ||
cnx_options[tls_option] = options.tls[tls_option]; | ||
} | ||
this.connection_options = cnx_options; | ||
this.connection_id = ++connection_id; | ||
@@ -96,11 +94,25 @@ this.connected = false; | ||
this.options = options; | ||
self.stream = net.createConnection(cnx_options); | ||
self.install_stream_listeners(); | ||
// Init parser once per instance | ||
this.init_parser(); | ||
this.create_stream(); | ||
} | ||
util.inherits(RedisClient, events.EventEmitter); | ||
RedisClient.prototype.install_stream_listeners = function () { | ||
// Attention: the function name "create_stream" should not be changed, as other libraries need this to mock the stream (e.g. fakeredis) | ||
RedisClient.prototype.create_stream = function () { | ||
var self = this; | ||
// On a reconnect destroy the former stream and retry | ||
if (this.stream) { | ||
this.stream.removeAllListeners(); | ||
this.stream.destroy(); | ||
} | ||
/* istanbul ignore if: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ | ||
if (this.options.tls) { | ||
this.stream = tls.connect(this.connection_options); | ||
} else { | ||
this.stream = net.createConnection(this.connection_options); | ||
} | ||
if (this.options.connect_timeout) { | ||
@@ -113,3 +125,5 @@ this.stream.setTimeout(this.connect_timeout, function () { | ||
this.stream.once('connect', function () { | ||
/* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ | ||
var connect_event = this.options.tls ? "secureConnect" : "connect"; | ||
this.stream.once(connect_event, function () { | ||
this.removeAllListeners("timeout"); | ||
@@ -129,2 +143,7 @@ self.on_connect(); | ||
/* istanbul ignore next: travis does not work with stunnel atm. Therefor the tls tests are skipped on travis */ | ||
this.stream.on('clientError', function (err) { | ||
self.on_error(err); | ||
}); | ||
this.stream.once('close', function () { | ||
@@ -146,2 +165,13 @@ self.connection_gone('close'); | ||
RedisClient.prototype.duplicate = function (options) { | ||
var existing_options = clone(this.options); | ||
options = clone(options); | ||
for (var elem in options) { // jshint ignore: line | ||
existing_options[elem] = options[elem]; | ||
} | ||
var client = new RedisClient(existing_options); | ||
client.selected_db = this.selected_db; | ||
return client; | ||
}; | ||
RedisClient.prototype.initialize_retry_vars = function () { | ||
@@ -167,3 +197,3 @@ this.retry_timer = null; | ||
// flush provided queues, erroring any items with a callback first | ||
// Flush provided queues, erroring any items with a callback first | ||
RedisClient.prototype.flush_and_error = function (error, queue_names) { | ||
@@ -267,4 +297,2 @@ var command_obj; | ||
this.init_parser(); | ||
if (typeof this.auth_pass === 'string') { | ||
@@ -306,14 +334,10 @@ this.do_auth(); | ||
// converts to Strings if the input arguments are not Buffers. | ||
this.reply_parser = new this.parser_module.Parser(self.options.return_buffers || self.options.detect_buffers || false); | ||
this.reply_parser = new this.parser_module.Parser(self.options.return_buffers || self.options.detect_buffers); | ||
// Important: Only send results / errors async. | ||
// That way the result / error won't stay in a try catch block and catch user things | ||
this.reply_parser.send_error = function (data) { | ||
process.nextTick(function() { | ||
self.return_error(data); | ||
}); | ||
self.return_error(data); | ||
}; | ||
this.reply_parser.send_reply = function (data) { | ||
process.nextTick(function() { | ||
self.return_reply(data); | ||
}); | ||
self.return_reply(data); | ||
}; | ||
@@ -444,3 +468,3 @@ }; | ||
// expose info key/vals to users | ||
// Expose info key/vals to users | ||
this.server_info = obj; | ||
@@ -498,6 +522,3 @@ | ||
self.retry_delay = Math.round(self.retry_delay * self.retry_backoff); | ||
self.stream = net.createConnection(self.connection_option); | ||
self.install_stream_listeners(); | ||
self.create_stream(); | ||
self.retry_timer = null; | ||
@@ -676,3 +697,3 @@ }; | ||
} | ||
// subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback | ||
// Subscribe commands take an optional callback and also emit an event, but only the first response is included in the callback | ||
// TODO - document this or fix it so it works in a more obvious way | ||
@@ -716,2 +737,3 @@ if (command_obj && typeof command_obj.callback === 'function') { | ||
big_data = false, | ||
prefix_keys, | ||
buffer = this.options.return_buffers; | ||
@@ -804,3 +826,10 @@ | ||
} | ||
if (this.options.prefix) { | ||
prefix_keys = commands.getKeyIndexes(command, args); | ||
i = prefix_keys.pop(); | ||
while (i !== undefined) { | ||
args[i] = this.options.prefix + args[i]; | ||
i = prefix_keys.pop(); | ||
} | ||
} | ||
// Always use 'Multi bulk commands', but if passed any Buffer args, then do multiple writes, one for each arg. | ||
@@ -938,20 +967,4 @@ // This means that using Buffers in commands is going to be slower, so use Strings if you don't already have a Buffer. | ||
RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { | ||
var multi = new Multi(this, args); | ||
multi.exec = multi.EXEC = multi.exec_transaction; | ||
return multi; | ||
}; | ||
commands.list.forEach(function (command) { | ||
RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) { | ||
return new Multi(this, args); | ||
}; | ||
commands.forEach(function (fullCommand) { | ||
var command = fullCommand.split(' ')[0]; | ||
// Skip all full commands that have already been added instead of overwriting them over and over again | ||
if (RedisClient.prototype[command]) { | ||
return; | ||
} | ||
RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command] = function (key, arg, callback) { | ||
@@ -1002,3 +1015,13 @@ if (Array.isArray(key)) { | ||
// store db in this.select_db to restore it on reconnect | ||
RedisClient.prototype.multi = RedisClient.prototype.MULTI = function (args) { | ||
var multi = new Multi(this, args); | ||
multi.exec = multi.EXEC = multi.exec_transaction; | ||
return multi; | ||
}; | ||
RedisClient.prototype.batch = RedisClient.prototype.BATCH = function (args) { | ||
return new Multi(this, args); | ||
}; | ||
// Store db in this.select_db to restore it on reconnect | ||
RedisClient.prototype.select = RedisClient.prototype.SELECT = function (db, callback) { | ||
@@ -1135,3 +1158,3 @@ var self = this; | ||
this.send_command('multi', []); | ||
// drain queue, callback will catch 'QUEUED' or error | ||
// Drain queue, callback will catch 'QUEUED' or error | ||
for (var index = 0; index < len; index++) { | ||
@@ -1245,2 +1268,3 @@ var args = this.queue.get(index).slice(0); | ||
if (callback) { | ||
// The execution order won't be obtained in this case | ||
setImmediate(function () { | ||
@@ -1280,11 +1304,9 @@ callback(null, []); | ||
var createClient = function (port_arg, host_arg, options) { | ||
if (typeof port_arg === 'object' || port_arg === undefined) { | ||
options = port_arg || options || {}; | ||
} else if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { | ||
if (typeof port_arg === 'number' || typeof port_arg === 'string' && /^\d+$/.test(port_arg)) { | ||
options = clone(options); | ||
options.host = host_arg; | ||
options.port = port_arg; | ||
} else if (typeof port_arg === 'string') { | ||
options = clone(host_arg || options); | ||
var parsed = URL.parse(port_arg, true, true); | ||
} else if (typeof port_arg === 'string' || port_arg && port_arg.url) { | ||
options = clone(port_arg.url ? port_arg : host_arg || options); | ||
var parsed = URL.parse(port_arg.url || port_arg, true, true); | ||
if (parsed.hostname) { | ||
@@ -1294,2 +1316,5 @@ if (parsed.auth) { | ||
} | ||
if (parsed.protocol !== 'redis:') { | ||
throw new Error('Connection string must use the "redis:" protocol'); | ||
} | ||
options.host = parsed.hostname; | ||
@@ -1300,2 +1325,5 @@ options.port = parsed.port; | ||
} | ||
} else if (typeof port_arg === 'object' || port_arg === undefined) { | ||
options = clone(port_arg || options); | ||
options.host = options.host || host_arg; | ||
} | ||
@@ -1302,0 +1330,0 @@ if (!options) { |
@@ -7,29 +7,23 @@ 'use strict'; | ||
this.name = exports.name; | ||
this.return_buffers = return_buffers; | ||
this.reset(); | ||
this.reader = new hiredis.Reader({ | ||
return_buffers: return_buffers | ||
}); | ||
} | ||
HiredisReplyParser.prototype.reset = function () { | ||
this.reader = new hiredis.Reader({ | ||
return_buffers: this.return_buffers || false | ||
}); | ||
HiredisReplyParser.prototype.return_data = function () { | ||
try { | ||
return this.reader.get(); | ||
} catch (err) { | ||
// Protocol errors land here | ||
this.send_error(err); | ||
return void 0; | ||
} | ||
}; | ||
HiredisReplyParser.prototype.execute = function (data) { | ||
var reply; | ||
this.reader.feed(data); | ||
while (true) { | ||
try { | ||
reply = this.reader.get(); | ||
} catch (err) { | ||
// Protocol errors land here | ||
this.send_error(err); | ||
break; | ||
} | ||
var reply = this.return_data(); | ||
if (reply === undefined) { | ||
break; | ||
} | ||
if (reply && reply.constructor === Error) { | ||
while (reply !== undefined) { | ||
if (reply && reply.name === 'Error') { | ||
this.send_error(reply); | ||
@@ -39,2 +33,3 @@ } else { | ||
} | ||
reply = this.return_data(); | ||
} | ||
@@ -41,0 +36,0 @@ }; |
@@ -9,3 +9,7 @@ 'use strict'; | ||
this._offset = 0; | ||
this._big_offset = 0; | ||
this._chunks_size = 0; | ||
this._buffers = []; | ||
this._type = 0; | ||
this._protocol_error = false; | ||
} | ||
@@ -23,10 +27,11 @@ | ||
offset = 0, | ||
packetHeader = 0; | ||
packetHeader = 0, | ||
res, | ||
reply; | ||
if (type === 43 || type === 58 || type === 45) { // + or : or - | ||
// up to the delimiter | ||
// Up to the delimiter | ||
end = this._packetEndOffset(); | ||
start = this._offset; | ||
// include the delimiter | ||
// Include the delimiter | ||
this._offset = end + 2; | ||
@@ -37,3 +42,3 @@ | ||
} else if (type === 58) { | ||
// return the coerced numeric value | ||
// Return the coerced numeric value | ||
return +this._buffer.toString('ascii', start, end); | ||
@@ -43,21 +48,17 @@ } | ||
} else if (type === 36) { // $ | ||
// set a rewind point, as the packet could be larger than the | ||
// buffer in memory | ||
offset = this._offset - 1; | ||
packetHeader = this.parseHeader(); | ||
// packets with a size of -1 are considered null | ||
// Packets with a size of -1 are considered null | ||
if (packetHeader === -1) { | ||
return null; | ||
} | ||
end = this._offset + packetHeader; | ||
start = this._offset; | ||
if (end > this._buffer.length) { | ||
if (end + 2 > this._buffer.length) { | ||
this._chunks_size = this._buffer.length - this._offset - 2; | ||
this._big_offset = packetHeader; | ||
throw new IncompleteReadBuffer('Wait for more data.'); | ||
} | ||
// set the offset to after the delimiter | ||
// Set the offset to after the delimiter | ||
this._offset = end + 2; | ||
@@ -67,2 +68,3 @@ | ||
} else if (type === 42) { // * | ||
// Set a rewind point, as the packet is larger than the buffer in memory | ||
offset = this._offset; | ||
@@ -74,26 +76,15 @@ packetHeader = this.parseHeader(); | ||
} | ||
if (packetHeader > this._buffer.length - this._offset) { | ||
this._offset = offset - 1; | ||
throw new IncompleteReadBuffer('Wait for more data.'); | ||
} | ||
var reply = []; | ||
var ntype, i, res; | ||
reply = []; | ||
offset = this._offset - 1; | ||
for (i = 0; i < packetHeader; i++) { | ||
ntype = this._buffer[this._offset++]; | ||
if (this._offset > this._buffer.length) { | ||
for (var i = 0; i < packetHeader; i++) { | ||
if (this._offset >= this._buffer.length) { | ||
throw new IncompleteReadBuffer('Wait for more data.'); | ||
} | ||
res = this._parseResult(ntype); | ||
res = this._parseResult(this._buffer[this._offset++]); | ||
reply.push(res); | ||
} | ||
return reply; | ||
} else { | ||
return null; | ||
return void 0; | ||
} | ||
@@ -103,12 +94,7 @@ }; | ||
JavascriptReplyParser.prototype.execute = function (buffer) { | ||
var i = buffer.length - 1; | ||
while (buffer[i] !== 0x0a) { | ||
i--; | ||
if (i < 1) { | ||
this._buffers.push(buffer); | ||
return; | ||
} | ||
if (this._chunks_size !== 0 && this._big_offset > this._chunks_size + buffer.length) { | ||
this._buffers.push(buffer); | ||
this._chunks_size += buffer.length; | ||
return; | ||
} | ||
if (this._buffers.length !== 0) { | ||
@@ -119,2 +105,4 @@ this._buffers.unshift(this._offset === 0 ? this._buffer : this._buffer.slice(this._offset)); | ||
this._buffers = []; | ||
this._big_offset = 0; | ||
this._chunks_size = 0; | ||
} else if (this._offset >= this._buffer.length) { | ||
@@ -126,39 +114,38 @@ this._buffer = buffer; | ||
this._offset = 0; | ||
this._protocol_error = true; | ||
this.run(); | ||
}; | ||
JavascriptReplyParser.prototype.try_parsing = function () { | ||
// Set a rewind point. If a failure occurs, wait for the next execute()/append() and try again | ||
var offset = this._offset - 1; | ||
try { | ||
return this._parseResult(this._type); | ||
} catch (err) { | ||
// Catch the error (not enough data), rewind if it's an array, | ||
// and wait for the next packet to appear | ||
this._offset = offset; | ||
this._protocol_error = false; | ||
return void 0; | ||
} | ||
}; | ||
JavascriptReplyParser.prototype.run = function (buffer) { | ||
var type, offset = this._offset; | ||
this._type = this._buffer[this._offset++]; | ||
var reply = this.try_parsing(); | ||
while (true) { | ||
offset = this._offset; | ||
// at least 4 bytes: :1\r\n | ||
if (this._buffer.length - this._offset < 4) { | ||
break; | ||
while (reply !== undefined) { | ||
if (this._type === 45) { // Errors - | ||
this.send_error(reply); | ||
} else { | ||
this.send_reply(reply); // Strings + // Integers : // Bulk strings $ // Arrays * | ||
} | ||
try { | ||
type = this._buffer[this._offset++]; | ||
if (type === 43 || type === 58 || type === 36) { // Strings + // Integers : // Bulk strings $ | ||
this.send_reply(this._parseResult(type)); | ||
} else if (type === 45) { // Errors - | ||
this.send_error(this._parseResult(type)); | ||
} else if (type === 42) { // Arrays * | ||
// set a rewind point. if a failure occurs, | ||
// wait for the next execute()/append() and try again | ||
offset = this._offset - 1; | ||
this.send_reply(this._parseResult(type)); | ||
} else if (type !== 10 && type !== 13) { | ||
var err = new Error('Protocol error, got "' + String.fromCharCode(type) + '" as reply type byte'); | ||
this.send_error(err); | ||
} | ||
} catch (err) { | ||
// catch the error (not enough data), rewind, and wait | ||
// for the next packet to appear | ||
this._offset = offset; | ||
break; | ||
} | ||
this._type = this._buffer[this._offset++]; | ||
reply = this.try_parsing(); | ||
} | ||
if (this._type !== undefined && this._protocol_error === true) { | ||
// Reset the buffer so the parser can handle following commands properly | ||
this._buffer = new Buffer(0); | ||
this.send_error(new Error('Protocol error, got "' + String.fromCharCode(this._type) + '" as reply type byte')); | ||
} | ||
}; | ||
@@ -171,3 +158,2 @@ | ||
this._offset = end + 2; | ||
return value; | ||
@@ -177,3 +163,4 @@ }; | ||
JavascriptReplyParser.prototype._packetEndOffset = function () { | ||
var offset = this._offset; | ||
var offset = this._offset, | ||
len = this._buffer.length - 1; | ||
@@ -183,8 +170,6 @@ while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { | ||
/* istanbul ignore if: activate the js parser out of memory test to test this */ | ||
if (offset >= this._buffer.length) { | ||
if (offset >= len) { | ||
throw new IncompleteReadBuffer('Did not see LF after NL reading multi bulk count (' + offset + ' => ' + this._buffer.length + ', ' + this._offset + ')'); | ||
} | ||
} | ||
return offset; | ||
@@ -191,0 +176,0 @@ }; |
{ | ||
"name": "redis", | ||
"version": "2.3.1", | ||
"version": "2.4.0", | ||
"description": "Redis client library", | ||
@@ -9,3 +9,5 @@ "keywords": [ | ||
"transaction", | ||
"pipelining" | ||
"pipelining", | ||
"performance", | ||
"queue" | ||
], | ||
@@ -24,3 +26,4 @@ "author": "Matt Ranney <mjr@ranney.com>", | ||
"dependencies": { | ||
"double-ended-queue": "^2.1.0-0" | ||
"double-ended-queue": "^2.1.0-0", | ||
"redis-commands": "^1.0.1" | ||
}, | ||
@@ -27,0 +30,0 @@ "engines": { |
@@ -9,3 +9,3 @@ redis - a node.js redis client | ||
This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on performance. | ||
This is a complete and feature rich Redis client for node.js. It supports all Redis commands and focuses on high performance. | ||
@@ -184,2 +184,3 @@ Install with: | ||
* `path`: *null*; The unix socket string to connect to | ||
* `url`: *null*; The redis url to connect to | ||
* `parser`: *hiredis*; Which Redis protocol reply parser to use. If `hiredis` is not installed it will fallback to `javascript`. | ||
@@ -216,2 +217,5 @@ * `return_buffers`: *false*; If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. | ||
* `rename_commands`: *null*; pass a object with renamed commands to use those instead of the original functions. See the [redis security topics](http://redis.io/topics/security) for more info. | ||
* `tls`: an object containing options to pass to [tls.connect](http://nodejs.org/api/tls.html#tls_tls_connect_port_host_options_callback), | ||
to set up a TLS connection to Redis (if, for example, it is set up to be accessible via a tunnel). | ||
* `prefix`: *null*; pass a string to prefix all used keys with that string as prefix e.g. 'namespace:test' | ||
@@ -252,3 +256,3 @@ ```js | ||
If flush is set to true, all commands will be rejected instead of ignored after using `.end`. | ||
If flush is set to true, all still running commands will be rejected instead of ignored after using `.end`. | ||
@@ -270,4 +274,3 @@ This example closes the connection to the Redis server before the replies have been read. You probably don't | ||
`client.end()` is useful for timeout cases where something is stuck or taking too long and you want | ||
to start over. | ||
`client.end()` without the flush parameter should not be used in production! | ||
@@ -477,8 +480,2 @@ ## client.unref() | ||
}); | ||
// you can re-run the same transaction if you like | ||
multi.exec(function (err, replies) { | ||
console.log(replies); // 102, 3 | ||
client.quit(); | ||
}); | ||
``` | ||
@@ -587,2 +584,6 @@ | ||
## client.duplicate([options]) | ||
Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option. | ||
## client.send_command(command_name[, [args][, callback]]) | ||
@@ -661,41 +662,41 @@ | ||
``` | ||
Client count: 1, node version: 4.2.1, server version: 3.0.3, parser: hiredis | ||
PING, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 10001ms total, 38850.41 ops/sec | ||
PING, batch 50/1 min/max/avg/p95: 0/ 3/ 0.10/ 1.00 10001ms total, 488376.16 ops/sec | ||
SET 4B str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 10001ms total, 35782.02 ops/sec | ||
SET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 10001ms total, 349740.03 ops/sec | ||
SET 4B buf, 1/1 min/max/avg/p95: 0/ 5/ 0.04/ 0.00 10001ms total, 23497.75 ops/sec | ||
SET 4B buf, batch 50/1 min/max/avg/p95: 0/ 3/ 0.28/ 1.00 10001ms total, 177087.29 ops/sec | ||
GET 4B str, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 37044.10 ops/sec | ||
GET 4B str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.12/ 1.00 10001ms total, 421987.80 ops/sec | ||
GET 4B buf, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 35608.24 ops/sec | ||
GET 4B buf, batch 50/1 min/max/avg/p95: 0/ 3/ 0.12/ 1.00 10001ms total, 416593.34 ops/sec | ||
SET 4KiB str, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 30014.10 ops/sec | ||
SET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.34/ 1.00 10001ms total, 147705.23 ops/sec | ||
SET 4KiB buf, 1/1 min/max/avg/p95: 0/ 4/ 0.04/ 0.00 10001ms total, 23803.52 ops/sec | ||
SET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 4/ 0.37/ 1.00 10001ms total, 132611.74 ops/sec | ||
GET 4KiB str, 1/1 min/max/avg/p95: 0/ 5/ 0.03/ 0.00 10001ms total, 34216.98 ops/sec | ||
GET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 4/ 0.32/ 1.00 10001ms total, 153039.70 ops/sec | ||
GET 4KiB buf, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 10001ms total, 34169.18 ops/sec | ||
GET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 10001ms total, 153264.67 ops/sec | ||
INCR, 1/1 min/max/avg/p95: 0/ 3/ 0.03/ 0.00 10001ms total, 36307.17 ops/sec | ||
INCR, batch 50/1 min/max/avg/p95: 0/ 4/ 0.12/ 1.00 10001ms total, 412438.76 ops/sec | ||
LPUSH, 1/1 min/max/avg/p95: 0/ 4/ 0.03/ 0.00 10001ms total, 36073.89 ops/sec | ||
LPUSH, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 10001ms total, 355954.40 ops/sec | ||
LRANGE 10, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 10001ms total, 30395.66 ops/sec | ||
LRANGE 10, batch 50/1 min/max/avg/p95: 0/ 3/ 0.33/ 1.00 10001ms total, 149400.06 ops/sec | ||
LRANGE 100, 1/1 min/max/avg/p95: 0/ 2/ 0.06/ 1.00 10001ms total, 16814.62 ops/sec | ||
LRANGE 100, batch 50/1 min/max/avg/p95: 1/ 4/ 2.01/ 2.00 10002ms total, 24790.04 ops/sec | ||
SET 4MiB str, 1/1 min/max/avg/p95: 1/ 7/ 2.01/ 2.00 10002ms total, 496.90 ops/sec | ||
SET 4MiB str, batch 20/1 min/max/avg/p95: 100/ 135/ 109.58/ 125.00 10085ms total, 182.45 ops/sec | ||
SET 4MiB buf, 1/1 min/max/avg/p95: 1/ 5/ 1.87/ 2.00 10001ms total, 531.75 ops/sec | ||
SET 4MiB buf, batch 20/1 min/max/avg/p95: 52/ 77/ 58.90/ 68.45 10016ms total, 339.46 ops/sec | ||
GET 4MiB str, 1/1 min/max/avg/p95: 3/ 19/ 5.79/ 11.00 10005ms total, 172.51 ops/sec | ||
GET 4MiB str, batch 20/1 min/max/avg/p95: 73/ 112/ 89.89/ 107.00 10072ms total, 222.40 ops/sec | ||
GET 4MiB buf, 1/1 min/max/avg/p95: 3/ 13/ 5.35/ 9.00 10002ms total, 186.76 ops/sec | ||
GET 4MiB buf, batch 20/1 min/max/avg/p95: 76/ 106/ 85.37/ 98.00 10077ms total, 234.20 ops/sec | ||
Client count: 1, node version: 4.2.2, server version: 3.0.3, parser: hiredis | ||
PING, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 47503.80 ops/sec | ||
PING, batch 50/1 min/max/avg/p95: 0/ 2/ 0.09/ 1.00 2501ms total, 529668.13 ops/sec | ||
SET 4B str, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 41900.04 ops/sec | ||
SET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.14/ 1.00 2501ms total, 354658.14 ops/sec | ||
SET 4B buf, 1/1 min/max/avg/p95: 0/ 4/ 0.04/ 0.00 2501ms total, 23499.00 ops/sec | ||
SET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.31/ 1.00 2501ms total, 159836.07 ops/sec | ||
GET 4B str, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 2501ms total, 43489.80 ops/sec | ||
GET 4B str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.11/ 1.00 2501ms total, 444202.32 ops/sec | ||
GET 4B buf, 1/1 min/max/avg/p95: 0/ 3/ 0.02/ 0.00 2501ms total, 38561.38 ops/sec | ||
GET 4B buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.11/ 1.00 2501ms total, 452139.14 ops/sec | ||
SET 4KiB str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 32990.80 ops/sec | ||
SET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.34/ 1.00 2501ms total, 146161.54 ops/sec | ||
SET 4KiB buf, 1/1 min/max/avg/p95: 0/ 1/ 0.04/ 0.00 2501ms total, 23294.28 ops/sec | ||
SET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.36/ 1.00 2501ms total, 137584.97 ops/sec | ||
GET 4KiB str, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 36350.66 ops/sec | ||
GET 4KiB str, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 155157.94 ops/sec | ||
GET 4KiB buf, 1/1 min/max/avg/p95: 0/ 4/ 0.02/ 0.00 2501ms total, 39776.49 ops/sec | ||
GET 4KiB buf, batch 50/1 min/max/avg/p95: 0/ 2/ 0.32/ 1.00 2501ms total, 155457.82 ops/sec | ||
INCR, 1/1 min/max/avg/p95: 0/ 3/ 0.02/ 0.00 2501ms total, 43972.41 ops/sec | ||
INCR, batch 50/1 min/max/avg/p95: 0/ 1/ 0.12/ 1.00 2501ms total, 425809.68 ops/sec | ||
LPUSH, 1/1 min/max/avg/p95: 0/ 2/ 0.02/ 0.00 2501ms total, 38998.40 ops/sec | ||
LPUSH, batch 50/1 min/max/avg/p95: 0/ 4/ 0.14/ 1.00 2501ms total, 365013.99 ops/sec | ||
LRANGE 10, 1/1 min/max/avg/p95: 0/ 2/ 0.03/ 0.00 2501ms total, 31879.25 ops/sec | ||
LRANGE 10, batch 50/1 min/max/avg/p95: 0/ 1/ 0.32/ 1.00 2501ms total, 153698.52 ops/sec | ||
LRANGE 100, 1/1 min/max/avg/p95: 0/ 4/ 0.06/ 0.00 2501ms total, 16676.13 ops/sec | ||
LRANGE 100, batch 50/1 min/max/avg/p95: 1/ 6/ 2.03/ 2.00 2502ms total, 24520.38 ops/sec | ||
SET 4MiB str, 1/1 min/max/avg/p95: 1/ 6/ 2.11/ 3.00 2502ms total, 472.82 ops/sec | ||
SET 4MiB str, batch 20/1 min/max/avg/p95: 85/ 112/ 94.93/ 109.60 2563ms total, 210.69 ops/sec | ||
SET 4MiB buf, 1/1 min/max/avg/p95: 1/ 8/ 2.02/ 3.00 2502ms total, 490.01 ops/sec | ||
SET 4MiB buf, batch 20/1 min/max/avg/p95: 37/ 52/ 39.48/ 46.75 2528ms total, 506.33 ops/sec | ||
GET 4MiB str, 1/1 min/max/avg/p95: 3/ 13/ 5.26/ 9.00 2504ms total, 190.10 ops/sec | ||
GET 4MiB str, batch 20/1 min/max/avg/p95: 70/ 106/ 89.36/ 103.75 2503ms total, 223.73 ops/sec | ||
GET 4MiB buf, 1/1 min/max/avg/p95: 3/ 11/ 5.04/ 8.15 2502ms total, 198.24 ops/sec | ||
GET 4MiB buf, batch 20/1 min/max/avg/p95: 70/ 105/ 88.07/ 103.00 2554ms total, 227.09 ops/sec | ||
``` | ||
The hiredis and js parser should most of the time be on the same level. The js parser lacks speed for large responses though. | ||
Therefor the hiredis parser is the default used in node_redis and we recommend using the hiredis parser. To use `hiredis`, do: | ||
The hiredis and js parser should most of the time be on the same level. But if you use Redis for big SUNION/SINTER/LRANGE/ZRANGE hiredis is significantly faster. | ||
Therefor the hiredis parser is the default used in node_redis. To use `hiredis`, do: | ||
@@ -702,0 +703,0 @@ npm install hiredis redis |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2091649
726
2
48
1884
+ Addedredis-commands@^1.0.1
+ Addedredis-commands@1.7.0(transitive)