UNPKG

47.3 kBMarkdownView Raw
1<p align="center">
2 <a href="https://github.com/noderedis/node-redis/">
3 <img width="190px" src="https://static.invertase.io/assets/node_redis_logo.png" />
4 </a>
5 <h2 align="center">Node Redis</h2>
6 <h4 align="center">A high performance Node.js Redis client.</h4>
7</p>
8
9---
10
11<p align="center">
12 <a href="https://www.npmjs.com/package/redis"><img src="https://img.shields.io/npm/dm/redis.svg?style=flat-square" alt="NPM downloads"></a>
13 <a href="https://www.npmjs.com/package/redis"><img src="https://img.shields.io/npm/v/redis.svg?style=flat-square" alt="NPM version"></a>
14 <a href="https://travis-ci.org/NodeRedis/node-redis"><img src="https://travis-ci.org/NodeRedis/node-redis.svg?style=flat-square&branch=master" alt="Build Status" /></a>
15 <a href="https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master"><img src="https://img.shields.io/appveyor/ci/BridgeAR/node-redis/master.svg?style=flat-square&label=Windows%20Tests" alt="Windows Tests" /></a>
16 <a href="https://coveralls.io/r/NodeRedis/node-redis?branch="><img src="https://coveralls.io/repos/NodeRedis/node-redis/badge.svg?style=flat-square&branch=" alt="Coverage Status" /></a>
17 <a href="https://twitter.com/NodeRedis"><img src="https://img.shields.io/twitter/follow/NodeRedis.svg?style=flat-square&colorA=1da1f2&colorB=&label=Follow%20on%20Twitter" alt="Follow on Twitter"></a>
18</p>
19
20---
21
22## Installation
23
24```bash
25npm install redis
26```
27
28## Usage
29
30#### Example
31
32```js
33const redis = require("redis");
34const client = redis.createClient();
35
36client.on("error", function(error) {
37 console.error(error);
38});
39
40client.set("key", "value", redis.print);
41client.get("key", redis.print);
42```
43
44Note that the API is entirely asynchronous. To get data back from the server,
45you'll need to use a callback.
46
47### Promises
48
49Node Redis currently doesn't natively support promises (this is coming in v4), however you can wrap the methods you
50want to use with promises using the built-in Node.js `util.promisify` method on Node.js >= v8;
51
52```js
53const { promisify } = require("util");
54const getAsync = promisify(client.get).bind(client);
55
56getAsync.then(console.log).catch(console.error);
57```
58
59### Commands
60
61This library is a 1 to 1 mapping of the [Redis commands](https://redis.io/commands).
62
63Each Redis command is exposed as a function on the `client` object.
64All functions take either an `args` Array plus optional `callback` Function or
65a variable number of individual arguments followed by an optional callback.
66Examples:
67
68```js
69client.hmset(["key", "foo", "bar"], function(err, res) {
70 // ...
71});
72
73// Works the same as
74client.hmset("key", ["foo", "bar"], function(err, res) {
75 // ...
76});
77
78// Or
79client.hmset("key", "foo", "bar", function(err, res) {
80 // ...
81});
82```
83
84Care should be taken with user input if arrays are possible (via body-parser, query string or other method), as single arguments could be unintentionally interpreted as multiple args.
85
86Note that in either form the `callback` is optional:
87
88```js
89client.set("foo", "bar");
90client.set(["hello", "world"]);
91```
92
93If the key is missing, reply will be null. Only if the [Redis Command
94Reference](http://redis.io/commands) states something else it will not be null.
95
96```js
97client.get("missing_key", function(err, reply) {
98 // reply is null when the key is missing
99 console.log(reply);
100});
101```
102
103Minimal parsing is done on the replies. Commands that return a integer return
104JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object
105keyed by the hash keys. All strings will either be returned as string or as
106buffer depending on your setting. Please be aware that sending null, undefined
107and Boolean values will result in the value coerced to a string!
108
109## API
110
111### Connection and other Events
112
113`client` will emit some events about the state of the connection to the Redis server.
114
115#### `"ready"`
116
117`client` will emit `ready` once a connection is established. Commands issued
118before the `ready` event are queued, then replayed just before this event is
119emitted.
120
121#### `"connect"`
122
123`client` will emit `connect` as soon as the stream is connected to the server.
124
125#### `"reconnecting"`
126
127`client` will emit `reconnecting` when trying to reconnect to the Redis server
128after losing the connection. Listeners are passed an object containing `delay`
129(in ms from the previous try) and `attempt` (the attempt #) attributes.
130
131#### `"error"`
132
133`client` will emit `error` when encountering an error connecting to the Redis
134server or when any other in Node Redis occurs. If you use a command without
135callback and encounter a ReplyError it is going to be emitted to the error
136listener.
137
138So please attach the error listener to Node Redis.
139
140#### `"end"`
141
142`client` will emit `end` when an established Redis server connection has closed.
143
144#### `"warning"`
145
146`client` will emit `warning` when password was set but none is needed and if a
147deprecated option / function / similar is used.
148
149### redis.createClient()
150
151If you have `redis-server` running on the same machine as node, then the
152defaults for port and host are probably fine and you don't need to supply any
153arguments. `createClient()` returns a `RedisClient` object. Otherwise,
154`createClient()` accepts these arguments:
155
156- `redis.createClient([options])`
157- `redis.createClient(unix_socket[, options])`
158- `redis.createClient(redis_url[, options])`
159- `redis.createClient(port[, host][, options])`
160
161**Tip:** If the Redis server runs on the same machine as the client consider
162using unix sockets if possible to increase throughput.
163
164**Note:** Using `'rediss://...` for the protocol in a `redis_url` will enable a TLS socket connection. However, additional TLS options will need to be passed in `options`, if required.
165
166#### `options` object properties
167
168| Property | Default | Description |
169| -------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
170| host | 127.0.0.1 | IP address of the Redis server |
171| port | 6379 | Port of the Redis server |
172| path | null | The UNIX socket string of the Redis server |
173| url | null | The URL of the Redis server. Format: `[redis[s]:]//[[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)). |
174| 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. |
175| return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. |
176| detect_buffers | false | If set to `true`, then replies will be sent to callbacks as Buffers. This option lets you switch between Buffers and Strings on a per-command basis, whereas `return_buffers` applies to every command on a client. **Note**: This doesn't work properly with the pubsub mode. A subscriber has to either always return Strings or Buffers. |
177| socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. |
178| socket_initial_delay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. |
179| no_ready_check | false | When a connection is established to the Redis server, the server might still be loading the database from disk. While loading, the server will not respond to any commands. To work around this, Node Redis has a "ready check" which sends the `INFO` command to the server. The response from the `INFO` command indicates whether the server is ready for more commands. When ready, `node_redis` emits a `ready` event. Setting `no_ready_check` to `true` will inhibit this check. |
180| enable_offline_queue | true | By default, if there is no active connection to the Redis server, commands are added to a queue and are executed once the connection has been established. Setting `enable_offline_queue` to `false` will disable this feature and the callback will be executed immediately with an error, or an error will be emitted if no callback is specified. |
181| retry_unfulfilled_commands | false | If set to `true`, all commands that were unfulfilled while the connection is lost will be retried after the connection has been reestablished. Use this with caution if you use state altering commands (e.g. `incr`). This is especially useful if you use blocking commands. |
182| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` **Note** Node Redis < 2.5 must use `auth_pass` |
183| db | null | If set, client will run Redis `select` command on connect. |
184| family | IPv4 | You can force using IPv6 if you set the family to 'IPv6'. See Node.js [net](https://nodejs.org/api/net.html) or [dns](https://nodejs.org/api/dns.html) modules on how to use the family type. |
185| disable_resubscribing | false | If set to `true`, a client won't resubscribe after disconnecting. |
186| rename_commands | null | Passing an object with renamed commands to use instead of the original functions. For example, if you renamed the command KEYS to "DO-NOT-USE" then the rename_commands object would be: `{ KEYS : "DO-NOT-USE" }` . See the [Redis security topics](http://redis.io/topics/security) for more info. |
187| tls | null | 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). |
188| prefix | null | A string used to prefix all used keys (e.g. `namespace:test`). Please be aware that the `keys` command will not be prefixed. The `keys` command has a "pattern" as argument and no key and it would be impossible to determine the existing keys in Redis if this would be prefixed. |
189| retry_strategy | function | A function that receives an options object as parameter including the retry `attempt`, the `total_retry_time` indicating how much time passed since the last time connected, the `error` why the connection was lost and the number of `times_connected` in total. If you return a number from this function, the retry will happen exactly after that time in milliseconds. If you return a non-number, no further retry will happen and all offline commands are flushed with errors. Return an error to return that specific error to all offline commands. Example below. |
190
191**`detect_buffers` example:**
192
193```js
194const redis = require("redis");
195const client = redis.createClient({ detect_buffers: true });
196
197client.set("foo_rand000000000000", "OK");
198
199// This will return a JavaScript String
200client.get("foo_rand000000000000", function(err, reply) {
201 console.log(reply.toString()); // Will print `OK`
202});
203
204// This will return a Buffer since original key is specified as a Buffer
205client.get(new Buffer("foo_rand000000000000"), function(err, reply) {
206 console.log(reply.toString()); // Will print `<Buffer 4f 4b>`
207});
208```
209
210**`retry_strategy` example:**
211
212```js
213var client = redis.createClient({
214 retry_strategy: function(options) {
215 if (options.error && options.error.code === "ECONNREFUSED") {
216 // End reconnecting on a specific error and flush all commands with
217 // a individual error
218 return new Error("The server refused the connection");
219 }
220 if (options.total_retry_time > 1000 * 60 * 60) {
221 // End reconnecting after a specific timeout and flush all commands
222 // with a individual error
223 return new Error("Retry time exhausted");
224 }
225 if (options.attempt > 10) {
226 // End reconnecting with built in error
227 return undefined;
228 }
229 // reconnect after
230 return Math.min(options.attempt * 100, 3000);
231 },
232});
233```
234
235### client.auth(password[, callback])
236
237When connecting to a Redis server that requires authentication, the `AUTH`
238command must be sent as the first command after connecting. This can be tricky
239to coordinate with reconnections, the ready check, etc. To make this easier,
240`client.auth()` stashes `password` and will send it after each connection,
241including reconnections. `callback` is invoked only once, after the response to
242the very first `AUTH` command sent.
243NOTE: Your call to `client.auth()` should not be inside the ready handler. If
244you are doing this wrong, `client` will emit an error that looks
245something like this `Error: Ready check failed: ERR operation not permitted`.
246
247### client.quit(callback)
248
249This sends the quit command to the redis server and ends cleanly right after all
250running commands were properly handled. If this is called while reconnecting
251(and therefore no connection to the redis server exists) it is going to end the
252connection right away instead of resulting in further reconnections! All offline
253commands are going to be flushed with an error in that case.
254
255### client.end(flush)
256
257Forcibly close the connection to the Redis server. Note that this does not wait
258until all replies have been parsed. If you want to exit cleanly, call
259`client.quit()` as mentioned above.
260
261You should set flush to true, if you are not absolutely sure you do not care
262about any other commands. If you set flush to false all still running commands
263will silently fail.
264
265This example closes the connection to the Redis server before the replies have
266been read. You probably don't want to do this:
267
268```js
269const redis = require("redis");
270const client = redis.createClient();
271
272client.set("hello", "world", function(err) {
273 // This will either result in an error (flush parameter is set to true)
274 // or will silently fail and this callback will not be called at all (flush set to false)
275 console.error(err);
276});
277
278// No further commands will be processed
279client.end(true);
280
281client.get("hello", function(err) {
282 console.error(err); // => 'The connection has already been closed.'
283});
284```
285
286`client.end()` without the flush parameter set to true should NOT be used in production!
287
288### Error Handling
289
290Currently the following `Error` subclasses exist:
291
292- `RedisError`: _All errors_ returned by the client
293- `ReplyError` subclass of `RedisError`: All errors returned by **Redis** itself
294- `AbortError` subclass of `RedisError`: All commands that could not finish due
295 to what ever reason
296- `ParserError` subclass of `RedisError`: Returned in case of a parser error
297 (this should not happen)
298- `AggregateError` subclass of `AbortError`: Emitted in case multiple unresolved
299 commands without callback got rejected in debug_mode instead of lots of
300 `AbortError`s.
301
302All error classes are exported by the module.
303
304#### Example
305
306```js
307const assert = require("assert");
308
309const redis = require("redis");
310const { AbortError, AggregateError, ReplyError } = require("redis");
311
312const client = redis.createClient();
313
314client.on("error", function(err) {
315 assert(err instanceof Error);
316 assert(err instanceof AbortError);
317 assert(err instanceof AggregateError);
318
319 // The set and get are aggregated in here
320 assert.strictEqual(err.errors.length, 2);
321 assert.strictEqual(err.code, "NR_CLOSED");
322});
323
324client.set("foo", "bar", "baz", function(err, res) {
325 // Too many arguments
326 assert(err instanceof ReplyError); // => true
327 assert.strictEqual(err.command, "SET");
328 assert.deepStrictEqual(err.args, ["foo", 123, "bar"]);
329
330 redis.debug_mode = true;
331
332 client.set("foo", "bar");
333 client.get("foo");
334
335 process.nextTick(function() {
336 // Force closing the connection while the command did not yet return
337 client.end(true);
338 redis.debug_mode = false;
339 });
340});
341```
342
343Every `ReplyError` contains the `command` name in all-caps and the arguments (`args`).
344
345If Node Redis emits a library error because of another error, the triggering
346error is added to the returned error as `origin` attribute.
347
348**_Error codes_**
349
350Node Redis returns a `NR_CLOSED` error code if the clients connection dropped.
351If a command unresolved command got rejected a `UNCERTAIN_STATE` code is
352returned. A `CONNECTION_BROKEN` error code is used in case Node Redis gives up
353to reconnect.
354
355### client.unref()
356
357Call `unref()` on the underlying socket connection to the Redis server, allowing
358the program to exit once no more commands are pending.
359
360This is an **experimental** feature, and only supports a subset of the Redis
361protocol. Any commands where client state is saved on the Redis server, e.g.
362`*SUBSCRIBE` or the blocking `BL*` commands will _NOT_ work with `.unref()`.
363
364```js
365const redis = require("redis");
366const client = redis.createClient();
367
368/*
369 * Calling unref() will allow this program to exit immediately after the get
370 * command finishes. Otherwise the client would hang as long as the
371 * client-server connection is alive.
372 */
373client.unref();
374
375client.get("foo", function(err, value) {
376 if (err) throw err;
377 console.log(value);
378});
379```
380
381### Hash Commands
382
383Most Redis commands take a single String or an Array of Strings as arguments,
384and replies are sent back as a single String or an Array of Strings. When
385dealing with hash values, there are a couple of useful exceptions to this.
386
387#### client.hgetall(hash, callback)
388
389The reply from an `HGETALL` command will be converted into a JavaScript Object. That way you can interact with the
390responses using JavaScript syntax.
391
392**Example:**
393
394```js
395client.hmset("key", "foo", "bar", "hello", "world");
396
397client.hgetall("hosts", function(err, value) {
398 console.log(value.foo); // > "bar"
399 console.log(value.hello); // > "world"
400});
401```
402
403#### client.hmset(hash, key1, val1, ...keyN, valN, [callback])
404
405Multiple values may also be set by supplying more arguments.
406
407**Example:**
408
409```js
410// key
411// 1) foo => bar
412// 2) hello => world
413client.HMSET("key", "foo", "bar", "hello", "world");
414```
415
416### PubSub
417
418#### Example
419
420This example opens two client connections, subscribes to a channel on one of them, and publishes to that
421channel on the other.
422
423```js
424const redis = require("redis");
425
426const subscriber = redis.createClient();
427const publisher = redis.createClient();
428
429let messageCount = 0;
430
431subscriber.on("subscribe", function(channel, count) {
432 publisher.publish("a channel", "a message");
433 publisher.publish("a channel", "another message");
434});
435
436subscriber.on("message", function(channel, message) {
437 messageCount += 1;
438
439 console.log("Subscriber received message in channel '" + channel + "': " + message);
440
441 if (messageCount === 2) {
442 subscriber.unsubscribe();
443 subscriber.quit();
444 publisher.quit();
445 }
446});
447
448subscriber.subscribe("a channel");
449```
450
451When a client issues a `SUBSCRIBE` or `PSUBSCRIBE`, that connection is put into
452a `"subscriber"` mode. At that point, the only valid commands are those that modify the subscription
453set, and quit (also ping on some redis versions). When
454the subscription set is empty, the connection is put back into regular mode.
455
456If you need to send regular commands to Redis while in subscriber mode, just
457open another connection with a new client (use `client.duplicate()` to quickly duplicate an existing client).
458
459#### Subscriber Events
460
461If a client has subscriptions active, it may emit these events:
462
463**"message" (channel, message)**:
464
465Client will emit `message` for every message received that matches an active subscription.
466Listeners are passed the channel name as `channel` and the message as `message`.
467
468**"pmessage" (pattern, channel, message)**:
469
470Client will emit `pmessage` for every message received that matches an active
471subscription pattern. Listeners are passed the original pattern used with
472`PSUBSCRIBE` as `pattern`, the sending channel name as `channel`, and the
473message as `message`.
474
475**"message_buffer" (channel, message)**:
476
477This is the same as the `message` event with the exception, that it is always
478going to emit a buffer. If you listen to the `message` event at the same time as
479the `message_buffer`, it is always going to emit a string.
480
481**"pmessage_buffer" (pattern, channel, message)**:
482
483This is the same as the `pmessage` event with the exception, that it is always
484going to emit a buffer. If you listen to the `pmessage` event at the same time
485as the `pmessage_buffer`, it is always going to emit a string.
486
487**"subscribe" (channel, count)**:
488
489Client will emit `subscribe` in response to a `SUBSCRIBE` command. Listeners are
490passed the channel name as `channel` and the new count of subscriptions for this
491client as `count`.
492
493**"psubscribe" (pattern, count)**:
494
495Client will emit `psubscribe` in response to a `PSUBSCRIBE` command. Listeners
496are passed the original pattern as `pattern`, and the new count of subscriptions
497for this client as `count`.
498
499**"unsubscribe" (channel, count)**:
500
501Client will emit `unsubscribe` in response to a `UNSUBSCRIBE` command. Listeners
502are passed the channel name as `channel` and the new count of subscriptions for
503this client as `count`. When `count` is 0, this client has left subscriber mode
504and no more subscriber events will be emitted.
505
506**"punsubscribe" (pattern, count)**:
507
508Client will emit `punsubscribe` in response to a `PUNSUBSCRIBE` command.
509Listeners are passed the channel name as `channel` and the new count of
510subscriptions for this client as `count`. When `count` is 0, this client has
511left subscriber mode and no more subscriber events will be emitted.
512
513### client.multi([commands])
514
515`MULTI` commands are queued up until an `EXEC` is issued, and then all commands
516are run atomically by Redis. The interface returns an
517individual `Multi` object by calling `client.multi()`. If any command fails to
518queue, all commands are rolled back and none is going to be executed (For
519further information see the [Redis transactions](http://redis.io/topics/transactions) documentation).
520
521```js
522const redis = require("redis");
523const client = redis.createClient();
524
525let setSize = 20;
526
527client.sadd("key", "member1");
528client.sadd("key", "member2");
529
530while (setSize > 0) {
531 client.sadd("key", "member" + setSize);
532 setSize -= 1;
533}
534
535// chain commands
536client
537 .multi()
538 .scard("key")
539 .smembers("key")
540 .keys("*")
541 .dbsize()
542 .exec(function(err, replies) {
543 console.log("MULTI got " + replies.length + " replies");
544 replies.forEach(function(reply, index) {
545 console.log("REPLY @ index " + index + ": " + reply.toString());
546 });
547 });
548```
549
550#### Multi.exec([callback])
551
552`client.multi()` is a constructor that returns a `Multi` object. `Multi` objects
553share all of the same command methods as `client` objects do. Commands are
554queued up inside the `Multi` object until `Multi.exec()` is invoked.
555
556If your code contains an syntax error an `EXECABORT` error is going to be thrown
557and all commands are going to be aborted. That error contains a `.errors`
558property that contains the concrete errors.
559If all commands were queued successfully and an error is thrown by redis while
560processing the commands that error is going to be returned in the result array!
561No other command is going to be aborted though than the ones failing.
562
563You can either chain together `MULTI` commands as in the above example, or you
564can queue individual commands while still sending regular client command as in
565this example:
566
567```js
568const redis = require("redis");
569const client = redis.createClient();
570
571// start a separate multi command queue
572const multi = client.multi();
573
574// add some commands to the queue
575multi.incr("count_cats", redis.print);
576multi.incr("count_dogs", redis.print);
577
578// runs a command immediately outside of the `multi` instance
579client.mset("count_cats", 100, "count_dogs", 50, redis.print);
580
581// drains the multi queue and runs each command atomically
582multi.exec(function(err, replies) {
583 console.log(replies); // 101, 51
584});
585```
586
587In addition to adding commands to the `MULTI` queue individually, you can also
588pass an array of commands and arguments to the constructor:
589
590```js
591const redis = require("redis");
592
593const client = redis.createClient();
594
595client
596 .multi([
597 ["mget", "foo", "bar", redis.print],
598 ["incr", "hello"],
599 ])
600 .exec(function(err, replies) {
601 console.log(replies);
602 });
603```
604
605#### Multi.exec_atomic([callback])
606
607Identical to Multi.exec but with the difference that executing a single command
608will not use transactions.
609
610#### Optimistic Locks
611
612Using `multi` you can make sure your modifications run as a transaction, but you
613can't be sure you got there first. What if another client modified a key while
614you were working with it's data?
615
616To solve this, Redis supports the [WATCH](https://redis.io/topics/transactions)
617command, which is meant to be used with MULTI:
618
619```js
620const redis = require("redis");
621
622const client = redis.createClient();
623
624client.watch("foo", function(watchError) {
625 if (watchError) throw watchError;
626
627 client.get("foo", function(getError, result) {
628 if (getError) throw getError;
629
630 // Process result
631 // Heavy and time consuming operation here to generate "bar"
632
633 client
634 .multi()
635 .set("foo", "bar")
636 .exec(function(execError, results) {
637 /**
638 * If err is null, it means Redis successfully attempted
639 * the operation.
640 */
641 if (execError) throw err;
642
643 /**
644 * If results === null, it means that a concurrent client
645 * changed the key while we were processing it and thus
646 * the execution of the MULTI command was not performed.
647 *
648 * NOTICE: Failing an execution of MULTI is not considered
649 * an error. So you will have err === null and results === null
650 */
651 });
652 });
653});
654```
655
656The above snippet shows the correct usage of `watch` with `multi`. Every time a
657watched key is changed before the execution of a `multi` command, the execution
658will return `null`. On a normal situation, the execution will return an array of
659values with the results of the operations.
660
661As stated in the snippet, failing the execution of a `multi` command being watched
662is not considered an error. The execution may return an error if, for example, the
663client cannot connect to Redis.
664
665An example where we can see the execution of a `multi` command fail is as follows:
666
667```js
668const clients = {
669 watcher: redis.createClient(),
670 modifier: redis.createClient(),
671};
672
673clients.watcher.watch("foo", function(watchError) {
674 if (watchError) throw watchError;
675
676 // if you comment out the next line, the transaction will work
677 clients.modifier.set("foo", Math.random(), setError => {
678 if (setError) throw err;
679 });
680
681 //using a setTimeout here to ensure that the MULTI/EXEC will come after the SET.
682 //Normally, you would use a callback to ensure order, but I want the above SET command
683 //to be easily comment-out-able.
684 setTimeout(function() {
685 clients.watcher
686 .multi()
687 .set("foo", "bar")
688 .set("hello", "world")
689 .exec((multiExecError, results) => {
690 if (multiExecError) throw multiExecError;
691
692 if (results === null) {
693 console.log("transaction aborted because results were null");
694 } else {
695 console.log("transaction worked and returned", results);
696 }
697
698 clients.watcher.quit();
699 clients.modifier.quit();
700 });
701 }, 1000);
702});
703```
704
705#### `WATCH` limitations
706
707Redis WATCH works only on _whole_ key values. For example, with WATCH you can
708watch a hash for modifications, but you cannot watch a specific field of a hash.
709
710The following example would watch the keys `foo` and `hello`, not the field `hello`
711of hash `foo`:
712
713```js
714const redis = require("redis");
715
716const client = redis.createClient();
717
718client.hget("foo", "hello", function(hashGetError, result) {
719 if (hashGetError) throw hashGetError;
720
721 //Do some processing with the value from this field and watch it after
722
723 client.watch("foo", "hello", function(watchError) {
724 if (watchError) throw watchError;
725
726 /**
727 * This is now watching the keys 'foo' and 'hello'. It is not
728 * watching the field 'hello' of hash 'foo'. Because the key 'foo'
729 * refers to a hash, this command is now watching the entire hash
730 * for modifications.
731 */
732 });
733});
734```
735
736This limitation also applies to sets (you can not watch individual set members)
737and any other collections.
738
739### client.batch([commands])
740
741Identical to `.multi()` without transactions. This is recommended if you want to
742execute many commands at once but don't need to rely on transactions.
743
744`BATCH` commands are queued up until an `EXEC` is issued, and then all commands
745are run atomically by Redis. The interface returns an
746individual `Batch` object by calling `client.batch()`. The only difference
747between .batch and .multi is that no transaction is going to be used.
748Be aware that the errors are - just like in multi statements - in the result.
749Otherwise both, errors and results could be returned at the same time.
750
751If you fire many commands at once this is going to boost the execution speed
752significantly compared to firing the same commands in a loop without waiting for
753the result! See the benchmarks for further comparison. Please remember that all
754commands are kept in memory until they are fired.
755
756### Monitor mode
757
758Redis supports the `MONITOR` command, which lets you see all commands received
759by the Redis server across all client connections, including from other client
760libraries and other computers.
761
762A `monitor` event is going to be emitted for every command fired from any client
763connected to the server including the monitoring client itself. The callback for
764the `monitor` event takes a timestamp from the Redis server, an array of command
765arguments and the raw monitoring string.
766
767#### Example:
768
769```js
770const redis = require("redis");
771const client = redis.createClient();
772
773client.monitor(function(err, res) {
774 console.log("Entering monitoring mode.");
775});
776
777client.set("foo", "bar");
778
779client.on("monitor", function(time, args, rawReply) {
780 console.log(time + ": " + args); // 1458910076.446514:['set', 'foo', 'bar']
781});
782```
783
784## Extras
785
786Some other things you might find useful.
787
788### `client.server_info`
789
790After the ready probe completes, the results from the INFO command are saved in
791the `client.server_info` object.
792
793The `versions` key contains an array of the elements of the version string for
794easy comparison.
795
796```
797> client.server_info.redis_version
798'2.3.0'
799> client.server_info.versions
800[ 2, 3, 0 ]
801```
802
803### `redis.print()`
804
805A handy callback function for displaying return values when testing. Example:
806
807```js
808const redis = require("redis");
809const client = redis.createClient();
810
811client.on("connect", function() {
812 client.set("foo", "bar", redis.print); // => "Reply: OK"
813 client.get("foo", redis.print); // => "Reply: bar"
814 client.quit();
815});
816```
817
818### Multi-word commands
819
820To execute redis multi-word commands like `SCRIPT LOAD` or `CLIENT LIST` pass
821the second word as first parameter:
822
823```js
824client.script("load", "return 1");
825
826client
827 .multi()
828 .script("load", "return 1")
829 .exec();
830
831client.multi([["script", "load", "return 1"]]).exec();
832```
833
834### `client.duplicate([options][, callback])`
835
836Duplicate all current options and return a new redisClient instance. All options
837passed to the duplicate function are going to replace the original option. If
838you pass a callback, duplicate is going to wait until the client is ready and
839returns it in the callback. If an error occurs in the meanwhile, that is going
840to return an error instead in the callback.
841
842One example of when to use duplicate() would be to accommodate the connection-
843blocking redis commands BRPOP, BLPOP, and BRPOPLPUSH. If these commands
844are used on the same Redis client instance as non-blocking commands, the
845non-blocking ones may be queued up until after the blocking ones finish.
846
847Another reason to use duplicate() is when multiple DBs on the same server are
848accessed via the redis SELECT command. Each DB could use its own connection.
849
850### `client.send_command(command_name[, [args][, callback]])`
851
852All Redis commands have been added to the `client` object. However, if new
853commands are introduced before this library is updated or if you want to add
854individual commands you can use `send_command()` to send arbitrary commands to
855Redis.
856
857All commands are sent as multi-bulk commands. `args` can either be an Array of
858arguments, or omitted / set to undefined.
859
860### `redis.add_command(command_name)`
861
862Calling add_command will add a new command to the prototype. The exact command
863name will be used when calling using this new command. Using arbitrary arguments
864is possible as with any other command.
865
866### `client.connected`
867
868Boolean tracking the state of the connection to the Redis server.
869
870### `client.command_queue_length`
871
872The number of commands that have been sent to the Redis server but not yet
873replied to. You can use this to enforce some kind of maximum queue depth for
874commands while connected.
875
876### `client.offline_queue_length`
877
878The number of commands that have been queued up for a future connection. You can
879use this to enforce some kind of maximum queue depth for pre-connection
880commands.
881
882### Commands with Optional and Keyword arguments
883
884This applies to anything that uses an optional `[WITHSCORES]` or `[LIMIT offset count]` in the [redis.io/commands](http://redis.io/commands) documentation.
885
886#### Example
887
888```js
889const args = ["myzset", 1, "one", 2, "two", 3, "three", 99, "ninety-nine"];
890
891client.zadd(args, function(addError, addResponse) {
892 if (addError) throw addError;
893 console.log("added " + addResponse + " items.");
894
895 // -Infinity and +Infinity also work
896 const args1 = ["myzset", "+inf", "-inf"];
897 client.zrevrangebyscore(args1, function(rangeError, rangeResponse) {
898 if (rangeError) throw rangeError;
899 console.log("response1", rangeResponse);
900 // ...
901 });
902
903 const max = 3;
904 const min = 1;
905 const offset = 1;
906 const count = 2;
907 const args2 = ["myzset", max, min, "WITHSCORES", "LIMIT", offset, count];
908 client.zrevrangebyscore(args2, function(rangeError, rangeResponse) {
909 if (rangeError) throw rangeError;
910 console.log("response2", rangeResponse);
911 // ...
912 });
913});
914```
915
916## Performance
917
918Much effort has been spent to make Node Redis as fast as possible for common operations.
919
920```
921Mac mini (2018), i7-3.2GHz and 32gb memory
922clients: 1, NodeJS: 12.15.0, Redis: 5.0.6, parser: javascript, connected by: tcp
923 PING, 1/1 avg/max: 0.03/ 3.28 2501ms total, 31926 ops/sec
924 PING, batch 50/1 avg/max: 0.08/ 3.35 2501ms total, 599460 ops/sec
925 SET 4B str, 1/1 avg/max: 0.03/ 3.54 2501ms total, 29483 ops/sec
926 SET 4B str, batch 50/1 avg/max: 0.10/ 1.39 2501ms total, 477689 ops/sec
927 SET 4B buf, 1/1 avg/max: 0.04/ 1.52 2501ms total, 23449 ops/sec
928 SET 4B buf, batch 50/1 avg/max: 0.20/ 2.09 2501ms total, 244382 ops/sec
929 GET 4B str, 1/1 avg/max: 0.03/ 1.35 2501ms total, 32205 ops/sec
930 GET 4B str, batch 50/1 avg/max: 0.09/ 2.02 2501ms total, 568992 ops/sec
931 GET 4B buf, 1/1 avg/max: 0.03/ 2.93 2501ms total, 32802 ops/sec
932 GET 4B buf, batch 50/1 avg/max: 0.08/ 1.03 2501ms total, 592863 ops/sec
933 SET 4KiB str, 1/1 avg/max: 0.03/ 0.76 2501ms total, 29287 ops/sec
934 SET 4KiB str, batch 50/1 avg/max: 0.35/ 2.97 2501ms total, 143163 ops/sec
935 SET 4KiB buf, 1/1 avg/max: 0.04/ 1.21 2501ms total, 23070 ops/sec
936 SET 4KiB buf, batch 50/1 avg/max: 0.28/ 2.34 2501ms total, 176809 ops/sec
937 GET 4KiB str, 1/1 avg/max: 0.03/ 1.54 2501ms total, 29555 ops/sec
938 GET 4KiB str, batch 50/1 avg/max: 0.18/ 1.59 2501ms total, 279188 ops/sec
939 GET 4KiB buf, 1/1 avg/max: 0.03/ 1.80 2501ms total, 30681 ops/sec
940 GET 4KiB buf, batch 50/1 avg/max: 0.17/ 5.00 2501ms total, 285886 ops/sec
941 INCR, 1/1 avg/max: 0.03/ 1.99 2501ms total, 32757 ops/sec
942 INCR, batch 50/1 avg/max: 0.09/ 2.54 2501ms total, 538964 ops/sec
943 LPUSH, 1/1 avg/max: 0.05/ 4.85 2501ms total, 19482 ops/sec
944 LPUSH, batch 50/1 avg/max: 0.12/ 9.52 2501ms total, 395562 ops/sec
945 LRANGE 10, 1/1 avg/max: 0.06/ 9.21 2501ms total, 17062 ops/sec
946 LRANGE 10, batch 50/1 avg/max: 0.22/ 1.03 2501ms total, 228269 ops/sec
947 LRANGE 100, 1/1 avg/max: 0.05/ 1.44 2501ms total, 19051 ops/sec
948 LRANGE 100, batch 50/1 avg/max: 0.99/ 3.46 2501ms total, 50480 ops/sec
949 SET 4MiB str, 1/1 avg/max: 4.11/ 13.96 2501ms total, 243 ops/sec
950 SET 4MiB str, batch 20/1 avg/max: 91.16/145.01 2553ms total, 219 ops/sec
951 SET 4MiB buf, 1/1 avg/max: 2.81/ 11.90 2502ms total, 354 ops/sec
952 SET 4MiB buf, batch 20/1 avg/max: 36.21/ 70.96 2535ms total, 552 ops/sec
953 GET 4MiB str, 1/1 avg/max: 2.82/ 19.10 2503ms total, 354 ops/sec
954 GET 4MiB str, batch 20/1 avg/max: 128.57/207.86 2572ms total, 156 ops/sec
955 GET 4MiB buf, 1/1 avg/max: 3.13/ 23.88 2501ms total, 318 ops/sec
956 GET 4MiB buf, batch 20/1 avg/max: 65.91/ 87.59 2572ms total, 303 ops/sec
957```
958
959## Debugging
960
961To get debug output run your Node Redis application with `NODE_DEBUG=redis`.
962
963This is also going to result in good stack traces opposed to useless ones
964otherwise for any async operation.
965If you only want to have good stack traces but not the debug output run your
966application in development mode instead (`NODE_ENV=development`).
967
968Good stack traces are only activated in development and debug mode as this
969results in a significant performance penalty.
970
971**_Comparison_**:
972
973Standard stack trace:
974
975```
976ReplyError: ERR wrong number of arguments for 'set' command
977 at parseError (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:158:12)
978 at parseType (/home/ruben/repos/redis/node_modules/redis-parser/lib/parser.js:219:14)
979```
980
981Debug stack trace:
982
983```
984ReplyError: ERR wrong number of arguments for 'set' command
985 at new Command (/home/ruben/repos/redis/lib/command.js:9:902)
986 at RedisClient.set (/home/ruben/repos/redis/lib/commands.js:9:3238)
987 at Context.<anonymous> (/home/ruben/repos/redis/test/good_stacks.spec.js:20:20)
988 at callFnAsync (/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:349:8)
989 at Test.Runnable.run (/home/ruben/repos/redis/node_modules/mocha/lib/runnable.js:301:7)
990 at Runner.runTest (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:422:10)
991 at /home/ruben/repos/redis/node_modules/mocha/lib/runner.js:528:12
992 at next (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:342:14)
993 at /home/ruben/repos/redis/node_modules/mocha/lib/runner.js:352:7
994 at next (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:284:14)
995 at Immediate._onImmediate (/home/ruben/repos/redis/node_modules/mocha/lib/runner.js:320:5)
996 at processImmediate [as _immediateCallback] (timers.js:383:17)
997```
998
999## Contributing
1000
1001Please see the [contributing guide](CONTRIBUTING.md).
1002
1003## License
1004
1005This repository is licensed under the "MIT" license. See [LICENSE](LICENSE).