You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-04 15:02:09 +03:00
start of work towards v3 release
This commit is contained in:
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
open_collective: node-redis
|
@@ -11,3 +11,9 @@ coverage/
|
||||
*.rdb
|
||||
*.out
|
||||
*.yml
|
||||
CHANGELOG.md
|
||||
CONTRIBUTING.md
|
||||
CODE_OF_CONDUCT.md
|
||||
.travis.yml
|
||||
appveyor.yml
|
||||
package-lock.json
|
||||
|
@@ -9,7 +9,6 @@ addons:
|
||||
packages:
|
||||
- g++-4.8
|
||||
node_js:
|
||||
- "4"
|
||||
- "6"
|
||||
- "8"
|
||||
- "12"
|
||||
|
@@ -1,5 +1,34 @@
|
||||
# Changelog
|
||||
|
||||
## v.3.0.0 - 09 Feb, 2020
|
||||
|
||||
This version is mainly a release to distribute all the unreleased changes on master since 2017 and additionally removes
|
||||
a lot of old deprecated features and old internals in preparation for an upcoming modernization refactor (v4).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Dropped support for Node.js < 6
|
||||
- Dropped support for `hiredis` (no longer required)
|
||||
- Removed previously deprecated `drain` event
|
||||
- Removed previously deprecated `idle` event
|
||||
- Removed previously deprecated `parser` option
|
||||
- Removed previously deprecated `max_delay` option
|
||||
- Removed previously deprecated `max_attempts` option
|
||||
- Removed previously deprecated `socket_no_delay` option
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Removed development files from published package (#1370)
|
||||
- Duplicate function now allows db param to be passed (#1311)
|
||||
|
||||
### Features
|
||||
|
||||
- Upgraded to latest `redis-commands` package
|
||||
- Upgraded to latest `redis-parser` package, v3.0.0, which brings performance improvements
|
||||
- Replaced `double-ended-queue` with `denque`, which brings performance improvements
|
||||
- Add timestamps to debug traces
|
||||
- Add `socket_initial_delay` option for `socket.setKeepAlive` (#1396)
|
||||
|
||||
## v.2.8.0 - 31 Jul, 2017
|
||||
|
||||
Features
|
46
CODE_OF_CONDUCT.md
Normal file
46
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at `redis @ invertase.io`. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
1
CONTRIBUTING.md
Normal file
1
CONTRIBUTING.md
Normal file
@@ -0,0 +1 @@
|
||||
<!-- TODO -->
|
4
LICENSE
4
LICENSE
@@ -1,6 +1,6 @@
|
||||
LICENSE - "MIT License"
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 by NodeRedis
|
||||
Copyright (c) Node Redis contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
81
README.md
81
README.md
@@ -1,10 +1,21 @@
|
||||
redis - a node.js redis client
|
||||
===========================
|
||||
<p align="center">
|
||||
<a href="https://github.com/noderedis/node-redis/">
|
||||
<img width="160px" src="https://static.invertase.io/assets/node_redis_logo.png" />
|
||||
</a>
|
||||
<h2 align="center">Node Redis</h2>
|
||||
A high performance Node.js Redis client.
|
||||
</p>
|
||||
|
||||
[](https://travis-ci.org/NodeRedis/node_redis)
|
||||
[](https://coveralls.io/r/NodeRedis/node_redis?branch=)
|
||||
[](https://ci.appveyor.com/project/BridgeAR/node-redis/branch/master)
|
||||
[](https://gitter.im/NodeRedis/node_redis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
<p align="center">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<a href="https://twitter.com/rnfirebase"><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>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
This is a complete and feature rich Redis client for node.js. __It supports all
|
||||
Redis commands__ and focuses on high performance.
|
||||
@@ -195,30 +206,11 @@ So please attach the error listener to node_redis.
|
||||
|
||||
`client` will emit `end` when an established Redis server connection has closed.
|
||||
|
||||
### "drain" (deprecated)
|
||||
|
||||
`client` will emit `drain` when the TCP connection to the Redis server has been
|
||||
buffering, but is now writable. This event can be used to stream commands in to
|
||||
Redis and adapt to backpressure.
|
||||
|
||||
If the stream is buffering `client.should_buffer` is set to true. Otherwise the
|
||||
variable is always set to false. That way you can decide when to reduce your
|
||||
send rate and resume sending commands when you get `drain`.
|
||||
|
||||
You can also check the return value of each command as it will also return the
|
||||
backpressure indicator (deprecated). If false is returned the stream had to
|
||||
buffer.
|
||||
|
||||
### "warning"
|
||||
|
||||
`client` will emit `warning` when password was set but none is needed and if a
|
||||
deprecated option / function / similar is used.
|
||||
|
||||
### "idle" (deprecated)
|
||||
|
||||
`client` will emit `idle` when there are no outstanding commands that are
|
||||
awaiting a response.
|
||||
|
||||
## redis.createClient()
|
||||
If you have `redis-server` running on the same machine as node, then the
|
||||
defaults for port and host are probably fine and you don't need to supply any
|
||||
@@ -242,17 +234,13 @@ __Note:__ Using `'rediss://...` for the protocol in a `redis_url` will enable a
|
||||
| port | 6379 | Port of the Redis server |
|
||||
| path | null | The UNIX socket string of the Redis server |
|
||||
| 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)). |
|
||||
| parser | javascript | __Deprecated__ Use either the built-in JS parser [`javascript`]() or the native [`hiredis`]() parser. __Note__ `node_redis` < 2.6 uses hiredis as default if installed. This changed in v.2.6.0. |
|
||||
| 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. |
|
||||
| return_buffers | false | If set to `true`, then all replies will be sent to callbacks as Buffers instead of Strings. |
|
||||
| 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. |
|
||||
| socket_keepalive | true | If set to `true`, the keep-alive functionality is enabled on the underlying socket. |
|
||||
| socket_initialdelay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. |
|
||||
| socket_initial_delay | 0 | Initial Delay in milliseconds, and this will also behave the interval keep alive message sending to Redis. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| retry_max_delay | null | __Deprecated__ _Please use `retry_strategy` instead._ By default, every time the client tries to connect and fails, the reconnection delay almost doubles. This delay normally grows infinitely, but setting `retry_max_delay` limits it to the maximum value provided in milliseconds. |
|
||||
| connect_timeout | 3600000 | __Deprecated__ _Please use `retry_strategy` instead._ Setting `connect_timeout` limits the total time for the client to connect and reconnect. The value is provided in milliseconds and is counted from the moment a new client is created or from the time the connection is lost. The last retry is going to happen exactly at the timeout time. Default is to try connecting until the default system socket timeout has been exceeded and to try reconnecting until 1h has elapsed. |
|
||||
| max_attempts | 0 | __Deprecated__ _Please use `retry_strategy` instead._ By default, a client will try reconnecting until connected. Setting `max_attempts` limits total amount of connection attempts. Setting this to 1 will prevent any reconnect attempt. |
|
||||
| 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. |
|
||||
| password | null | If set, client will run Redis auth command on connect. Alias `auth_pass` __Note__ `node_redis` < 2.5 must use `auth_pass` |
|
||||
| db | null | If set, client will run Redis `select` command on connect. |
|
||||
@@ -473,12 +461,6 @@ client.hgetall("hosts", function (err, obj) {
|
||||
});
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```js
|
||||
{ mjr: '1', another: '23', home: '1234' }
|
||||
```
|
||||
|
||||
### client.hmset(hash, obj[, callback])
|
||||
|
||||
Multiple values in a hash can be set by supplying an object:
|
||||
@@ -770,7 +752,7 @@ clients.alterer = clients.watcher.duplicate();
|
||||
clients.watcher.watch('foo',function(err) {
|
||||
if (err) { throw err; }
|
||||
//if you comment out the next line, the transaction will work
|
||||
clients.alterer.set('foo',Math.random(), (err) => {if (err) { throw err; }})
|
||||
clients.alterer.set('foo',Math.random(), (err) => {if (err) { throw err; }});
|
||||
|
||||
//using a setTimeout here to ensure that the MULTI/EXEC will come after the SET.
|
||||
//Normally, you would use a callback to ensure order, but I want the above SET command
|
||||
@@ -897,8 +879,8 @@ the second word as first parameter:
|
||||
|
||||
```js
|
||||
client.script('load', 'return 1');
|
||||
client.multi().script('load', 'return 1').exec(...);
|
||||
client.multi([['script', 'load', 'return 1']]).exec(...);
|
||||
client.multi().script('load', 'return 1').exec();
|
||||
client.multi([['script', 'load', 'return 1']]).exec();
|
||||
```
|
||||
|
||||
## client.duplicate([options][, callback])
|
||||
@@ -1082,6 +1064,7 @@ ReplyError: ERR wrong number of arguments for 'set' command
|
||||
```
|
||||
|
||||
## How to Contribute
|
||||
|
||||
- Open a pull request or an issue about what you want to implement / change. We're glad for any help!
|
||||
- Please be aware that we'll only accept fully tested code.
|
||||
|
||||
@@ -1097,23 +1080,3 @@ contributed to `node_redis` too. Thanks to all of them!
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
### Consolidation: It's time for celebration
|
||||
|
||||
Right now there are two great redis clients around and both have some advantages
|
||||
above each other. We speak about ioredis and node_redis. So after talking to
|
||||
each other about how we could improve in working together we (that is @luin and
|
||||
@BridgeAR) decided to work towards a single library on the long run. But step by
|
||||
step.
|
||||
|
||||
First of all, we want to split small parts of our libraries into others so that
|
||||
we're both able to use the same code. Those libraries are going to be maintained
|
||||
under the NodeRedis organization. This is going to reduce the maintenance
|
||||
overhead, allows others to use the very same code, if they need it and it's way
|
||||
easyer for others to contribute to both libraries.
|
||||
|
||||
We're very happy about this step towards working together as we both want to
|
||||
give you the best redis experience possible.
|
||||
|
||||
If you want to join our cause by help maintaining something, please don't
|
||||
hesitate to contact either one of us.
|
||||
|
@@ -3,7 +3,6 @@
|
||||
# Test against these versions of Node.js.
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "4"
|
||||
- nodejs_version: "6"
|
||||
- nodejs_version: "8"
|
||||
- nodejs_version: "10"
|
||||
|
141
index.js
141
index.js
@@ -67,38 +67,16 @@ function RedisClient (options, stream) {
|
||||
cnx_options.family = (!options.family && net.isIP(cnx_options.host)) || (options.family === 'IPv6' ? 6 : 4);
|
||||
this.address = cnx_options.host + ':' + cnx_options.port;
|
||||
}
|
||||
// Warn on misusing deprecated functions
|
||||
if (typeof options.retry_strategy === 'function') {
|
||||
if ('max_attempts' in options) {
|
||||
self.warn('WARNING: You activated the retry_strategy and max_attempts at the same time. This is not possible and max_attempts will be ignored.');
|
||||
// Do not print deprecation warnings twice
|
||||
delete options.max_attempts;
|
||||
}
|
||||
if ('retry_max_delay' in options) {
|
||||
self.warn('WARNING: You activated the retry_strategy and retry_max_delay at the same time. This is not possible and retry_max_delay will be ignored.');
|
||||
// Do not print deprecation warnings twice
|
||||
delete options.retry_max_delay;
|
||||
}
|
||||
}
|
||||
|
||||
this.connection_options = cnx_options;
|
||||
this.connection_id = RedisClient.connection_id++;
|
||||
this.connected = false;
|
||||
this.ready = false;
|
||||
if (options.socket_nodelay === undefined) {
|
||||
options.socket_nodelay = true;
|
||||
} else if (!options.socket_nodelay) { // Only warn users with this set to false
|
||||
self.warn(
|
||||
'socket_nodelay is deprecated and will be removed in v.3.0.0.\n' +
|
||||
'Setting socket_nodelay to false likely results in a reduced throughput. Please use .batch for pipelining instead.\n' +
|
||||
'If you are sure you rely on the NAGLE-algorithm you can activate it by calling client.stream.setNoDelay(false) instead.'
|
||||
);
|
||||
}
|
||||
if (options.socket_keepalive === undefined) {
|
||||
options.socket_keepalive = true;
|
||||
}
|
||||
if (options.socket_initialdelay === undefined) {
|
||||
options.socket_initialdelay = 0;
|
||||
if (options.socket_initial_delay === undefined) {
|
||||
options.socket_initial_delay = 0;
|
||||
// set default to 0, which is aligned to https://nodejs.org/api/net.html#net_socket_setkeepalive_enable_initialdelay
|
||||
}
|
||||
for (var command in options.rename_commands) {
|
||||
@@ -116,14 +94,6 @@ function RedisClient (options, stream) {
|
||||
this.handle_reply = handle_detect_buffers_reply;
|
||||
}
|
||||
this.should_buffer = false;
|
||||
this.max_attempts = options.max_attempts | 0;
|
||||
if ('max_attempts' in options) {
|
||||
self.warn(
|
||||
'max_attempts is deprecated and will be removed in v.3.0.0.\n' +
|
||||
'To reduce the number of options and to improve the reconnection handling please use the new `retry_strategy` option instead.\n' +
|
||||
'This replaces the max_attempts and retry_max_delay option.'
|
||||
);
|
||||
}
|
||||
this.command_queue = new Queue(); // Holds sent commands to de-pipeline them
|
||||
this.offline_queue = new Queue(); // Holds commands issued but not able to be sent
|
||||
this.pipeline_queue = new Queue(); // Holds all pipelined commands
|
||||
@@ -131,14 +101,6 @@ function RedisClient (options, stream) {
|
||||
// This should be done by the retry_strategy. Instead it should only be the timeout for connecting to redis
|
||||
this.connect_timeout = +options.connect_timeout || 3600000; // 60 * 60 * 1000 ms
|
||||
this.enable_offline_queue = options.enable_offline_queue === false ? false : true;
|
||||
this.retry_max_delay = +options.retry_max_delay || null;
|
||||
if ('retry_max_delay' in options) {
|
||||
self.warn(
|
||||
'retry_max_delay is deprecated and will be removed in v.3.0.0.\n' +
|
||||
'To reduce the amount of options and the improve the reconnection handling please use the new `retry_strategy` option instead.\n' +
|
||||
'This replaces the max_attempts and retry_max_delay option.'
|
||||
);
|
||||
}
|
||||
this.initialize_retry_vars();
|
||||
this.pub_sub_mode = 0;
|
||||
this.subscription_set = {};
|
||||
@@ -158,17 +120,7 @@ function RedisClient (options, stream) {
|
||||
this.create_stream();
|
||||
// The listeners will not be attached right away, so let's print the deprecation message while the listener is attached
|
||||
this.on('newListener', function (event) {
|
||||
if (event === 'idle') {
|
||||
this.warn(
|
||||
'The idle event listener is deprecated and will likely be removed in v.3.0.0.\n' +
|
||||
'If you rely on this feature please open a new ticket in node_redis with your use case'
|
||||
);
|
||||
} else if (event === 'drain') {
|
||||
this.warn(
|
||||
'The drain event listener is deprecated and will be removed in v.3.0.0.\n' +
|
||||
'If you want to keep on listening to this event please listen to the stream drain event directly.'
|
||||
);
|
||||
} else if ((event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.message_buffers) {
|
||||
if ((event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') && !this.buffers && !this.message_buffers) {
|
||||
this.reply_parser.optionReturnBuffers = true;
|
||||
this.message_buffers = true;
|
||||
this.handle_reply = handle_detect_buffers_reply;
|
||||
@@ -264,7 +216,6 @@ RedisClient.prototype.create_stream = function () {
|
||||
// The buffer_from_socket.toString() has a significant impact on big chunks and therefore this should only be used if necessary
|
||||
debug('Net read ' + self.address + ' id ' + self.connection_id); // + ': ' + buffer_from_socket.toString());
|
||||
self.reply_parser.execute(buffer_from_socket);
|
||||
self.emit_idle();
|
||||
});
|
||||
|
||||
this.stream.on('error', function (err) {
|
||||
@@ -283,9 +234,7 @@ RedisClient.prototype.create_stream = function () {
|
||||
self.drain();
|
||||
});
|
||||
|
||||
if (this.options.socket_nodelay) {
|
||||
this.stream.setNoDelay();
|
||||
}
|
||||
|
||||
// Fire the command before redis is connected to be sure it's the first fired command
|
||||
if (this.auth_pass !== undefined) {
|
||||
@@ -387,7 +336,7 @@ RedisClient.prototype.on_error = function (err) {
|
||||
this.connected = false;
|
||||
this.ready = false;
|
||||
|
||||
// Only emit the error if the retry_stategy option is not set
|
||||
// Only emit the error if the retry_strategy option is not set
|
||||
if (!this.options.retry_strategy) {
|
||||
this.emit('error', err);
|
||||
}
|
||||
@@ -402,7 +351,7 @@ RedisClient.prototype.on_connect = function () {
|
||||
this.connected = true;
|
||||
this.ready = false;
|
||||
this.emitted_end = false;
|
||||
this.stream.setKeepAlive(this.options.socket_keepalive, this.options.socket_initialdelay);
|
||||
this.stream.setKeepAlive(this.options.socket_keepalive, this.options.socket_initial_delay);
|
||||
this.stream.setTimeout(0);
|
||||
|
||||
this.emit('connect');
|
||||
@@ -607,18 +556,31 @@ RedisClient.prototype.connection_gone = function (why, error) {
|
||||
if (this.retry_delay instanceof Error) {
|
||||
error = this.retry_delay;
|
||||
}
|
||||
var errorMessage = 'Redis connection in broken state: ';
|
||||
if (this.retry_totaltime >= this.connect_timeout) {
|
||||
errorMessage += 'connection timeout exceeded.';
|
||||
} else {
|
||||
errorMessage += 'maximum connection attempts exceeded.';
|
||||
}
|
||||
|
||||
this.flush_and_error({
|
||||
message: 'Stream connection ended and command aborted.',
|
||||
code: 'NR_CLOSED'
|
||||
message: errorMessage,
|
||||
code: 'CONNECTION_BROKEN',
|
||||
}, {
|
||||
error: error
|
||||
});
|
||||
var retryError = new Error(errorMessage);
|
||||
retryError.code = 'CONNECTION_BROKEN';
|
||||
if (error) {
|
||||
retryError.origin = error;
|
||||
}
|
||||
this.end(false);
|
||||
this.emit('error', retryError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.max_attempts !== 0 && this.attempts >= this.max_attempts || this.retry_totaltime >= this.connect_timeout) {
|
||||
if (this.retry_totaltime >= this.connect_timeout) {
|
||||
var message = 'Redis connection in broken state: ';
|
||||
if (this.retry_totaltime >= this.connect_timeout) {
|
||||
message += 'connection timeout exceeded.';
|
||||
@@ -637,8 +599,8 @@ RedisClient.prototype.connection_gone = function (why, error) {
|
||||
if (error) {
|
||||
err.origin = error;
|
||||
}
|
||||
this.emit('error', err);
|
||||
this.end(false);
|
||||
this.emit('error', err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -656,15 +618,12 @@ RedisClient.prototype.connection_gone = function (why, error) {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.retry_max_delay !== null && this.retry_delay > this.retry_max_delay) {
|
||||
this.retry_delay = this.retry_max_delay;
|
||||
} else if (this.retry_totaltime + this.retry_delay > this.connect_timeout) {
|
||||
if (this.retry_totaltime + this.retry_delay > this.connect_timeout) {
|
||||
// Do not exceed the maximum
|
||||
this.retry_delay = this.connect_timeout - this.retry_totaltime;
|
||||
}
|
||||
|
||||
debug('Retry connection in ' + this.retry_delay + ' ms');
|
||||
|
||||
this.retry_timer = setTimeout(retry_connection, this.retry_delay, this, error);
|
||||
};
|
||||
|
||||
@@ -693,16 +652,9 @@ RedisClient.prototype.return_error = function (err) {
|
||||
};
|
||||
|
||||
RedisClient.prototype.drain = function () {
|
||||
this.emit('drain');
|
||||
this.should_buffer = false;
|
||||
};
|
||||
|
||||
RedisClient.prototype.emit_idle = function () {
|
||||
if (this.command_queue.length === 0 && this.pub_sub_mode === 0) {
|
||||
this.emit('idle');
|
||||
}
|
||||
};
|
||||
|
||||
function normal_reply (self, reply) {
|
||||
var command_obj = self.command_queue.shift();
|
||||
if (typeof command_obj.callback === 'function') {
|
||||
@@ -752,7 +704,7 @@ function subscribe_unsubscribe (self, reply, type) {
|
||||
self.command_queue.shift();
|
||||
if (typeof command_obj.callback === 'function') {
|
||||
// TODO: The current return value is pretty useless.
|
||||
// Evaluate to change this in v.3 to return all subscribed / unsubscribed channels in an array including the number of channels subscribed too
|
||||
// Evaluate to change this in v.4 to return all subscribed / unsubscribed channels in an array including the number of channels subscribed too
|
||||
command_obj.callback(null, channel);
|
||||
}
|
||||
self.sub_commands_left = 0;
|
||||
@@ -768,7 +720,7 @@ function subscribe_unsubscribe (self, reply, type) {
|
||||
function return_pub_sub (self, reply) {
|
||||
var type = reply[0].toString();
|
||||
if (type === 'message') { // channel, message
|
||||
if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter
|
||||
if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.4 to always return a string on the normal emitter
|
||||
self.emit('message', reply[1].toString(), reply[2].toString());
|
||||
self.emit('message_buffer', reply[1], reply[2]);
|
||||
self.emit('messageBuffer', reply[1], reply[2]);
|
||||
@@ -776,7 +728,7 @@ function return_pub_sub (self, reply) {
|
||||
self.emit('message', reply[1], reply[2]);
|
||||
}
|
||||
} else if (type === 'pmessage') { // pattern, channel, message
|
||||
if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.3 to always return a string on the normal emitter
|
||||
if (!self.options.return_buffers || self.message_buffers) { // backwards compatible. Refactor this in v.4 to always return a string on the normal emitter
|
||||
self.emit('pmessage', reply[1].toString(), reply[2].toString(), reply[3].toString());
|
||||
self.emit('pmessage_buffer', reply[1], reply[2], reply[3]);
|
||||
self.emit('pmessageBuffer', reply[1], reply[2], reply[3]);
|
||||
@@ -884,32 +836,39 @@ RedisClient.prototype.internal_send_command = function (command_obj) {
|
||||
} else if (typeof args[i] === 'object') { // Checking for object instead of Buffer.isBuffer helps us finding data types that we can't handle properly
|
||||
if (args[i] instanceof Date) { // Accept dates as valid input
|
||||
args_copy[i] = args[i].toString();
|
||||
} else if (args[i] === null) {
|
||||
this.warn(
|
||||
'Deprecated: The ' + command.toUpperCase() + ' command contains a "null" argument.\n' +
|
||||
'This is converted to a "null" string now and will return an error from v.3.0 on.\n' +
|
||||
'Please handle this in your code to make sure everything works as you intended it to.'
|
||||
);
|
||||
args_copy[i] = 'null'; // Backwards compatible :/
|
||||
} else if (Buffer.isBuffer(args[i])) {
|
||||
args_copy[i] = args[i];
|
||||
command_obj.buffer_args = true;
|
||||
big_data = true;
|
||||
} else {
|
||||
this.warn(
|
||||
'Deprecated: The ' + command.toUpperCase() + ' command contains a argument of type ' + args[i].constructor.name + '.\n' +
|
||||
'This is converted to "' + args[i].toString() + '" by using .toString() now and will return an error from v.3.0 on.\n' +
|
||||
'Please handle this in your code to make sure everything works as you intended it to.'
|
||||
var invalidArgError = new Error(
|
||||
'node_redis: The ' + command.toUpperCase() + ' command contains a invalid argument type.\n' +
|
||||
'Only strings, dates and buffers are accepted. Please update your code to use valid argument types.'
|
||||
);
|
||||
args_copy[i] = args[i].toString(); // Backwards compatible :/
|
||||
invalidArgError.command = command_obj.command.toUpperCase();
|
||||
if (command_obj.args && command_obj.args.length) {
|
||||
invalidArgError.args = command_obj.args;
|
||||
}
|
||||
if (command_obj.callback) {
|
||||
command_obj.callback(invalidArgError);
|
||||
return false;
|
||||
}
|
||||
throw invalidArgError;
|
||||
}
|
||||
} else if (typeof args[i] === 'undefined') {
|
||||
this.warn(
|
||||
'Deprecated: The ' + command.toUpperCase() + ' command contains a "undefined" argument.\n' +
|
||||
'This is converted to a "undefined" string now and will return an error from v.3.0 on.\n' +
|
||||
'Please handle this in your code to make sure everything works as you intended it to.'
|
||||
var undefinedArgError = new Error(
|
||||
'node_redis: The ' + command.toUpperCase() + ' command contains a invalid argument type of "undefined".\n' +
|
||||
'Only strings, dates and buffers are accepted. Please update your code to use valid argument types.'
|
||||
);
|
||||
args_copy[i] = 'undefined'; // Backwards compatible :/
|
||||
undefinedArgError.command = command_obj.command.toUpperCase();
|
||||
if (command_obj.args && command_obj.args.length) {
|
||||
undefinedArgError.args = command_obj.args;
|
||||
}
|
||||
if (command_obj.callback) {
|
||||
command_obj.callback(undefinedArgError);
|
||||
return false;
|
||||
}
|
||||
throw undefinedArgError;
|
||||
} else {
|
||||
// Seems like numbers are converted fast using string concatenation
|
||||
args_copy[i] = '' + args[i];
|
||||
|
40
package.json
40
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "redis",
|
||||
"version": "2.8.0",
|
||||
"description": "Redis client library",
|
||||
"description": "A high performance Redis client.",
|
||||
"keywords": [
|
||||
"database",
|
||||
"redis",
|
||||
@@ -14,6 +14,16 @@
|
||||
"backpressure"
|
||||
],
|
||||
"author": "Matt Ranney <mjr@ranney.com>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Mike Diarmid (Salakar)",
|
||||
"url": "https://github.com/salakar"
|
||||
},
|
||||
{
|
||||
"name": "Ruben Bridgewater (BridgeAR)",
|
||||
"url": "https://github.com/BridgeAR"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "./index.js",
|
||||
"scripts": {
|
||||
@@ -31,30 +41,34 @@
|
||||
"redis-parser": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
"node": ">=6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bluebird": "^3.0.2",
|
||||
"bluebird": "^3.7.2",
|
||||
"coveralls": "^2.11.2",
|
||||
"eslint": "^4.2.0",
|
||||
"eslint": "^6.8.0",
|
||||
"intercept-stdout": "~0.1.2",
|
||||
"metrics": "^0.1.9",
|
||||
"mocha": "^3.1.2",
|
||||
"nyc": "^10.0.0",
|
||||
"tcp-port-used": "^0.1.2",
|
||||
"uuid": "^2.0.1",
|
||||
"win-spawn": "^2.0.0"
|
||||
"metrics": "^0.1.21",
|
||||
"mocha": "^4.1.0",
|
||||
"nyc": "^14.1.1",
|
||||
"tcp-port-used": "^1.0.1",
|
||||
"uuid": "^3.4.0",
|
||||
"cross-spawn": "^6.0.5"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/NodeRedis/node_redis.git"
|
||||
"url": "git://github.com/NodeRedis/node-redis.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/NodeRedis/node_redis/issues"
|
||||
"url": "https://github.com/NodeRedis/node-redis/issues"
|
||||
},
|
||||
"homepage": "https://github.com/NodeRedis/node_redis",
|
||||
"homepage": "https://github.com/NodeRedis/node-redis",
|
||||
"directories": {
|
||||
"example": "examples",
|
||||
"test": "test"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-redis"
|
||||
}
|
||||
}
|
||||
|
@@ -192,7 +192,7 @@ describe('client authentication', function () {
|
||||
|
||||
client = redis.createClient.apply(null, args);
|
||||
var async = true;
|
||||
client.auth(undefined, function (err, res) {
|
||||
client.auth('undefined', function (err, res) {
|
||||
assert.strictEqual(err.message, 'ERR invalid password');
|
||||
assert.strictEqual(err.command, 'AUTH');
|
||||
assert.strictEqual(res, undefined);
|
||||
|
@@ -45,22 +45,16 @@ describe("The 'hset' method", function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('warns if someone passed a array either as field or as value', function (done) {
|
||||
it('errors if someone passed a array either as field or as value', function (done) {
|
||||
var hash = 'test hash';
|
||||
var field = 'array';
|
||||
// This would be converted to "array contents" but if you use more than one entry,
|
||||
// it'll result in e.g. "array contents,second content" and this is not supported and considered harmful
|
||||
var value = ['array contents'];
|
||||
client.on('warning', function (msg) {
|
||||
assert.strictEqual(
|
||||
msg,
|
||||
'Deprecated: The HMSET command contains a argument of type Array.\n' +
|
||||
'This is converted to "array contents" by using .toString() now and will return an error from v.3.0 on.\n' +
|
||||
'Please handle this in your code to make sure everything works as you intended it to.'
|
||||
);
|
||||
done();
|
||||
});
|
||||
try {
|
||||
client.HMSET(hash, field, value);
|
||||
} catch (error) {
|
||||
assert(/node_redis: The HMSET command contains a invalid argument type./.test(error.message));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not error when a buffer and date are set as values on the same hash', function (done) {
|
||||
|
@@ -140,10 +140,14 @@ describe("The 'set' method", function () {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: This test has to be refactored from v.3.0 on to expect an error instead
|
||||
it("converts null to 'null'", function (done) {
|
||||
it('errors if null value is passed', function (done) {
|
||||
try {
|
||||
client.set('foo', null);
|
||||
client.get('foo', helper.isString('null', done));
|
||||
assert(false);
|
||||
} catch (error) {
|
||||
assert(/The SET command contains a invalid argument type./.test(error.message));
|
||||
}
|
||||
client.get('foo', helper.isNull(done));
|
||||
});
|
||||
|
||||
it('emit an error with only the key set', function (done) {
|
||||
|
@@ -152,40 +152,6 @@ describe('connection tests', function () {
|
||||
describe('using ' + parser + ' and ' + ip, function () {
|
||||
|
||||
describe('on lost connection', function () {
|
||||
it('emit an error after max retry attempts and do not try to reconnect afterwards', function (done) {
|
||||
var maxAttempts = 3;
|
||||
var options = {
|
||||
parser: parser,
|
||||
maxAttempts: maxAttempts
|
||||
};
|
||||
client = redis.createClient(options);
|
||||
assert.strictEqual(client.retryBackoff, 1.7);
|
||||
assert.strictEqual(client.retryDelay, 200);
|
||||
assert.strictEqual(Object.keys(options).length, 2);
|
||||
var calls = 0;
|
||||
|
||||
client.once('ready', function () {
|
||||
helper.killConnection(client);
|
||||
});
|
||||
|
||||
client.on('reconnecting', function (params) {
|
||||
calls++;
|
||||
});
|
||||
|
||||
client.on('error', function (err) {
|
||||
if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) {
|
||||
process.nextTick(function () { // End is called after the error got emitted
|
||||
assert.strictEqual(calls, maxAttempts - 1);
|
||||
assert.strictEqual(client.emitted_end, true);
|
||||
assert.strictEqual(client.connected, false);
|
||||
assert.strictEqual(client.ready, false);
|
||||
assert.strictEqual(client.closing, true);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('emit an error after max retry timeout and do not try to reconnect afterwards', function (done) {
|
||||
// TODO: Investigate why this test fails with windows. Reconnect is only triggered once
|
||||
if (process.platform === 'win32') this.skip();
|
||||
@@ -271,16 +237,11 @@ describe('connection tests', function () {
|
||||
});
|
||||
|
||||
it('retryStrategy used to reconnect with individual error', function (done) {
|
||||
var text = '';
|
||||
var unhookIntercept = intercept(function (data) {
|
||||
text += data;
|
||||
return '';
|
||||
});
|
||||
client = redis.createClient({
|
||||
retryStrategy: function (options) {
|
||||
if (options.totalRetryTime > 150) {
|
||||
client.set('foo', 'bar', function (err, res) {
|
||||
assert.strictEqual(err.message, 'Stream connection ended and command aborted.');
|
||||
assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.');
|
||||
assert.strictEqual(err.origin.message, 'Connection timeout');
|
||||
done();
|
||||
});
|
||||
@@ -289,18 +250,8 @@ describe('connection tests', function () {
|
||||
}
|
||||
return Math.min(options.attempt * 25, 200);
|
||||
},
|
||||
maxAttempts: 5,
|
||||
retryMaxDelay: 123,
|
||||
port: 9999
|
||||
});
|
||||
process.nextTick(function () {
|
||||
assert.strictEqual(
|
||||
text,
|
||||
'node_redis: WARNING: You activated the retry_strategy and max_attempts at the same time. This is not possible and max_attempts will be ignored.\n' +
|
||||
'node_redis: WARNING: You activated the retry_strategy and retry_max_delay at the same time. This is not possible and retry_max_delay will be ignored.\n'
|
||||
);
|
||||
unhookIntercept();
|
||||
});
|
||||
});
|
||||
|
||||
it('retry_strategy used to reconnect', function (done) {
|
||||
@@ -308,8 +259,8 @@ describe('connection tests', function () {
|
||||
retry_strategy: function (options) {
|
||||
if (options.total_retry_time > 150) {
|
||||
client.set('foo', 'bar', function (err, res) {
|
||||
assert.strictEqual(err.message, 'Stream connection ended and command aborted.');
|
||||
assert.strictEqual(err.code, 'NR_CLOSED');
|
||||
assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.');
|
||||
assert.strictEqual(err.code, 'CONNECTION_BROKEN');
|
||||
assert.strictEqual(err.origin.code, 'ECONNREFUSED');
|
||||
done();
|
||||
});
|
||||
@@ -337,11 +288,13 @@ describe('connection tests', function () {
|
||||
client.stream.destroy();
|
||||
}, 50);
|
||||
client.on('error', function (err) {
|
||||
assert.strictEqual(err.code, 'NR_CLOSED');
|
||||
assert.strictEqual(err.message, 'Stream connection ended and command aborted.');
|
||||
if (err instanceof redis.AbortError) {
|
||||
assert.strictEqual(err.message, 'Redis connection in broken state: maximum connection attempts exceeded.');
|
||||
assert.strictEqual(err.code, 'CONNECTION_BROKEN');
|
||||
unhookIntercept();
|
||||
redis.debugMode = false;
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -4,7 +4,7 @@
|
||||
var config = require('./config');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var spawn = require('win-spawn');
|
||||
var spawn = require('cross-spawn');
|
||||
var tcpPortUsed = require('tcp-port-used');
|
||||
var bluebird = require('bluebird');
|
||||
|
||||
|
@@ -219,17 +219,22 @@ describe("The 'multi' method", function () {
|
||||
client = redis.createClient({
|
||||
host: 'somewhere',
|
||||
port: 6379,
|
||||
max_attempts: 1
|
||||
retry_strategy: function (options) {
|
||||
if (options.attempt > 1) {
|
||||
// End reconnecting with built in error
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.on('error', function (err) {
|
||||
if (/Redis connection in broken state/.test(err.message)) {
|
||||
if (/Redis connection to somewhere:6379 failed/.test(err.origin.message)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
client.multi([['set', 'foo', 'bar'], ['get', 'foo']]).exec(function (err, res) {
|
||||
assert(/Redis connection in broken state/.test(err.message));
|
||||
assert(/Redis connection in broken state: maximum connection attempts exceeded/.test(err.message));
|
||||
assert.strictEqual(err.errors.length, 2);
|
||||
assert.strictEqual(err.errors[0].args.length, 2);
|
||||
});
|
||||
@@ -239,12 +244,17 @@ describe("The 'multi' method", function () {
|
||||
client = redis.createClient({
|
||||
host: 'somewhere',
|
||||
port: 6379,
|
||||
max_attempts: 1
|
||||
retry_strategy: function (options) {
|
||||
if (options.attempt > 1) {
|
||||
// End reconnecting with built in error
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.on('error', function (err) {
|
||||
// Results in multiple done calls if test fails
|
||||
if (/Redis connection in broken state/.test(err.message)) {
|
||||
if (/Redis connection to somewhere:6379 failed/.test(err.origin.message)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
@@ -707,25 +707,6 @@ describe('The node_redis client', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('idle', function () {
|
||||
it('emits idle as soon as there are no outstanding commands', function (done) {
|
||||
var end = helper.callFuncAfter(done, 2);
|
||||
client.on('warning', function (msg) {
|
||||
assert.strictEqual(
|
||||
msg,
|
||||
'The idle event listener is deprecated and will likely be removed in v.3.0.0.\n' +
|
||||
'If you rely on this feature please open a new ticket in node_redis with your use case'
|
||||
);
|
||||
end();
|
||||
});
|
||||
client.on('idle', function onIdle () {
|
||||
client.removeListener('idle', onIdle);
|
||||
client.get('foo', helper.isString('bar', end));
|
||||
});
|
||||
client.set('foo', 'bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('utf8', function () {
|
||||
it('handles utf-8 keys', function (done) {
|
||||
var utf8_sample = 'ಠ_ಠ';
|
||||
@@ -793,111 +774,6 @@ describe('The node_redis client', function () {
|
||||
// });
|
||||
});
|
||||
|
||||
describe('socket_nodelay', function () {
|
||||
describe('true', function () {
|
||||
var args = config.configureClient(parser, ip, {
|
||||
socket_nodelay: true
|
||||
});
|
||||
|
||||
it("fires client.on('ready')", function (done) {
|
||||
client = redis.createClient.apply(null, args);
|
||||
client.on('ready', function () {
|
||||
assert.strictEqual(true, client.options.socket_nodelay);
|
||||
client.quit(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('client is functional', function (done) {
|
||||
client = redis.createClient.apply(null, args);
|
||||
client.on('ready', function () {
|
||||
assert.strictEqual(true, client.options.socket_nodelay);
|
||||
client.set(['set key 1', 'set val'], helper.isString('OK'));
|
||||
client.set(['set key 2', 'set val'], helper.isString('OK'));
|
||||
client.get(['set key 1'], helper.isString('set val'));
|
||||
client.get(['set key 2'], helper.isString('set val'));
|
||||
client.quit(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('false', function () {
|
||||
var args = config.configureClient(parser, ip, {
|
||||
socket_nodelay: false
|
||||
});
|
||||
|
||||
it("fires client.on('ready')", function (done) {
|
||||
client = redis.createClient.apply(null, args);
|
||||
client.on('ready', function () {
|
||||
assert.strictEqual(false, client.options.socket_nodelay);
|
||||
client.quit(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('client is functional', function (done) {
|
||||
client = redis.createClient.apply(null, args);
|
||||
client.on('ready', function () {
|
||||
assert.strictEqual(false, client.options.socket_nodelay);
|
||||
client.set(['set key 1', 'set val'], helper.isString('OK'));
|
||||
client.set(['set key 2', 'set val'], helper.isString('OK'));
|
||||
client.get(['set key 1'], helper.isString('set val'));
|
||||
client.get(['set key 2'], helper.isString('set val'));
|
||||
client.quit(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaults to true', function () {
|
||||
|
||||
it("fires client.on('ready')", function (done) {
|
||||
client = redis.createClient.apply(null, args);
|
||||
client.on('ready', function () {
|
||||
assert.strictEqual(true, client.options.socket_nodelay);
|
||||
client.quit(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('client is functional', function (done) {
|
||||
client = redis.createClient.apply(null, args);
|
||||
client.on('ready', function () {
|
||||
assert.strictEqual(true, client.options.socket_nodelay);
|
||||
client.set(['set key 1', 'set val'], helper.isString('OK'));
|
||||
client.set(['set key 2', 'set val'], helper.isString('OK'));
|
||||
client.get(['set key 1'], helper.isString('set val'));
|
||||
client.get(['set key 2'], helper.isString('set val'));
|
||||
client.quit(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('retry_max_delay', function () {
|
||||
it('sets upper bound on how long client waits before reconnecting', function (done) {
|
||||
var time;
|
||||
var timeout = process.platform !== 'win32' ? 20 : 100;
|
||||
|
||||
client = redis.createClient.apply(null, config.configureClient(parser, ip, {
|
||||
retry_max_delay: 1 // ms
|
||||
}));
|
||||
client.on('ready', function () {
|
||||
if (this.times_connected === 1) {
|
||||
this.stream.end();
|
||||
time = Date.now();
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
client.on('reconnecting', function () {
|
||||
time = Date.now() - time;
|
||||
assert(time < timeout, 'The reconnect should not have taken longer than ' + timeout + ' but it took ' + time);
|
||||
});
|
||||
client.on('error', function (err) {
|
||||
// This is rare but it might be triggered.
|
||||
// So let's have a robust test
|
||||
assert.strictEqual(err.code, 'ECONNRESET');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('protocol error', function () {
|
||||
|
||||
it('should gracefully recover and only fail on the already send commands', function (done) {
|
||||
@@ -932,33 +808,9 @@ describe('The node_redis client', function () {
|
||||
|
||||
describe('enable_offline_queue', function () {
|
||||
describe('true', function () {
|
||||
it('should emit drain if offline queue is flushed and nothing to buffer', function (done) {
|
||||
client = redis.createClient({
|
||||
parser: parser,
|
||||
no_ready_check: true
|
||||
});
|
||||
var end = helper.callFuncAfter(done, 3);
|
||||
client.set('foo', 'bar');
|
||||
client.get('foo', end);
|
||||
client.on('warning', function (msg) {
|
||||
assert.strictEqual(
|
||||
msg,
|
||||
'The drain event listener is deprecated and will be removed in v.3.0.0.\n' +
|
||||
'If you want to keep on listening to this event please listen to the stream drain event directly.'
|
||||
);
|
||||
end();
|
||||
});
|
||||
client.on('drain', function () {
|
||||
assert(client.offline_queue.length === 0);
|
||||
end();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not return an error and enqueues operation', function (done) {
|
||||
client = redis.createClient(9999, null, {
|
||||
max_attempts: 0,
|
||||
parser: parser
|
||||
});
|
||||
client = redis.createClient(9999, null);
|
||||
var finished = false;
|
||||
client.on('error', function (e) {
|
||||
// ignore, b/c expecting a "can't connect" error
|
||||
@@ -980,8 +832,12 @@ describe('The node_redis client', function () {
|
||||
|
||||
it('enqueues operation and keep the queue while trying to reconnect', function (done) {
|
||||
client = redis.createClient(9999, null, {
|
||||
max_attempts: 4,
|
||||
parser: parser
|
||||
retry_strategy: function (options) {
|
||||
if (options.attempt > 4) {
|
||||
return undefined;
|
||||
}
|
||||
return 100;
|
||||
},
|
||||
});
|
||||
var i = 0;
|
||||
|
||||
|
Reference in New Issue
Block a user