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,23 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Readable, ReadableOptions } from "stream";
|
|
3
|
+
interface Options extends ReadableOptions {
|
|
4
|
+
key?: string;
|
|
5
|
+
match?: string;
|
|
6
|
+
type?: string;
|
|
7
|
+
command: string;
|
|
8
|
+
redis: any;
|
|
9
|
+
count?: string | number;
|
|
10
|
+
noValues?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Convenient class to convert the process of scanning keys to a readable stream.
|
|
14
|
+
*/
|
|
15
|
+
export default class ScanStream extends Readable {
|
|
16
|
+
private opt;
|
|
17
|
+
private _redisCursor;
|
|
18
|
+
private _redisDrained;
|
|
19
|
+
constructor(opt: Options);
|
|
20
|
+
_read(): void;
|
|
21
|
+
close(): void;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const stream_1 = require("stream");
|
|
4
|
+
/**
|
|
5
|
+
* Convenient class to convert the process of scanning keys to a readable stream.
|
|
6
|
+
*/
|
|
7
|
+
class ScanStream extends stream_1.Readable {
|
|
8
|
+
constructor(opt) {
|
|
9
|
+
super(opt);
|
|
10
|
+
this.opt = opt;
|
|
11
|
+
this._redisCursor = "0";
|
|
12
|
+
this._redisDrained = false;
|
|
13
|
+
}
|
|
14
|
+
_read() {
|
|
15
|
+
if (this._redisDrained) {
|
|
16
|
+
this.push(null);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const args = [this._redisCursor];
|
|
20
|
+
if (this.opt.key) {
|
|
21
|
+
args.unshift(this.opt.key);
|
|
22
|
+
}
|
|
23
|
+
if (this.opt.match) {
|
|
24
|
+
args.push("MATCH", this.opt.match);
|
|
25
|
+
}
|
|
26
|
+
if (this.opt.type) {
|
|
27
|
+
args.push("TYPE", this.opt.type);
|
|
28
|
+
}
|
|
29
|
+
if (this.opt.count) {
|
|
30
|
+
args.push("COUNT", String(this.opt.count));
|
|
31
|
+
}
|
|
32
|
+
if (this.opt.noValues) {
|
|
33
|
+
args.push("NOVALUES");
|
|
34
|
+
}
|
|
35
|
+
this.opt.redis[this.opt.command](args, (err, res) => {
|
|
36
|
+
if (err) {
|
|
37
|
+
this.emit("error", err);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this._redisCursor = res[0] instanceof Buffer ? res[0].toString() : res[0];
|
|
41
|
+
if (this._redisCursor === "0") {
|
|
42
|
+
this._redisDrained = true;
|
|
43
|
+
}
|
|
44
|
+
this.push(res[1]);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
close() {
|
|
48
|
+
this._redisDrained = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.default = ScanStream;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Callback } from "./types";
|
|
2
|
+
export default class Script {
|
|
3
|
+
private lua;
|
|
4
|
+
private numberOfKeys;
|
|
5
|
+
private keyPrefix;
|
|
6
|
+
private readOnly;
|
|
7
|
+
private sha;
|
|
8
|
+
private Command;
|
|
9
|
+
constructor(lua: string, numberOfKeys?: number | null, keyPrefix?: string, readOnly?: boolean);
|
|
10
|
+
execute(container: any, args: any[], options: any, callback?: Callback): any;
|
|
11
|
+
}
|
package/built/Script.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const crypto_1 = require("crypto");
|
|
4
|
+
const Command_1 = require("./Command");
|
|
5
|
+
const standard_as_callback_1 = require("standard-as-callback");
|
|
6
|
+
class Script {
|
|
7
|
+
constructor(lua, numberOfKeys = null, keyPrefix = "", readOnly = false) {
|
|
8
|
+
this.lua = lua;
|
|
9
|
+
this.numberOfKeys = numberOfKeys;
|
|
10
|
+
this.keyPrefix = keyPrefix;
|
|
11
|
+
this.readOnly = readOnly;
|
|
12
|
+
this.sha = (0, crypto_1.createHash)("sha1").update(lua).digest("hex");
|
|
13
|
+
const sha = this.sha;
|
|
14
|
+
const socketHasScriptLoaded = new WeakSet();
|
|
15
|
+
this.Command = class CustomScriptCommand extends Command_1.default {
|
|
16
|
+
toWritable(socket) {
|
|
17
|
+
const origReject = this.reject;
|
|
18
|
+
this.reject = (err) => {
|
|
19
|
+
if (err.message.indexOf("NOSCRIPT") !== -1) {
|
|
20
|
+
socketHasScriptLoaded.delete(socket);
|
|
21
|
+
}
|
|
22
|
+
origReject.call(this, err);
|
|
23
|
+
};
|
|
24
|
+
if (!socketHasScriptLoaded.has(socket)) {
|
|
25
|
+
socketHasScriptLoaded.add(socket);
|
|
26
|
+
this.name = "eval";
|
|
27
|
+
this.args[0] = lua;
|
|
28
|
+
}
|
|
29
|
+
else if (this.name === "eval") {
|
|
30
|
+
this.name = "evalsha";
|
|
31
|
+
this.args[0] = sha;
|
|
32
|
+
}
|
|
33
|
+
return super.toWritable(socket);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
execute(container, args, options, callback) {
|
|
38
|
+
if (typeof this.numberOfKeys === "number") {
|
|
39
|
+
args.unshift(this.numberOfKeys);
|
|
40
|
+
}
|
|
41
|
+
if (this.keyPrefix) {
|
|
42
|
+
options.keyPrefix = this.keyPrefix;
|
|
43
|
+
}
|
|
44
|
+
if (this.readOnly) {
|
|
45
|
+
options.readOnly = true;
|
|
46
|
+
}
|
|
47
|
+
const evalsha = new this.Command("evalsha", [this.sha, ...args], options);
|
|
48
|
+
evalsha.promise = evalsha.promise.catch((err) => {
|
|
49
|
+
if (err.message.indexOf("NOSCRIPT") === -1) {
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
// Resend the same custom evalsha command that gets transformed
|
|
53
|
+
// to an eval in case it's not loaded yet on the connection.
|
|
54
|
+
const resend = new this.Command("evalsha", [this.sha, ...args], options);
|
|
55
|
+
const client = container.isPipeline ? container.redis : container;
|
|
56
|
+
return client.sendCommand(resend);
|
|
57
|
+
});
|
|
58
|
+
(0, standard_as_callback_1.default)(evalsha.promise, callback);
|
|
59
|
+
return container.sendCommand(evalsha);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.default = Script;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CommandNameFlags } from "./Command";
|
|
2
|
+
declare type AddSet = CommandNameFlags["ENTER_SUBSCRIBER_MODE"][number];
|
|
3
|
+
declare type DelSet = CommandNameFlags["EXIT_SUBSCRIBER_MODE"][number];
|
|
4
|
+
/**
|
|
5
|
+
* Tiny class to simplify dealing with subscription set
|
|
6
|
+
*/
|
|
7
|
+
export default class SubscriptionSet {
|
|
8
|
+
private set;
|
|
9
|
+
add(set: AddSet, channel: string): void;
|
|
10
|
+
del(set: DelSet, channel: string): void;
|
|
11
|
+
channels(set: AddSet | DelSet): string[];
|
|
12
|
+
isEmpty(): boolean;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Tiny class to simplify dealing with subscription set
|
|
5
|
+
*/
|
|
6
|
+
class SubscriptionSet {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.set = {
|
|
9
|
+
subscribe: {},
|
|
10
|
+
psubscribe: {},
|
|
11
|
+
ssubscribe: {},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
add(set, channel) {
|
|
15
|
+
this.set[mapSet(set)][channel] = true;
|
|
16
|
+
}
|
|
17
|
+
del(set, channel) {
|
|
18
|
+
delete this.set[mapSet(set)][channel];
|
|
19
|
+
}
|
|
20
|
+
channels(set) {
|
|
21
|
+
return Object.keys(this.set[mapSet(set)]);
|
|
22
|
+
}
|
|
23
|
+
isEmpty() {
|
|
24
|
+
return (this.channels("subscribe").length === 0 &&
|
|
25
|
+
this.channels("psubscribe").length === 0 &&
|
|
26
|
+
this.channels("ssubscribe").length === 0);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.default = SubscriptionSet;
|
|
30
|
+
function mapSet(set) {
|
|
31
|
+
if (set === "unsubscribe") {
|
|
32
|
+
return "subscribe";
|
|
33
|
+
}
|
|
34
|
+
if (set === "punsubscribe") {
|
|
35
|
+
return "psubscribe";
|
|
36
|
+
}
|
|
37
|
+
if (set === "sunsubscribe") {
|
|
38
|
+
return "ssubscribe";
|
|
39
|
+
}
|
|
40
|
+
return set;
|
|
41
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { ArgumentType } from "./Command";
|
|
3
|
+
export declare const kExec: unique symbol;
|
|
4
|
+
export declare const kCallbacks: unique symbol;
|
|
5
|
+
export declare const notAllowedAutoPipelineCommands: string[];
|
|
6
|
+
export declare function shouldUseAutoPipelining(client: any, functionName: string, commandName: string): boolean;
|
|
7
|
+
export declare function getFirstValueInFlattenedArray(args: ArgumentType[]): string | Buffer | number | null | undefined;
|
|
8
|
+
export declare function executeWithAutoPipelining(client: any, functionName: string, commandName: string, args: ArgumentType[], callback: any): Promise<unknown>;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeWithAutoPipelining = exports.getFirstValueInFlattenedArray = exports.shouldUseAutoPipelining = exports.notAllowedAutoPipelineCommands = exports.kCallbacks = exports.kExec = void 0;
|
|
4
|
+
const lodash_1 = require("./utils/lodash");
|
|
5
|
+
const calculateSlot = require("cluster-key-slot");
|
|
6
|
+
const standard_as_callback_1 = require("standard-as-callback");
|
|
7
|
+
const commands_1 = require("@ioredis/commands");
|
|
8
|
+
exports.kExec = Symbol("exec");
|
|
9
|
+
exports.kCallbacks = Symbol("callbacks");
|
|
10
|
+
exports.notAllowedAutoPipelineCommands = [
|
|
11
|
+
"auth",
|
|
12
|
+
"info",
|
|
13
|
+
"script",
|
|
14
|
+
"quit",
|
|
15
|
+
"cluster",
|
|
16
|
+
"pipeline",
|
|
17
|
+
"multi",
|
|
18
|
+
"subscribe",
|
|
19
|
+
"psubscribe",
|
|
20
|
+
"unsubscribe",
|
|
21
|
+
"unpsubscribe",
|
|
22
|
+
"select",
|
|
23
|
+
"client",
|
|
24
|
+
];
|
|
25
|
+
function executeAutoPipeline(client, slotKey) {
|
|
26
|
+
/*
|
|
27
|
+
If a pipeline is already executing, keep queueing up commands
|
|
28
|
+
since ioredis-om won't serve two pipelines at the same time
|
|
29
|
+
*/
|
|
30
|
+
if (client._runningAutoPipelines.has(slotKey)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (!client._autoPipelines.has(slotKey)) {
|
|
34
|
+
/*
|
|
35
|
+
Rare edge case. Somehow, something has deleted this running autopipeline in an immediate
|
|
36
|
+
call to executeAutoPipeline.
|
|
37
|
+
|
|
38
|
+
Maybe the callback in the pipeline.exec is sometimes called in the same tick,
|
|
39
|
+
e.g. if redis is disconnected?
|
|
40
|
+
*/
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
client._runningAutoPipelines.add(slotKey);
|
|
44
|
+
// Get the pipeline and immediately delete it so that new commands are queued on a new pipeline
|
|
45
|
+
const pipeline = client._autoPipelines.get(slotKey);
|
|
46
|
+
client._autoPipelines.delete(slotKey);
|
|
47
|
+
const callbacks = pipeline[exports.kCallbacks];
|
|
48
|
+
// Stop keeping a reference to callbacks immediately after the callbacks stop being used.
|
|
49
|
+
// This allows the GC to reclaim objects referenced by callbacks, especially with 16384 slots
|
|
50
|
+
// in Redis.Cluster
|
|
51
|
+
pipeline[exports.kCallbacks] = null;
|
|
52
|
+
// Perform the call
|
|
53
|
+
pipeline.exec(function (err, results) {
|
|
54
|
+
client._runningAutoPipelines.delete(slotKey);
|
|
55
|
+
/*
|
|
56
|
+
Invoke all callback in nextTick so the stack is cleared
|
|
57
|
+
and callbacks can throw errors without affecting other callbacks.
|
|
58
|
+
*/
|
|
59
|
+
if (err) {
|
|
60
|
+
for (let i = 0; i < callbacks.length; i++) {
|
|
61
|
+
process.nextTick(callbacks[i], err);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
for (let i = 0; i < callbacks.length; i++) {
|
|
66
|
+
process.nextTick(callbacks[i], ...results[i]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// If there is another pipeline on the same node, immediately execute it without waiting for nextTick
|
|
70
|
+
if (client._autoPipelines.has(slotKey)) {
|
|
71
|
+
executeAutoPipeline(client, slotKey);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function shouldUseAutoPipelining(client, functionName, commandName) {
|
|
76
|
+
return (functionName &&
|
|
77
|
+
client.options.enableAutoPipelining &&
|
|
78
|
+
!client.isPipeline &&
|
|
79
|
+
!exports.notAllowedAutoPipelineCommands.includes(commandName) &&
|
|
80
|
+
!client.options.autoPipeliningIgnoredCommands.includes(commandName));
|
|
81
|
+
}
|
|
82
|
+
exports.shouldUseAutoPipelining = shouldUseAutoPipelining;
|
|
83
|
+
function getFirstValueInFlattenedArray(args) {
|
|
84
|
+
for (let i = 0; i < args.length; i++) {
|
|
85
|
+
const arg = args[i];
|
|
86
|
+
if (typeof arg === "string") {
|
|
87
|
+
return arg;
|
|
88
|
+
}
|
|
89
|
+
else if (Array.isArray(arg) || (0, lodash_1.isArguments)(arg)) {
|
|
90
|
+
if (arg.length === 0) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
return arg[0];
|
|
94
|
+
}
|
|
95
|
+
const flattened = [arg].flat();
|
|
96
|
+
if (flattened.length > 0) {
|
|
97
|
+
return flattened[0];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
exports.getFirstValueInFlattenedArray = getFirstValueInFlattenedArray;
|
|
103
|
+
function executeWithAutoPipelining(client, functionName, commandName, args, callback) {
|
|
104
|
+
// On cluster mode let's wait for slots to be available
|
|
105
|
+
if (client.isCluster && !client.slots.length) {
|
|
106
|
+
if (client.status === "wait")
|
|
107
|
+
client.connect().catch(lodash_1.noop);
|
|
108
|
+
return (0, standard_as_callback_1.default)(new Promise(function (resolve, reject) {
|
|
109
|
+
client.delayUntilReady((err) => {
|
|
110
|
+
if (err) {
|
|
111
|
+
reject(err);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
executeWithAutoPipelining(client, functionName, commandName, args, null).then(resolve, reject);
|
|
115
|
+
});
|
|
116
|
+
}), callback);
|
|
117
|
+
}
|
|
118
|
+
// If we have slot information, we can improve routing by grouping slots served by the same subset of nodes
|
|
119
|
+
// Note that the first value in args may be a (possibly empty) array.
|
|
120
|
+
// ioredis-om will only flatten one level of the array, in the Command constructor.
|
|
121
|
+
const prefix = client.options.keyPrefix || "";
|
|
122
|
+
let slotKey = client.isCluster
|
|
123
|
+
? client.slots[calculateSlot(`${prefix}${getFirstValueInFlattenedArray(args)}`)].join(",")
|
|
124
|
+
: "main";
|
|
125
|
+
// When scaleReads is enabled, separate read and write commands into different pipelines
|
|
126
|
+
// so they can be routed to replicas and masters respectively
|
|
127
|
+
if (client.isCluster && client.options.scaleReads !== "master") {
|
|
128
|
+
const isReadOnly = (0, commands_1.exists)(commandName) && (0, commands_1.hasFlag)(commandName, "readonly");
|
|
129
|
+
slotKey += isReadOnly ? ":read" : ":write";
|
|
130
|
+
}
|
|
131
|
+
if (!client._autoPipelines.has(slotKey)) {
|
|
132
|
+
const pipeline = client.pipeline();
|
|
133
|
+
pipeline[exports.kExec] = false;
|
|
134
|
+
pipeline[exports.kCallbacks] = [];
|
|
135
|
+
client._autoPipelines.set(slotKey, pipeline);
|
|
136
|
+
}
|
|
137
|
+
const pipeline = client._autoPipelines.get(slotKey);
|
|
138
|
+
/*
|
|
139
|
+
Mark the pipeline as scheduled.
|
|
140
|
+
The symbol will make sure that the pipeline is only scheduled once per tick.
|
|
141
|
+
New commands are appended to an already scheduled pipeline.
|
|
142
|
+
*/
|
|
143
|
+
if (!pipeline[exports.kExec]) {
|
|
144
|
+
pipeline[exports.kExec] = true;
|
|
145
|
+
/*
|
|
146
|
+
Deferring with setImmediate so we have a chance to capture multiple
|
|
147
|
+
commands that can be scheduled by I/O events already in the event loop queue.
|
|
148
|
+
*/
|
|
149
|
+
setImmediate(executeAutoPipeline, client, slotKey);
|
|
150
|
+
}
|
|
151
|
+
// Create the promise which will execute the command in the pipeline.
|
|
152
|
+
const autoPipelinePromise = new Promise(function (resolve, reject) {
|
|
153
|
+
pipeline[exports.kCallbacks].push(function (err, value) {
|
|
154
|
+
if (err) {
|
|
155
|
+
reject(err);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
resolve(value);
|
|
159
|
+
});
|
|
160
|
+
if (functionName === "call") {
|
|
161
|
+
args.unshift(commandName);
|
|
162
|
+
}
|
|
163
|
+
pipeline[functionName](...args);
|
|
164
|
+
});
|
|
165
|
+
return (0, standard_as_callback_1.default)(autoPipelinePromise, callback);
|
|
166
|
+
}
|
|
167
|
+
exports.executeWithAutoPipelining = executeWithAutoPipelining;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { SrvRecord } from "dns";
|
|
3
|
+
import { RedisOptions } from "../redis/RedisOptions";
|
|
4
|
+
import { CommanderOptions } from "../utils/Commander";
|
|
5
|
+
import { NodeRole } from "./util";
|
|
6
|
+
export declare type DNSResolveSrvFunction = (hostname: string, callback: (err: NodeJS.ErrnoException | null | undefined, records?: SrvRecord[]) => void) => void;
|
|
7
|
+
export declare type DNSLookupFunction = (hostname: string, callback: (err: NodeJS.ErrnoException | null | undefined, address: string, family?: number) => void) => void;
|
|
8
|
+
export declare type NatMapFunction = (key: string) => {
|
|
9
|
+
host: string;
|
|
10
|
+
port: number;
|
|
11
|
+
} | null;
|
|
12
|
+
export declare type NatMap = {
|
|
13
|
+
[key: string]: {
|
|
14
|
+
host: string;
|
|
15
|
+
port: number;
|
|
16
|
+
};
|
|
17
|
+
} | NatMapFunction;
|
|
18
|
+
/**
|
|
19
|
+
* Options for Cluster constructor
|
|
20
|
+
*/
|
|
21
|
+
export interface ClusterOptions extends CommanderOptions {
|
|
22
|
+
/**
|
|
23
|
+
* See "Quick Start" section.
|
|
24
|
+
*
|
|
25
|
+
* @default (times) => Math.min(100 + times * 2, 2000)
|
|
26
|
+
*/
|
|
27
|
+
clusterRetryStrategy?: ((times: number, reason?: Error) => number | void | null) | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* See Redis class.
|
|
30
|
+
*
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
enableOfflineQueue?: boolean | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* When enabled, ioredis-om only emits "ready" event when `CLUSTER INFO`
|
|
36
|
+
* command reporting the cluster is ready for handling commands.
|
|
37
|
+
*
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
enableReadyCheck?: boolean | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Scale reads to the node with the specified role.
|
|
43
|
+
*
|
|
44
|
+
* @default "master"
|
|
45
|
+
*/
|
|
46
|
+
scaleReads?: NodeRole | Function | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* When a MOVED or ASK error is received, client will redirect the
|
|
49
|
+
* command to another node.
|
|
50
|
+
* This option limits the max redirections allowed to send a command.
|
|
51
|
+
*
|
|
52
|
+
* @default 16
|
|
53
|
+
*/
|
|
54
|
+
maxRedirections?: number | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* When an error is received when sending a command (e.g.
|
|
57
|
+
* "Connection is closed." when the target Redis node is down), client will retry
|
|
58
|
+
* if `retryDelayOnFailover` is valid delay time (in ms).
|
|
59
|
+
*
|
|
60
|
+
* @default 100
|
|
61
|
+
*/
|
|
62
|
+
retryDelayOnFailover?: number | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* When a CLUSTERDOWN error is received, client will retry
|
|
65
|
+
* if `retryDelayOnClusterDown` is valid delay time (in ms).
|
|
66
|
+
*
|
|
67
|
+
* @default 100
|
|
68
|
+
*/
|
|
69
|
+
retryDelayOnClusterDown?: number | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* When a TRYAGAIN error is received, client will retry
|
|
72
|
+
* if `retryDelayOnTryAgain` is valid delay time (in ms).
|
|
73
|
+
*
|
|
74
|
+
* @default 100
|
|
75
|
+
*/
|
|
76
|
+
retryDelayOnTryAgain?: number | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* By default, this value is 0, which means when a `MOVED` error is received,
|
|
79
|
+
* the client will resend the command instantly to the node returned together with
|
|
80
|
+
* the `MOVED` error. However, sometimes it takes time for a cluster to become
|
|
81
|
+
* state stabilized after a failover, so adding a delay before resending can
|
|
82
|
+
* prevent a ping pong effect.
|
|
83
|
+
*
|
|
84
|
+
* @default 0
|
|
85
|
+
*/
|
|
86
|
+
retryDelayOnMoved?: number | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* The milliseconds before a timeout occurs while refreshing
|
|
89
|
+
* slots from the cluster.
|
|
90
|
+
*
|
|
91
|
+
* @default 1000
|
|
92
|
+
*/
|
|
93
|
+
slotsRefreshTimeout?: number | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* The milliseconds between every automatic slots refresh.
|
|
96
|
+
*
|
|
97
|
+
* @default 5000
|
|
98
|
+
*/
|
|
99
|
+
slotsRefreshInterval?: number | undefined;
|
|
100
|
+
/**
|
|
101
|
+
* Use sharded subscribers instead of a single subscriber.
|
|
102
|
+
*
|
|
103
|
+
* If sharded subscribers are used, then one additional subscriber connection per master node
|
|
104
|
+
* is established. If you don't plan to use SPUBLISH/SSUBSCRIBE, then this should be disabled.
|
|
105
|
+
*
|
|
106
|
+
* @default false
|
|
107
|
+
*/
|
|
108
|
+
shardedSubscribers?: boolean | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* Passed to the constructor of `Redis`
|
|
111
|
+
*
|
|
112
|
+
* @default null
|
|
113
|
+
*/
|
|
114
|
+
redisOptions?: Omit<RedisOptions, "port" | "host" | "path" | "sentinels" | "retryStrategy" | "enableOfflineQueue" | "readOnly"> | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* By default, When a new Cluster instance is created,
|
|
117
|
+
* it will connect to the Redis cluster automatically.
|
|
118
|
+
* If you want to keep the instance disconnected until the first command is called,
|
|
119
|
+
* set this option to `true`.
|
|
120
|
+
*
|
|
121
|
+
* @default false
|
|
122
|
+
*/
|
|
123
|
+
lazyConnect?: boolean | undefined;
|
|
124
|
+
/**
|
|
125
|
+
* Discover nodes using SRV records
|
|
126
|
+
*
|
|
127
|
+
* @default false
|
|
128
|
+
*/
|
|
129
|
+
useSRVRecords?: boolean | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* SRV records will be resolved via this function.
|
|
132
|
+
*
|
|
133
|
+
* You may provide a custom `resolveSrv` function when you want to customize
|
|
134
|
+
* the cache behavior of the default function.
|
|
135
|
+
*
|
|
136
|
+
* @default require('dns').resolveSrv
|
|
137
|
+
*/
|
|
138
|
+
resolveSrv?: DNSResolveSrvFunction | undefined;
|
|
139
|
+
/**
|
|
140
|
+
* Hostnames will be resolved to IP addresses via this function.
|
|
141
|
+
* This is needed when the addresses of startup nodes are hostnames instead
|
|
142
|
+
* of IPs.
|
|
143
|
+
*
|
|
144
|
+
* You may provide a custom `lookup` function when you want to customize
|
|
145
|
+
* the cache behavior of the default function.
|
|
146
|
+
*
|
|
147
|
+
* @default require('dns').lookup
|
|
148
|
+
*/
|
|
149
|
+
dnsLookup?: DNSLookupFunction | undefined;
|
|
150
|
+
natMap?: NatMap | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* See Redis class.
|
|
153
|
+
*
|
|
154
|
+
* @default false
|
|
155
|
+
*/
|
|
156
|
+
enableAutoPipelining?: boolean | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* See Redis class.
|
|
159
|
+
*
|
|
160
|
+
* @default []
|
|
161
|
+
*/
|
|
162
|
+
autoPipeliningIgnoredCommands?: string[] | undefined;
|
|
163
|
+
/**
|
|
164
|
+
* Custom LUA commands
|
|
165
|
+
*/
|
|
166
|
+
scripts?: Record<string, {
|
|
167
|
+
lua: string;
|
|
168
|
+
numberOfKeys?: number;
|
|
169
|
+
readOnly?: boolean;
|
|
170
|
+
}> | undefined;
|
|
171
|
+
}
|
|
172
|
+
export declare const DEFAULT_CLUSTER_OPTIONS: ClusterOptions;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_CLUSTER_OPTIONS = void 0;
|
|
4
|
+
const dns_1 = require("dns");
|
|
5
|
+
exports.DEFAULT_CLUSTER_OPTIONS = {
|
|
6
|
+
clusterRetryStrategy: (times) => Math.min(100 + times * 2, 2000),
|
|
7
|
+
enableOfflineQueue: true,
|
|
8
|
+
enableReadyCheck: true,
|
|
9
|
+
scaleReads: "master",
|
|
10
|
+
maxRedirections: 16,
|
|
11
|
+
retryDelayOnMoved: 0,
|
|
12
|
+
retryDelayOnFailover: 100,
|
|
13
|
+
retryDelayOnClusterDown: 100,
|
|
14
|
+
retryDelayOnTryAgain: 100,
|
|
15
|
+
slotsRefreshTimeout: 1000,
|
|
16
|
+
useSRVRecords: false,
|
|
17
|
+
resolveSrv: dns_1.resolveSrv,
|
|
18
|
+
dnsLookup: dns_1.lookup,
|
|
19
|
+
enableAutoPipelining: false,
|
|
20
|
+
autoPipeliningIgnoredCommands: [],
|
|
21
|
+
shardedSubscribers: false,
|
|
22
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
import ConnectionPool from "./ConnectionPool";
|
|
4
|
+
export default class ClusterSubscriber {
|
|
5
|
+
private connectionPool;
|
|
6
|
+
private emitter;
|
|
7
|
+
private isSharded;
|
|
8
|
+
private started;
|
|
9
|
+
private subscriber;
|
|
10
|
+
private lastActiveSubscriber;
|
|
11
|
+
private slotRange;
|
|
12
|
+
constructor(connectionPool: ConnectionPool, emitter: EventEmitter, isSharded?: boolean);
|
|
13
|
+
getInstance(): any;
|
|
14
|
+
/**
|
|
15
|
+
* Associate this subscriber to a specific slot range.
|
|
16
|
+
*
|
|
17
|
+
* Returns the range or an empty array if the slot range couldn't be associated.
|
|
18
|
+
*
|
|
19
|
+
* BTW: This is more for debugging and testing purposes.
|
|
20
|
+
*
|
|
21
|
+
* @param range
|
|
22
|
+
*/
|
|
23
|
+
associateSlotRange(range: number[]): number[];
|
|
24
|
+
start(): void;
|
|
25
|
+
stop(): void;
|
|
26
|
+
isStarted(): boolean;
|
|
27
|
+
private onSubscriberEnd;
|
|
28
|
+
private selectSubscriber;
|
|
29
|
+
}
|