ioredis-om 5.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1571 -0
- package/built/Command.d.ts +166 -0
- package/built/Command.js +450 -0
- package/built/DataHandler.d.ts +37 -0
- package/built/DataHandler.js +224 -0
- package/built/Pipeline.d.ts +31 -0
- package/built/Pipeline.js +342 -0
- package/built/Redis.d.ts +243 -0
- package/built/Redis.js +800 -0
- package/built/ScanStream.d.ts +23 -0
- package/built/ScanStream.js +51 -0
- package/built/Script.d.ts +11 -0
- package/built/Script.js +62 -0
- package/built/SubscriptionSet.d.ts +14 -0
- package/built/SubscriptionSet.js +41 -0
- package/built/autoPipelining.d.ts +8 -0
- package/built/autoPipelining.js +167 -0
- package/built/cluster/ClusterOptions.d.ts +172 -0
- package/built/cluster/ClusterOptions.js +22 -0
- package/built/cluster/ClusterSubscriber.d.ts +29 -0
- package/built/cluster/ClusterSubscriber.js +223 -0
- package/built/cluster/ClusterSubscriberGroup.d.ts +108 -0
- package/built/cluster/ClusterSubscriberGroup.js +373 -0
- package/built/cluster/ConnectionPool.d.ts +37 -0
- package/built/cluster/ConnectionPool.js +154 -0
- package/built/cluster/DelayQueue.d.ts +20 -0
- package/built/cluster/DelayQueue.js +53 -0
- package/built/cluster/ShardedSubscriber.d.ts +36 -0
- package/built/cluster/ShardedSubscriber.js +147 -0
- package/built/cluster/index.d.ts +163 -0
- package/built/cluster/index.js +937 -0
- package/built/cluster/util.d.ts +25 -0
- package/built/cluster/util.js +100 -0
- package/built/connectors/AbstractConnector.d.ts +12 -0
- package/built/connectors/AbstractConnector.js +26 -0
- package/built/connectors/ConnectorConstructor.d.ts +5 -0
- package/built/connectors/ConnectorConstructor.js +2 -0
- package/built/connectors/SentinelConnector/FailoverDetector.d.ts +11 -0
- package/built/connectors/SentinelConnector/FailoverDetector.js +45 -0
- package/built/connectors/SentinelConnector/SentinelIterator.d.ts +13 -0
- package/built/connectors/SentinelConnector/SentinelIterator.js +37 -0
- package/built/connectors/SentinelConnector/index.d.ts +72 -0
- package/built/connectors/SentinelConnector/index.js +305 -0
- package/built/connectors/SentinelConnector/types.d.ts +21 -0
- package/built/connectors/SentinelConnector/types.js +2 -0
- package/built/connectors/StandaloneConnector.d.ts +17 -0
- package/built/connectors/StandaloneConnector.js +69 -0
- package/built/connectors/index.d.ts +3 -0
- package/built/connectors/index.js +7 -0
- package/built/constants/TLSProfiles.d.ts +9 -0
- package/built/constants/TLSProfiles.js +149 -0
- package/built/errors/ClusterAllFailedError.d.ts +7 -0
- package/built/errors/ClusterAllFailedError.js +15 -0
- package/built/errors/MaxRetriesPerRequestError.d.ts +5 -0
- package/built/errors/MaxRetriesPerRequestError.js +14 -0
- package/built/errors/index.d.ts +2 -0
- package/built/errors/index.js +5 -0
- package/built/index.d.ts +44 -0
- package/built/index.js +62 -0
- package/built/redis/RedisOptions.d.ts +197 -0
- package/built/redis/RedisOptions.js +58 -0
- package/built/redis/event_handler.d.ts +4 -0
- package/built/redis/event_handler.js +315 -0
- package/built/tracing.d.ts +26 -0
- package/built/tracing.js +96 -0
- package/built/transaction.d.ts +13 -0
- package/built/transaction.js +100 -0
- package/built/types.d.ts +33 -0
- package/built/types.js +2 -0
- package/built/utils/Commander.d.ts +50 -0
- package/built/utils/Commander.js +117 -0
- package/built/utils/RedisCommander.d.ts +8950 -0
- package/built/utils/RedisCommander.js +7 -0
- package/built/utils/applyMixin.d.ts +3 -0
- package/built/utils/applyMixin.js +8 -0
- package/built/utils/argumentParsers.d.ts +14 -0
- package/built/utils/argumentParsers.js +74 -0
- package/built/utils/debug.d.ts +16 -0
- package/built/utils/debug.js +95 -0
- package/built/utils/index.d.ts +124 -0
- package/built/utils/index.js +332 -0
- package/built/utils/lodash.d.ts +4 -0
- package/built/utils/lodash.js +9 -0
- package/package.json +103 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { CommanderOptions } from "../utils/Commander";
|
|
2
|
+
import ConnectorConstructor from "../connectors/ConnectorConstructor";
|
|
3
|
+
import { SentinelConnectionOptions } from "../connectors/SentinelConnector";
|
|
4
|
+
import { StandaloneConnectionOptions } from "../connectors/StandaloneConnector";
|
|
5
|
+
export declare type ReconnectOnError = (err: Error) => boolean | 1 | 2;
|
|
6
|
+
export interface CommonRedisOptions extends CommanderOptions {
|
|
7
|
+
Connector?: ConnectorConstructor | undefined;
|
|
8
|
+
retryStrategy?: ((times: number) => number | void | null) | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* If a command does not return a reply within a set number of milliseconds,
|
|
11
|
+
* a "Command timed out" error will be thrown.
|
|
12
|
+
*/
|
|
13
|
+
commandTimeout?: number | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Enables client-side timeout protection for blocking commands when set
|
|
16
|
+
* to a positive number. If `blockingTimeout` is undefined, `0`, or
|
|
17
|
+
* negative (e.g. `-1`), the protection is disabled and no client-side
|
|
18
|
+
* timers are installed for blocking commands.
|
|
19
|
+
*/
|
|
20
|
+
blockingTimeout?: number | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Grace period (ms) added to blocking command timeouts. Only used when
|
|
23
|
+
* `blockingTimeout` is a positive number. Defaults to 100ms.
|
|
24
|
+
*/
|
|
25
|
+
blockingTimeoutGrace?: number | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* If the socket does not receive data within a set number of milliseconds:
|
|
28
|
+
* 1. the socket is considered "dead" and will be destroyed
|
|
29
|
+
* 2. the client will reject any running commands (altought they might have been processed by the server)
|
|
30
|
+
* 3. the reconnect strategy will kick in (depending on the configuration)
|
|
31
|
+
*/
|
|
32
|
+
socketTimeout?: number | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Enable/disable keep-alive functionality.
|
|
35
|
+
* @link https://nodejs.org/api/net.html#socketsetkeepaliveenable-initialdelay
|
|
36
|
+
* @default 0
|
|
37
|
+
*/
|
|
38
|
+
keepAlive?: number | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Enable/disable the use of Nagle's algorithm.
|
|
41
|
+
* @link https://nodejs.org/api/net.html#socketsetnodelaynodelay
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
noDelay?: boolean | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Set the name of the connection to make it easier to identity the connection
|
|
47
|
+
* in client list.
|
|
48
|
+
* @link https://redis.io/commands/client-setname
|
|
49
|
+
*/
|
|
50
|
+
connectionName?: string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* If true, skips setting library info via CLIENT SETINFO.
|
|
53
|
+
* @link https://redis.io/docs/latest/commands/client-setinfo/
|
|
54
|
+
* @default false
|
|
55
|
+
*/
|
|
56
|
+
disableClientInfo?: boolean | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Tag to append to the library name in CLIENT SETINFO (ioredis-om(tag)).
|
|
59
|
+
* @link https://redis.io/docs/latest/commands/client-setinfo/
|
|
60
|
+
* @default undefined
|
|
61
|
+
*/
|
|
62
|
+
clientInfoTag?: string | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* If set, client will send AUTH command with the value of this option as the first argument when connected.
|
|
65
|
+
* This is supported since Redis 6.
|
|
66
|
+
*/
|
|
67
|
+
username?: string | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* If set, client will send AUTH command with the value of this option when connected.
|
|
70
|
+
*/
|
|
71
|
+
password?: string | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Database index to use.
|
|
74
|
+
*
|
|
75
|
+
* @default 0
|
|
76
|
+
*/
|
|
77
|
+
db?: number | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* When the client reconnects, channels subscribed in the previous connection will be
|
|
80
|
+
* resubscribed automatically if `autoResubscribe` is `true`.
|
|
81
|
+
* @default true
|
|
82
|
+
*/
|
|
83
|
+
autoResubscribe?: boolean | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Whether or not to resend unfulfilled commands on reconnect.
|
|
86
|
+
* Unfulfilled commands are most likely to be blocking commands such as `brpop` or `blpop`.
|
|
87
|
+
* @default true
|
|
88
|
+
*/
|
|
89
|
+
autoResendUnfulfilledCommands?: boolean | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* Whether or not to reconnect on certain Redis errors.
|
|
92
|
+
* This options by default is `null`, which means it should never reconnect on Redis errors.
|
|
93
|
+
* You can pass a function that accepts an Redis error, and returns:
|
|
94
|
+
* - `true` or `1` to trigger a reconnection.
|
|
95
|
+
* - `false` or `0` to not reconnect.
|
|
96
|
+
* - `2` to reconnect and resend the failed command (who triggered the error) after reconnection.
|
|
97
|
+
* @example
|
|
98
|
+
* ```js
|
|
99
|
+
* const redis = new Redis({
|
|
100
|
+
* reconnectOnError(err) {
|
|
101
|
+
* const targetError = "READONLY";
|
|
102
|
+
* if (err.message.includes(targetError)) {
|
|
103
|
+
* // Only reconnect when the error contains "READONLY"
|
|
104
|
+
* return true; // or `return 1;`
|
|
105
|
+
* }
|
|
106
|
+
* },
|
|
107
|
+
* });
|
|
108
|
+
* ```
|
|
109
|
+
* @default null
|
|
110
|
+
*/
|
|
111
|
+
reconnectOnError?: ReconnectOnError | null | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* @default false
|
|
114
|
+
*/
|
|
115
|
+
readOnly?: boolean | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* When enabled, numbers returned by Redis will be converted to JavaScript strings instead of numbers.
|
|
118
|
+
* This is necessary if you want to handle big numbers (above `Number.MAX_SAFE_INTEGER` === 2^53).
|
|
119
|
+
* @default false
|
|
120
|
+
*/
|
|
121
|
+
stringNumbers?: boolean | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* How long the client will wait before killing a socket due to inactivity during initial connection.
|
|
124
|
+
* @default 10000
|
|
125
|
+
*/
|
|
126
|
+
connectTimeout?: number | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* This option is used internally when you call `redis.monitor()` to tell Redis
|
|
129
|
+
* to enter the monitor mode when the connection is established.
|
|
130
|
+
*
|
|
131
|
+
* @default false
|
|
132
|
+
*/
|
|
133
|
+
monitor?: boolean | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* The commands that don't get a reply due to the connection to the server is lost are
|
|
136
|
+
* put into a queue and will be resent on reconnect (if allowed by the `retryStrategy` option).
|
|
137
|
+
* This option is used to configure how many reconnection attempts should be allowed before
|
|
138
|
+
* the queue is flushed with a `MaxRetriesPerRequestError` error.
|
|
139
|
+
* Set this options to `null` instead of a number to let commands wait forever
|
|
140
|
+
* until the connection is alive again.
|
|
141
|
+
*
|
|
142
|
+
* @default 20
|
|
143
|
+
*/
|
|
144
|
+
maxRetriesPerRequest?: number | null | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* @default 10000
|
|
147
|
+
*/
|
|
148
|
+
maxLoadingRetryTime?: number | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* @default false
|
|
151
|
+
*/
|
|
152
|
+
enableAutoPipelining?: boolean | undefined;
|
|
153
|
+
/**
|
|
154
|
+
* @default []
|
|
155
|
+
*/
|
|
156
|
+
autoPipeliningIgnoredCommands?: string[] | undefined;
|
|
157
|
+
offlineQueue?: boolean | undefined;
|
|
158
|
+
commandQueue?: boolean | undefined;
|
|
159
|
+
/**
|
|
160
|
+
*
|
|
161
|
+
* By default, if the connection to Redis server has not been established, commands are added to a queue
|
|
162
|
+
* and are executed once the connection is "ready" (when `enableReadyCheck` is true, "ready" means
|
|
163
|
+
* the Redis server has loaded the database from disk, otherwise means the connection to the Redis
|
|
164
|
+
* server has been established). If this option is false, when execute the command when the connection
|
|
165
|
+
* isn't ready, an error will be returned.
|
|
166
|
+
*
|
|
167
|
+
* @default true
|
|
168
|
+
*/
|
|
169
|
+
enableOfflineQueue?: boolean | undefined;
|
|
170
|
+
/**
|
|
171
|
+
* The client will sent an INFO command to check whether the server is still loading data from the disk (
|
|
172
|
+
* which happens when the server is just launched) when the connection is established, and only wait until
|
|
173
|
+
* the loading process is finished before emitting the `ready` event.
|
|
174
|
+
*
|
|
175
|
+
* @default true
|
|
176
|
+
*/
|
|
177
|
+
enableReadyCheck?: boolean | undefined;
|
|
178
|
+
/**
|
|
179
|
+
* When a Redis instance is initialized, a connection to the server is immediately established. Set this to
|
|
180
|
+
* true will delay the connection to the server until the first command is sent or `redis.connect()` is called
|
|
181
|
+
* explicitly. When `redis.connect()` is called explicitly, a Promise is returned, which will be resolved
|
|
182
|
+
* when the connection is ready or rejected when it fails. The rejection should be handled by the user.
|
|
183
|
+
*
|
|
184
|
+
* @default false
|
|
185
|
+
*/
|
|
186
|
+
lazyConnect?: boolean | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* @default undefined
|
|
189
|
+
*/
|
|
190
|
+
scripts?: Record<string, {
|
|
191
|
+
lua: string;
|
|
192
|
+
numberOfKeys?: number | undefined;
|
|
193
|
+
readOnly?: boolean | undefined;
|
|
194
|
+
}> | undefined;
|
|
195
|
+
}
|
|
196
|
+
export declare type RedisOptions = CommonRedisOptions & SentinelConnectionOptions & StandaloneConnectionOptions;
|
|
197
|
+
export declare const DEFAULT_REDIS_OPTIONS: RedisOptions;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_REDIS_OPTIONS = void 0;
|
|
4
|
+
exports.DEFAULT_REDIS_OPTIONS = {
|
|
5
|
+
// Connection
|
|
6
|
+
port: 6379,
|
|
7
|
+
host: "localhost",
|
|
8
|
+
family: 0,
|
|
9
|
+
connectTimeout: 10000,
|
|
10
|
+
disconnectTimeout: 2000,
|
|
11
|
+
retryStrategy: function (times) {
|
|
12
|
+
return Math.min(times * 50, 2000);
|
|
13
|
+
},
|
|
14
|
+
keepAlive: 0,
|
|
15
|
+
noDelay: true,
|
|
16
|
+
connectionName: null,
|
|
17
|
+
disableClientInfo: false,
|
|
18
|
+
clientInfoTag: undefined,
|
|
19
|
+
// Sentinel
|
|
20
|
+
sentinels: null,
|
|
21
|
+
name: null,
|
|
22
|
+
role: "master",
|
|
23
|
+
sentinelRetryStrategy: function (times) {
|
|
24
|
+
return Math.min(times * 10, 1000);
|
|
25
|
+
},
|
|
26
|
+
sentinelReconnectStrategy: function () {
|
|
27
|
+
// This strategy only applies when sentinels are used for detecting
|
|
28
|
+
// a failover, not during initial master resolution.
|
|
29
|
+
// The deployment can still function when some of the sentinels are down
|
|
30
|
+
// for a long period of time, so we may not want to attempt reconnection
|
|
31
|
+
// very often. Therefore the default interval is fairly long (1 minute).
|
|
32
|
+
return 60000;
|
|
33
|
+
},
|
|
34
|
+
natMap: null,
|
|
35
|
+
enableTLSForSentinelMode: false,
|
|
36
|
+
updateSentinels: true,
|
|
37
|
+
failoverDetector: false,
|
|
38
|
+
// Status
|
|
39
|
+
username: null,
|
|
40
|
+
password: null,
|
|
41
|
+
db: 0,
|
|
42
|
+
// Others
|
|
43
|
+
enableOfflineQueue: true,
|
|
44
|
+
enableReadyCheck: true,
|
|
45
|
+
autoResubscribe: true,
|
|
46
|
+
autoResendUnfulfilledCommands: true,
|
|
47
|
+
lazyConnect: false,
|
|
48
|
+
keyPrefix: "",
|
|
49
|
+
reconnectOnError: null,
|
|
50
|
+
readOnly: false,
|
|
51
|
+
stringNumbers: false,
|
|
52
|
+
maxRetriesPerRequest: 20,
|
|
53
|
+
maxLoadingRetryTime: 10000,
|
|
54
|
+
enableAutoPipelining: false,
|
|
55
|
+
autoPipeliningIgnoredCommands: [],
|
|
56
|
+
sentinelMaxConnections: 10,
|
|
57
|
+
blockingTimeoutGrace: 100,
|
|
58
|
+
};
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readyHandler = exports.errorHandler = exports.closeHandler = exports.connectHandler = void 0;
|
|
4
|
+
const redis_errors_1 = require("redis-errors");
|
|
5
|
+
const Command_1 = require("../Command");
|
|
6
|
+
const errors_1 = require("../errors");
|
|
7
|
+
const utils_1 = require("../utils");
|
|
8
|
+
const DataHandler_1 = require("../DataHandler");
|
|
9
|
+
const debug = (0, utils_1.Debug)("connection");
|
|
10
|
+
function connectHandler(self) {
|
|
11
|
+
return function () {
|
|
12
|
+
var _a;
|
|
13
|
+
self.setStatus("connect");
|
|
14
|
+
self.resetCommandQueue();
|
|
15
|
+
// AUTH command should be processed before any other commands
|
|
16
|
+
let flushed = false;
|
|
17
|
+
const { connectionEpoch } = self;
|
|
18
|
+
if (self.condition.auth) {
|
|
19
|
+
self.auth(self.condition.auth, function (err) {
|
|
20
|
+
if (connectionEpoch !== self.connectionEpoch) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (err) {
|
|
24
|
+
if (err.message.indexOf("no password is set") !== -1) {
|
|
25
|
+
console.warn("[WARN] Redis server does not require a password, but a password was supplied.");
|
|
26
|
+
}
|
|
27
|
+
else if (err.message.indexOf("without any password configured for the default user") !== -1) {
|
|
28
|
+
console.warn("[WARN] This Redis server's `default` user does not require a password, but a password was supplied");
|
|
29
|
+
}
|
|
30
|
+
else if (err.message.indexOf("wrong number of arguments for 'auth' command") !== -1) {
|
|
31
|
+
console.warn(`[ERROR] The server returned "wrong number of arguments for 'auth' command". You are probably passing both username and password to Redis version 5 or below. You should only pass the 'password' option for Redis version 5 and under.`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
flushed = true;
|
|
35
|
+
self.recoverFromFatalError(err, err);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (self.condition.select) {
|
|
41
|
+
self.select(self.condition.select).catch((err) => {
|
|
42
|
+
// If the node is in cluster mode, select is disallowed.
|
|
43
|
+
// In this case, reconnect won't help.
|
|
44
|
+
self.silentEmit("error", err);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/*
|
|
48
|
+
No need to keep the reference of DataHandler here
|
|
49
|
+
because we don't need to do the cleanup.
|
|
50
|
+
`Stream#end()` will remove all listeners for us.
|
|
51
|
+
*/
|
|
52
|
+
new DataHandler_1.default(self, {
|
|
53
|
+
stringNumbers: self.options.stringNumbers,
|
|
54
|
+
});
|
|
55
|
+
const clientCommandPromises = [];
|
|
56
|
+
if (self.options.connectionName) {
|
|
57
|
+
debug("set the connection name [%s]", self.options.connectionName);
|
|
58
|
+
clientCommandPromises.push(self.client("setname", self.options.connectionName).catch(utils_1.noop));
|
|
59
|
+
}
|
|
60
|
+
if (!self.options.disableClientInfo) {
|
|
61
|
+
debug("set the client info");
|
|
62
|
+
clientCommandPromises.push((0, utils_1.getPackageMeta)()
|
|
63
|
+
.then((packageMeta) => {
|
|
64
|
+
return self
|
|
65
|
+
.client("SETINFO", "LIB-VER", packageMeta.version)
|
|
66
|
+
.catch(utils_1.noop);
|
|
67
|
+
})
|
|
68
|
+
.catch(utils_1.noop));
|
|
69
|
+
clientCommandPromises.push(self
|
|
70
|
+
.client("SETINFO", "LIB-NAME", ((_a = self.options) === null || _a === void 0 ? void 0 : _a.clientInfoTag)
|
|
71
|
+
? `ioredis-om(${self.options.clientInfoTag})`
|
|
72
|
+
: "ioredis-om")
|
|
73
|
+
.catch(utils_1.noop));
|
|
74
|
+
}
|
|
75
|
+
Promise.all(clientCommandPromises)
|
|
76
|
+
.catch(utils_1.noop)
|
|
77
|
+
.finally(() => {
|
|
78
|
+
if (!self.options.enableReadyCheck) {
|
|
79
|
+
exports.readyHandler(self)();
|
|
80
|
+
}
|
|
81
|
+
if (self.options.enableReadyCheck) {
|
|
82
|
+
self._readyCheck(function (err, info) {
|
|
83
|
+
if (connectionEpoch !== self.connectionEpoch) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (err) {
|
|
87
|
+
if (!flushed) {
|
|
88
|
+
self.recoverFromFatalError(new Error("Ready check failed: " + err.message), err);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
if (self.connector.check(info)) {
|
|
93
|
+
exports.readyHandler(self)();
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
self.disconnect(true);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
exports.connectHandler = connectHandler;
|
|
105
|
+
function abortError(command) {
|
|
106
|
+
const err = new redis_errors_1.AbortError("Command aborted due to connection close");
|
|
107
|
+
err.command = {
|
|
108
|
+
name: command.name,
|
|
109
|
+
args: command.args,
|
|
110
|
+
};
|
|
111
|
+
return err;
|
|
112
|
+
}
|
|
113
|
+
// If a contiguous set of pipeline commands starts from index zero then they
|
|
114
|
+
// can be safely reattempted. If however we have a chain of pipelined commands
|
|
115
|
+
// starting at index 1 or more it means we received a partial response before
|
|
116
|
+
// the connection close and those pipelined commands must be aborted. For
|
|
117
|
+
// example, if the queue looks like this: [2, 3, 4, 0, 1, 2] then after
|
|
118
|
+
// aborting and purging we'll have a queue that looks like this: [0, 1, 2]
|
|
119
|
+
function abortIncompletePipelines(commandQueue) {
|
|
120
|
+
var _a;
|
|
121
|
+
let expectedIndex = 0;
|
|
122
|
+
for (let i = 0; i < commandQueue.length;) {
|
|
123
|
+
const command = (_a = commandQueue.peekAt(i)) === null || _a === void 0 ? void 0 : _a.command;
|
|
124
|
+
const pipelineIndex = command.pipelineIndex;
|
|
125
|
+
if (pipelineIndex === undefined || pipelineIndex === 0) {
|
|
126
|
+
expectedIndex = 0;
|
|
127
|
+
}
|
|
128
|
+
if (pipelineIndex !== undefined && pipelineIndex !== expectedIndex++) {
|
|
129
|
+
commandQueue.remove(i, 1);
|
|
130
|
+
command.reject(abortError(command));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// If only a partial transaction result was received before connection close,
|
|
137
|
+
// we have to abort any transaction fragments that may have ended up in the
|
|
138
|
+
// offline queue
|
|
139
|
+
function abortTransactionFragments(commandQueue) {
|
|
140
|
+
var _a;
|
|
141
|
+
for (let i = 0; i < commandQueue.length;) {
|
|
142
|
+
const command = (_a = commandQueue.peekAt(i)) === null || _a === void 0 ? void 0 : _a.command;
|
|
143
|
+
if (command.name === "multi") {
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
if (command.name === "exec") {
|
|
147
|
+
commandQueue.remove(i, 1);
|
|
148
|
+
command.reject(abortError(command));
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
if (command.inTransaction) {
|
|
152
|
+
commandQueue.remove(i, 1);
|
|
153
|
+
command.reject(abortError(command));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
i++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function closeHandler(self) {
|
|
161
|
+
return function () {
|
|
162
|
+
const prevStatus = self.status;
|
|
163
|
+
self.setStatus("close");
|
|
164
|
+
if (self.commandQueue.length) {
|
|
165
|
+
abortIncompletePipelines(self.commandQueue);
|
|
166
|
+
}
|
|
167
|
+
if (self.offlineQueue.length) {
|
|
168
|
+
abortTransactionFragments(self.offlineQueue);
|
|
169
|
+
}
|
|
170
|
+
if (prevStatus === "ready") {
|
|
171
|
+
if (!self.prevCondition) {
|
|
172
|
+
self.prevCondition = self.condition;
|
|
173
|
+
}
|
|
174
|
+
if (self.commandQueue.length) {
|
|
175
|
+
self.prevCommandQueue = self.commandQueue;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (self.manuallyClosing) {
|
|
179
|
+
self.manuallyClosing = false;
|
|
180
|
+
debug("skip reconnecting since the connection is manually closed.");
|
|
181
|
+
return close();
|
|
182
|
+
}
|
|
183
|
+
if (typeof self.options.retryStrategy !== "function") {
|
|
184
|
+
debug("skip reconnecting because `retryStrategy` is not a function");
|
|
185
|
+
return close();
|
|
186
|
+
}
|
|
187
|
+
const retryDelay = self.options.retryStrategy(++self.retryAttempts);
|
|
188
|
+
if (typeof retryDelay !== "number") {
|
|
189
|
+
debug("skip reconnecting because `retryStrategy` doesn't return a number");
|
|
190
|
+
return close();
|
|
191
|
+
}
|
|
192
|
+
debug("reconnect in %sms", retryDelay);
|
|
193
|
+
self.setStatus("reconnecting", retryDelay);
|
|
194
|
+
self.reconnectTimeout = setTimeout(function () {
|
|
195
|
+
self.reconnectTimeout = null;
|
|
196
|
+
self.connect().catch(utils_1.noop);
|
|
197
|
+
}, retryDelay);
|
|
198
|
+
const { maxRetriesPerRequest } = self.options;
|
|
199
|
+
if (typeof maxRetriesPerRequest === "number") {
|
|
200
|
+
if (maxRetriesPerRequest < 0) {
|
|
201
|
+
debug("maxRetriesPerRequest is negative, ignoring...");
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
const remainder = self.retryAttempts % (maxRetriesPerRequest + 1);
|
|
205
|
+
if (remainder === 0) {
|
|
206
|
+
debug("reach maxRetriesPerRequest limitation, flushing command queue...");
|
|
207
|
+
self.flushQueue(new errors_1.MaxRetriesPerRequestError(maxRetriesPerRequest));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
function close() {
|
|
213
|
+
self.setStatus("end");
|
|
214
|
+
self.flushQueue(new Error(utils_1.CONNECTION_CLOSED_ERROR_MSG));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
exports.closeHandler = closeHandler;
|
|
218
|
+
function errorHandler(self) {
|
|
219
|
+
return function (error) {
|
|
220
|
+
debug("error: %s", error);
|
|
221
|
+
self.silentEmit("error", error);
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
exports.errorHandler = errorHandler;
|
|
225
|
+
function readyHandler(self) {
|
|
226
|
+
return function () {
|
|
227
|
+
self.setStatus("ready");
|
|
228
|
+
self.retryAttempts = 0;
|
|
229
|
+
if (self.options.monitor) {
|
|
230
|
+
self.call("monitor").then(() => self.setStatus("monitoring"), (error) => self.emit("error", error));
|
|
231
|
+
const { sendCommand } = self;
|
|
232
|
+
self.sendCommand = function (command) {
|
|
233
|
+
if (Command_1.default.checkFlag("VALID_IN_MONITOR_MODE", command.name)) {
|
|
234
|
+
return sendCommand.call(self, command);
|
|
235
|
+
}
|
|
236
|
+
command.reject(new Error("Connection is in monitoring mode, can't process commands."));
|
|
237
|
+
return command.promise;
|
|
238
|
+
};
|
|
239
|
+
self.once("close", function () {
|
|
240
|
+
delete self.sendCommand;
|
|
241
|
+
});
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const finalSelect = self.prevCondition
|
|
245
|
+
? self.prevCondition.select
|
|
246
|
+
: self.condition.select;
|
|
247
|
+
if (self.options.readOnly) {
|
|
248
|
+
debug("set the connection to readonly mode");
|
|
249
|
+
self.readonly().catch(utils_1.noop);
|
|
250
|
+
}
|
|
251
|
+
if (self.prevCondition) {
|
|
252
|
+
const condition = self.prevCondition;
|
|
253
|
+
self.prevCondition = null;
|
|
254
|
+
if (condition.subscriber && self.options.autoResubscribe) {
|
|
255
|
+
// We re-select the previous db first since
|
|
256
|
+
// `SELECT` command is not valid in sub mode.
|
|
257
|
+
if (self.condition.select !== finalSelect) {
|
|
258
|
+
debug("connect to db [%d]", finalSelect);
|
|
259
|
+
self.select(finalSelect);
|
|
260
|
+
}
|
|
261
|
+
const subscribeChannels = condition.subscriber.channels("subscribe");
|
|
262
|
+
if (subscribeChannels.length) {
|
|
263
|
+
debug("subscribe %d channels", subscribeChannels.length);
|
|
264
|
+
self.subscribe(subscribeChannels);
|
|
265
|
+
}
|
|
266
|
+
const psubscribeChannels = condition.subscriber.channels("psubscribe");
|
|
267
|
+
if (psubscribeChannels.length) {
|
|
268
|
+
debug("psubscribe %d channels", psubscribeChannels.length);
|
|
269
|
+
self.psubscribe(psubscribeChannels);
|
|
270
|
+
}
|
|
271
|
+
const ssubscribeChannels = condition.subscriber.channels("ssubscribe");
|
|
272
|
+
if (ssubscribeChannels.length) {
|
|
273
|
+
debug("ssubscribe %s", ssubscribeChannels.length);
|
|
274
|
+
for (const channel of ssubscribeChannels) {
|
|
275
|
+
self.ssubscribe(channel);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (self.prevCommandQueue) {
|
|
281
|
+
if (self.options.autoResendUnfulfilledCommands) {
|
|
282
|
+
debug("resend %d unfulfilled commands", self.prevCommandQueue.length);
|
|
283
|
+
while (self.prevCommandQueue.length > 0) {
|
|
284
|
+
const item = self.prevCommandQueue.shift();
|
|
285
|
+
if (item.select !== self.condition.select &&
|
|
286
|
+
item.command.name !== "select") {
|
|
287
|
+
self.select(item.select);
|
|
288
|
+
}
|
|
289
|
+
self.sendCommand(item.command, item.stream);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
self.prevCommandQueue = null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (self.offlineQueue.length) {
|
|
297
|
+
debug("send %d commands in offline queue", self.offlineQueue.length);
|
|
298
|
+
const offlineQueue = self.offlineQueue;
|
|
299
|
+
self.resetOfflineQueue();
|
|
300
|
+
while (offlineQueue.length > 0) {
|
|
301
|
+
const item = offlineQueue.shift();
|
|
302
|
+
if (item.select !== self.condition.select &&
|
|
303
|
+
item.command.name !== "select") {
|
|
304
|
+
self.select(item.select);
|
|
305
|
+
}
|
|
306
|
+
self.sendCommand(item.command, item.stream);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (self.condition.select !== finalSelect) {
|
|
310
|
+
debug("connect to db [%d]", finalSelect);
|
|
311
|
+
self.select(finalSelect);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
exports.readyHandler = readyHandler;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CommandParameter } from "./types";
|
|
2
|
+
export declare function sanitizeArgs(commandName: string, args: CommandParameter[]): string[];
|
|
3
|
+
export interface CommandTraceContext {
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
database: number;
|
|
7
|
+
serverAddress: string;
|
|
8
|
+
serverPort: number | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface BatchOperationContext {
|
|
11
|
+
batchMode: "MULTI";
|
|
12
|
+
batchSize: number;
|
|
13
|
+
database: number;
|
|
14
|
+
serverAddress: string;
|
|
15
|
+
serverPort: number | undefined;
|
|
16
|
+
}
|
|
17
|
+
export interface ConnectTraceContext {
|
|
18
|
+
serverAddress: string;
|
|
19
|
+
serverPort: number | undefined;
|
|
20
|
+
connectionEpoch: number;
|
|
21
|
+
}
|
|
22
|
+
declare type CommandContext = CommandTraceContext;
|
|
23
|
+
export declare function traceCommand<T>(fn: () => Promise<T>, contextFactory: () => CommandContext): Promise<T>;
|
|
24
|
+
export declare function traceBatch<T>(fn: () => Promise<T>, contextFactory: () => BatchOperationContext): Promise<T>;
|
|
25
|
+
export declare function traceConnect<T>(fn: () => Promise<T>, contextFactory: () => ConnectTraceContext): Promise<T>;
|
|
26
|
+
export {};
|