Skip to content

Commit 0b8090b

Browse files
committed
v.2.6.0-2 pre-release
2 parents c2c57de + 03f1a60 commit 0b8090b

36 files changed

+1914
-616
lines changed

.github/ISSUE_TEMPLATE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ to a small test case, but it's highly appreciated to have as much data as possib
99
Thank you!_
1010

1111
* **Version**: What node_redis and what redis version is the issue happening on?
12-
* **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0)
12+
* **Platform**: What platform / version? (For example Node.js 0.10 or Node.js 5.7.0 on Windows 7 / Ubuntu 15.10 / Azure)
1313
* **Description**: Description of your issue, stack traces from errors and code that reproduces the issue
1414

1515
[gitter]: https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge

README.md

+68-24
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ Install with:
1212

1313
npm install redis
1414

15-
## Usage
16-
17-
Simple example, included as `examples/simple.js`:
15+
## Usage Example
1816

1917
```js
2018
var redis = require("redis"),
@@ -51,6 +49,8 @@ This will display:
5149
mjr:~/work/node_redis (master)$
5250

5351
Note that the API is entirely asynchronous. To get data back from the server, you'll need to use a callback.
52+
From v.2.6 on the API supports camelCase and snack_case and all options / variables / events etc. can be used either way.
53+
It is recommended to use camelCase as this is the default for the Node.js landscape.
5454

5555
### Promises
5656

@@ -83,7 +83,7 @@ return client.multi().get('foo').execAsync().then(function(res) {
8383
Each Redis command is exposed as a function on the `client` object.
8484
All functions take either an `args` Array plus optional `callback` Function or
8585
a variable number of individual arguments followed by an optional callback.
86-
Here are examples how to use the api:
86+
Examples:
8787

8888
```js
8989
client.hmset(["key", "test keys 1", "test val 1", "test keys 2", "test val 2"], function (err, res) {});
@@ -111,8 +111,6 @@ client.get("missingkey", function(err, reply) {
111111

112112
For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands)
113113

114-
The commands can be specified in uppercase or lowercase for convenience. `client.get()` is the same as `client.GET()`.
115-
116114
Minimal parsing is done on the replies. Commands that return a integer return JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object keyed by the hash keys. All strings will either be returned as string or as buffer depending on your setting.
117115
Please be aware that sending null, undefined and Boolean values will result in the value coerced to a string!
118116

@@ -139,6 +137,7 @@ are passed an object containing `delay` (in ms) and `attempt` (the attempt #) at
139137
### "error"
140138

141139
`client` will emit `error` when encountering an error connecting to the Redis server or when any other in node_redis occurs.
140+
If you use a command without callback and encounter a ReplyError it is going to be emitted to the error listener.
142141

143142
So please attach the error listener to node_redis.
144143

@@ -182,7 +181,7 @@ __Tip:__ If the Redis server runs on the same machine as the client consider usi
182181
| host | 127.0.0.1 | IP address of the Redis server |
183182
| port | 6379 | Port of the Redis server |
184183
| path | null | The UNIX socket string of the Redis server |
185-
| url | null | The URL of the Redis server. Format: `[redis:]//[user][:password@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). |
184+
| url | null | The URL of the Redis server. Format: `[redis:]//[[user][:password@]][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]]` (More info avaliable at [IANA](http://www.iana.org/assignments/uri-schemes/prov/redis)). |
186185
| parser | hiredis | If hiredis is not installed, automatic fallback to the built-in javascript parser |
187186
| string_numbers | null | Set to `true`, `node_redis` will return Redis number values as Strings instead of javascript Numbers. Useful if you need to handle big numbers (above `Number.MAX_SAFE_INTEGER === 2^53`). Hiredis is incapable of this behavior, so setting this option to `true` will result in the built-in javascript parser being used no matter the value of the `parser` option. |
188187
| return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. |
@@ -295,6 +294,51 @@ client.get("foo_rand000000000000", function (err, reply) {
295294

296295
`client.end()` without the flush parameter set to true should NOT be used in production!
297296

297+
## Error handling (>= v.2.6)
298+
299+
All redis errors are returned as `ReplyError`.
300+
All unresolved commands that get rejected due to what ever reason return a `AbortError`.
301+
As subclass of the `AbortError` a `AggregateError` exists. This is emitted in case multiple unresolved commands without callback got rejected in debug_mode.
302+
They are all aggregated and a single error is emitted in that case.
303+
304+
Example:
305+
```js
306+
var redis = require('./');
307+
var assert = require('assert');
308+
var client = redis.createClient();
309+
310+
client.on('error', function (err) {
311+
assert(err instanceof Error);
312+
assert(err instanceof redis.AbortError);
313+
assert(err instanceof redis.AggregateError);
314+
assert.strictEqual(err.errors.length, 2); // The set and get got aggregated in here
315+
assert.strictEqual(err.code, 'NR_CLOSED');
316+
});
317+
client.set('foo', 123, 'bar', function (err, res) { // To many arguments
318+
assert(err instanceof redis.ReplyError); // => true
319+
assert.strictEqual(err.command, 'SET');
320+
assert.deepStrictEqual(err.args, ['foo', 123, 'bar']);
321+
322+
redis.debug_mode = true;
323+
client.set('foo', 'bar');
324+
client.get('foo');
325+
process.nextTick(function () {
326+
client.end(true); // Force closing the connection while the command did not yet return
327+
redis.debug_mode = false;
328+
});
329+
});
330+
331+
```
332+
333+
Every `ReplyError` contains the `command` name in all-caps and the arguments (`args`).
334+
335+
If node_redis emits a library error because of another error, the triggering error is added to the returned error as `origin` attribute.
336+
337+
___Error codes___
338+
339+
node_redis returns a `NR_CLOSED` error code if the clients connection dropped. If a command unresolved command got rejected a `UNERCTAIN_STATE` code is returned.
340+
A `CONNECTION_BROKEN` error code is used in case node_redis gives up to reconnect.
341+
298342
## client.unref()
299343

300344
Call `unref()` on the underlying socket connection to the Redis server, allowing the program to exit once no more commands are pending.
@@ -363,7 +407,7 @@ client.HMSET(key1, "0123456789", "abcdefghij", "some manner of key", "a type of
363407

364408
## Publish / Subscribe
365409

366-
Here is a simple example of the API for publish / subscribe. This program opens two
410+
Example of the publish / subscribe API. This program opens two
367411
client connections, subscribes to a channel on one of them, and publishes to that
368412
channel on the other:
369413

@@ -412,6 +456,16 @@ Client will emit `pmessage` for every message received that matches an active su
412456
Listeners are passed the original pattern used with `PSUBSCRIBE` as `pattern`, the sending channel
413457
name as `channel`, and the message as `message`.
414458

459+
### "message_buffer" (channel, message)
460+
461+
This is the same as the `message` event with the exception, that it is always going to emit a buffer.
462+
If you listen to the `message` event at the same time as the `message_buffer`, it is always going to emit a string.
463+
464+
### "pmessage_buffer" (pattern, channel, message)
465+
466+
This is the same as the `pmessage` event with the exception, that it is always going to emit a buffer.
467+
If you listen to the `pmessage` event at the same time as the `pmessage_buffer`, it is always going to emit a string.
468+
415469
### "subscribe" (channel, count)
416470

417471
Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are passed the
@@ -529,7 +583,7 @@ Redis. The interface in `node_redis` is to return an individual `Batch` object b
529583
The only difference between .batch and .multi is that no transaction is going to be used.
530584
Be aware that the errors are - just like in multi statements - in the result. Otherwise both, errors and results could be returned at the same time.
531585

532-
If you fire many commands at once this is going to **boost the execution speed by up to 400%** [sic!] compared to fireing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired.
586+
If you fire many commands at once this is going to boost the execution speed significantly compared to fireing the same commands in a loop without waiting for the result! See the benchmarks for further comparison. Please remember that all commands are kept in memory until they are fired.
533587

534588
## Monitor mode
535589

@@ -539,7 +593,7 @@ across all client connections, including from other client libraries and other c
539593
A `monitor` event is going to be emitted for every command fired from any client connected to the server including the monitoring client itself.
540594
The callback for the `monitor` event takes a timestamp from the Redis server, an array of command arguments and the raw monitoring string.
541595

542-
Here is a simple example:
596+
Example:
543597

544598
```js
545599
var client = require("redis").createClient();
@@ -599,9 +653,10 @@ the second word as first parameter:
599653
client.multi().script('load', 'return 1').exec(...);
600654
client.multi([['script', 'load', 'return 1']]).exec(...);
601655

602-
## client.duplicate([options])
656+
## client.duplicate([options][, callback])
603657

604658
Duplicate all current options and return a new redisClient instance. All options passed to the duplicate function are going to replace the original option.
659+
If you pass a callback, duplicate is going to wait until the client is ready and returns it in the callback. If an error occurs in the meanwhile, that is going to return an error instead in the callback.
605660

606661
## client.send_command(command_name[, [args][, callback]])
607662

@@ -615,27 +670,16 @@ All commands are sent as multi-bulk commands. `args` can either be an Array of a
615670

616671
Boolean tracking the state of the connection to the Redis server.
617672

618-
## client.command_queue.length
673+
## client.command_queue_length
619674

620675
The number of commands that have been sent to the Redis server but not yet replied to. You can use this to
621676
enforce some kind of maximum queue depth for commands while connected.
622677

623-
Don't mess with `client.command_queue` though unless you really know what you are doing.
624-
625-
## client.offline_queue.length
678+
## client.offline_queue_length
626679

627680
The number of commands that have been queued up for a future connection. You can use this to enforce
628681
some kind of maximum queue depth for pre-connection commands.
629682

630-
## client.retry_delay
631-
632-
Current delay in milliseconds before a connection retry will be attempted. This starts at `200`.
633-
634-
## client.retry_backoff
635-
636-
Multiplier for future retry timeouts. This should be larger than 1 to add more time between retries.
637-
Defaults to 1.7. The default initial connection retry is 200, so the second retry will be 340, followed by 578, etc.
638-
639683
### Commands with Optional and Keyword arguments
640684

641685
This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation.

benchmarks/multi_bench.js

+22-20
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function returnArg (name, def) {
2323
}
2424
var num_clients = returnArg('clients', 1);
2525
var run_time = returnArg('time', 2500); // ms
26+
var pipeline = returnArg('pipeline', 1); // number of concurrent commands
2627
var versions_logged = false;
2728
var client_options = {
2829
parser: returnArg('parser', 'hiredis'),
@@ -41,17 +42,18 @@ function lpad (input, len, chr) {
4142

4243
metrics.Histogram.prototype.print_line = function () {
4344
var obj = this.printObj();
44-
return lpad((obj.min / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6) + '/' + lpad((obj.mean / 1e6).toFixed(2), 6);
45+
return lpad((obj.mean / 1e6).toFixed(2), 6) + '/' + lpad((obj.max / 1e6).toFixed(2), 6);
4546
};
4647

4748
function Test (args) {
4849
this.args = args;
50+
this.args.pipeline = +pipeline;
4951
this.callback = null;
5052
this.clients = [];
5153
this.clients_ready = 0;
5254
this.commands_sent = 0;
5355
this.commands_completed = 0;
54-
this.max_pipeline = this.args.pipeline || 50;
56+
this.max_pipeline = +pipeline;
5557
this.batch_pipeline = this.args.batch || 0;
5658
this.client_options = args.client_options || {};
5759
this.client_options.parser = client_options.parser;
@@ -206,7 +208,7 @@ Test.prototype.print_stats = function () {
206208
var duration = Date.now() - this.test_start;
207209
totalTime += duration;
208210

209-
console.log('min/max/avg: ' + this.command_latency.print_line() + ' ' + lpad(duration, 6) + 'ms total, ' +
211+
console.log('avg/max: ' + this.command_latency.print_line() + lpad(duration, 5) + 'ms total, ' +
210212
lpad(Math.round(this.commands_completed / (duration / 1000)), 7) + ' ops/sec');
211213
};
212214

@@ -217,55 +219,55 @@ large_buf = new Buffer(large_str);
217219
very_large_str = (new Array((4 * 1024 * 1024) + 1).join('-'));
218220
very_large_buf = new Buffer(very_large_str);
219221

220-
tests.push(new Test({descr: 'PING', command: 'ping', args: [], pipeline: 1}));
222+
tests.push(new Test({descr: 'PING', command: 'ping', args: []}));
221223
tests.push(new Test({descr: 'PING', command: 'ping', args: [], batch: 50}));
222224

223-
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], pipeline: 1}));
225+
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str]}));
224226
tests.push(new Test({descr: 'SET 4B str', command: 'set', args: ['foo_rand000000000000', small_str], batch: 50}));
225227

226-
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], pipeline: 1}));
228+
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf]}));
227229
tests.push(new Test({descr: 'SET 4B buf', command: 'set', args: ['foo_rand000000000000', small_buf], batch: 50}));
228230

229-
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], pipeline: 1}));
231+
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000']}));
230232
tests.push(new Test({descr: 'GET 4B str', command: 'get', args: ['foo_rand000000000000'], batch: 50}));
231233

232-
tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], pipeline: 1, client_opts: { return_buffers: true} }));
234+
tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], client_opts: { return_buffers: true} }));
233235
tests.push(new Test({descr: 'GET 4B buf', command: 'get', args: ['foo_rand000000000000'], batch: 50, client_opts: { return_buffers: true} }));
234236

235-
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], pipeline: 1}));
237+
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str]}));
236238
tests.push(new Test({descr: 'SET 4KiB str', command: 'set', args: ['foo_rand000000000001', large_str], batch: 50}));
237239

238-
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], pipeline: 1}));
240+
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf]}));
239241
tests.push(new Test({descr: 'SET 4KiB buf', command: 'set', args: ['foo_rand000000000001', large_buf], batch: 50}));
240242

241-
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], pipeline: 1}));
243+
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001']}));
242244
tests.push(new Test({descr: 'GET 4KiB str', command: 'get', args: ['foo_rand000000000001'], batch: 50}));
243245

244-
tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], pipeline: 1, client_opts: { return_buffers: true} }));
246+
tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], client_opts: { return_buffers: true} }));
245247
tests.push(new Test({descr: 'GET 4KiB buf', command: 'get', args: ['foo_rand000000000001'], batch: 50, client_opts: { return_buffers: true} }));
246248

247-
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], pipeline: 1}));
249+
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000']}));
248250
tests.push(new Test({descr: 'INCR', command: 'incr', args: ['counter_rand000000000000'], batch: 50}));
249251

250-
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], pipeline: 1}));
252+
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str]}));
251253
tests.push(new Test({descr: 'LPUSH', command: 'lpush', args: ['mylist', small_str], batch: 50}));
252254

253-
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], pipeline: 1}));
255+
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9']}));
254256
tests.push(new Test({descr: 'LRANGE 10', command: 'lrange', args: ['mylist', '0', '9'], batch: 50}));
255257

256-
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], pipeline: 1}));
258+
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99']}));
257259
tests.push(new Test({descr: 'LRANGE 100', command: 'lrange', args: ['mylist', '0', '99'], batch: 50}));
258260

259-
tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], pipeline: 1}));
261+
tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str]}));
260262
tests.push(new Test({descr: 'SET 4MiB str', command: 'set', args: ['foo_rand000000000002', very_large_str], batch: 20}));
261263

262-
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], pipeline: 1}));
264+
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf]}));
263265
tests.push(new Test({descr: 'SET 4MiB buf', command: 'set', args: ['foo_rand000000000002', very_large_buf], batch: 20}));
264266

265-
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], pipeline: 1}));
267+
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002']}));
266268
tests.push(new Test({descr: 'GET 4MiB str', command: 'get', args: ['foo_rand000000000002'], batch: 20}));
267269

268-
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], pipeline: 1, client_opts: { return_buffers: true} }));
270+
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], client_opts: { return_buffers: true} }));
269271
tests.push(new Test({descr: 'GET 4MiB buf', command: 'get', args: ['foo_rand000000000002'], batch: 20, client_opts: { return_buffers: true} }));
270272

271273
function next () {

0 commit comments

Comments
 (0)