node-red-contrib-redis-variable 1.4.1 → 1.5.1
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/package.json +1 -1
- package/redis-variable-config.js +25 -69
- package/redis-variable.js +71 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-red-contrib-redis-variable",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "A comprehensive Node-RED node for Redis operations with universal payload-based configuration, automatic JSON handling, SSL/TLS support, and advanced pattern matching with pagination",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node-red",
|
package/redis-variable-config.js
CHANGED
|
@@ -43,11 +43,11 @@ module.exports = function (RED) {
|
|
|
43
43
|
// Helper function to get nested values like "redis_config.host"
|
|
44
44
|
function getNestedValue(context, path) {
|
|
45
45
|
if (!context) return undefined;
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
if (path.includes('.')) {
|
|
48
48
|
const parts = path.split('.');
|
|
49
49
|
let result = context.get(parts[0]);
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
// If the first part returns an object, traverse it
|
|
52
52
|
if (result && typeof result === 'object') {
|
|
53
53
|
for (let i = 1; i < parts.length; i++) {
|
|
@@ -71,7 +71,7 @@ module.exports = function (RED) {
|
|
|
71
71
|
RED.nodes.createNode(this, config);
|
|
72
72
|
this.name = config.name || "Redis Config";
|
|
73
73
|
this.cluster = config.cluster || false;
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
// Connection configuration
|
|
76
76
|
this.hostType = config.hostType || 'str';
|
|
77
77
|
this.host = config.host || 'localhost';
|
|
@@ -79,7 +79,7 @@ module.exports = function (RED) {
|
|
|
79
79
|
this.port = config.port || 6379;
|
|
80
80
|
this.portType = config.portType || 'str';
|
|
81
81
|
this.portContext = config.portContext;
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
// Authentication
|
|
84
84
|
this.passwordType = config.passwordType || 'str';
|
|
85
85
|
this.password = config.password;
|
|
@@ -87,7 +87,7 @@ module.exports = function (RED) {
|
|
|
87
87
|
this.username = config.username;
|
|
88
88
|
this.usernameType = config.usernameType || 'str';
|
|
89
89
|
this.usernameContext = config.usernameContext;
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
// SSL/TLS Configuration
|
|
92
92
|
this.enableTLS = config.enableTLS || false;
|
|
93
93
|
this.tlsRejectUnauthorized = config.tlsRejectUnauthorized !== false; // Default to true
|
|
@@ -97,12 +97,12 @@ module.exports = function (RED) {
|
|
|
97
97
|
this.tlsKeyContext = config.tlsKeyContext;
|
|
98
98
|
this.tlsCaType = config.tlsCaType || 'str';
|
|
99
99
|
this.tlsCaContext = config.tlsCaContext;
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
// Database and other options
|
|
102
102
|
this.database = config.database || 0;
|
|
103
103
|
this.databaseType = config.databaseType || 'str';
|
|
104
104
|
this.databaseContext = config.databaseContext;
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
// Advanced options
|
|
107
107
|
this.optionsType = config.optionsType || 'json';
|
|
108
108
|
this.options = config.options || '{}';
|
|
@@ -116,7 +116,7 @@ module.exports = function (RED) {
|
|
|
116
116
|
if (!value && value !== 0) {
|
|
117
117
|
return null;
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
try {
|
|
121
121
|
let result;
|
|
122
122
|
switch (type) {
|
|
@@ -164,11 +164,11 @@ module.exports = function (RED) {
|
|
|
164
164
|
if (this.hostType === 'str' && this.portType === 'str') {
|
|
165
165
|
return true;
|
|
166
166
|
}
|
|
167
|
-
|
|
167
|
+
|
|
168
168
|
// For context-based configuration, check if the required values exist
|
|
169
169
|
let hasHost = false;
|
|
170
170
|
let hasPort = false;
|
|
171
|
-
|
|
171
|
+
|
|
172
172
|
// Check host configuration
|
|
173
173
|
if (this.hostType === 'str') {
|
|
174
174
|
hasHost = !!(this.host);
|
|
@@ -176,7 +176,7 @@ module.exports = function (RED) {
|
|
|
176
176
|
const contextHost = this.parseCredentialValue(this.hostContext, this.hostType, msg, executingNode);
|
|
177
177
|
hasHost = !!(contextHost);
|
|
178
178
|
}
|
|
179
|
-
|
|
179
|
+
|
|
180
180
|
// Check port configuration
|
|
181
181
|
if (this.portType === 'str') {
|
|
182
182
|
hasPort = !!(this.port);
|
|
@@ -184,16 +184,16 @@ module.exports = function (RED) {
|
|
|
184
184
|
const contextPort = this.parseCredentialValue(this.portContext, this.portType, msg, executingNode);
|
|
185
185
|
hasPort = !!(contextPort);
|
|
186
186
|
}
|
|
187
|
-
|
|
187
|
+
|
|
188
188
|
// We need at least host and port to have a valid configuration
|
|
189
189
|
const result = hasHost && hasPort;
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
if (executingNode && process.env.NODE_RED_DEBUG) {
|
|
192
192
|
executingNode.log(`Configuration check - hasHost: ${hasHost}, hasPort: ${hasPort}, result: ${result}`);
|
|
193
193
|
}
|
|
194
|
-
|
|
194
|
+
|
|
195
195
|
return result;
|
|
196
|
-
|
|
196
|
+
|
|
197
197
|
} catch (error) {
|
|
198
198
|
if (executingNode) {
|
|
199
199
|
executingNode.error(`Error checking configuration: ${error.message}`);
|
|
@@ -339,7 +339,7 @@ module.exports = function (RED) {
|
|
|
339
339
|
this.getClient = function(msg, executingNode, nodeId) {
|
|
340
340
|
try {
|
|
341
341
|
const id = nodeId || this.id;
|
|
342
|
-
|
|
342
|
+
|
|
343
343
|
// Return existing connection if available
|
|
344
344
|
if (connections[id]) {
|
|
345
345
|
usedConn[id]++;
|
|
@@ -353,7 +353,7 @@ module.exports = function (RED) {
|
|
|
353
353
|
if (!executingNode._configWarningShown) {
|
|
354
354
|
executingNode.warn("Redis configuration not available in context. Skipping connection attempt.");
|
|
355
355
|
executingNode._configWarningShown = true;
|
|
356
|
-
|
|
356
|
+
|
|
357
357
|
// Reset warning flag after 30 seconds
|
|
358
358
|
setTimeout(() => {
|
|
359
359
|
if (executingNode) {
|
|
@@ -382,58 +382,14 @@ module.exports = function (RED) {
|
|
|
382
382
|
maxRetriesPerRequest: 0
|
|
383
383
|
};
|
|
384
384
|
|
|
385
|
-
// Create Redis client
|
|
386
385
|
// Create Redis client
|
|
387
386
|
let client;
|
|
388
387
|
if (this.cluster) {
|
|
389
|
-
//
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
// AWS ElastiCache cluster connection (TLS-safe)
|
|
394
|
-
const clusterNodes = [{ host, port }];
|
|
395
|
-
|
|
396
|
-
const clusterOptions = {
|
|
397
|
-
scaleReads: "slave",
|
|
398
|
-
enableReadyCheck: false,
|
|
399
|
-
slotsRefreshTimeout: 5000,
|
|
400
|
-
redisOptions: {
|
|
401
|
-
username: options.username || undefined,
|
|
402
|
-
password: options.password || undefined,
|
|
403
|
-
db: options.db || 0,
|
|
404
|
-
connectTimeout: 5000,
|
|
405
|
-
maxRetriesPerRequest: 3,
|
|
406
|
-
retryStrategy: (times) => Math.min(times * 200, 2000),
|
|
407
|
-
tls: this.enableTLS
|
|
408
|
-
? {
|
|
409
|
-
servername: host,
|
|
410
|
-
rejectUnauthorized: this.tlsRejectUnauthorized,
|
|
411
|
-
ca: this.credentials?.tlsCa || undefined,
|
|
412
|
-
cert: this.credentials?.tlsCert || undefined,
|
|
413
|
-
key: this.credentials?.tlsKey || undefined,
|
|
414
|
-
}
|
|
415
|
-
: undefined,
|
|
416
|
-
},
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
client = new Redis.Cluster(clusterNodes, clusterOptions);
|
|
388
|
+
// For cluster mode, options should be an array of nodes
|
|
389
|
+
const clusterNodes = Array.isArray(connectionOptions) ? connectionOptions : [connectionOptions];
|
|
390
|
+
client = new Redis.Cluster(clusterNodes);
|
|
420
391
|
} else {
|
|
421
|
-
|
|
422
|
-
client = new Redis({
|
|
423
|
-
...options,
|
|
424
|
-
enableReadyCheck: false,
|
|
425
|
-
connectTimeout: 5000,
|
|
426
|
-
maxRetriesPerRequest: 3,
|
|
427
|
-
tls: this.enableTLS
|
|
428
|
-
? {
|
|
429
|
-
servername: options.host,
|
|
430
|
-
rejectUnauthorized: this.tlsRejectUnauthorized,
|
|
431
|
-
ca: this.credentials?.tlsCa || undefined,
|
|
432
|
-
cert: this.credentials?.tlsCert || undefined,
|
|
433
|
-
key: this.credentials?.tlsKey || undefined,
|
|
434
|
-
}
|
|
435
|
-
: undefined,
|
|
436
|
-
});
|
|
392
|
+
client = new Redis(connectionOptions);
|
|
437
393
|
}
|
|
438
394
|
|
|
439
395
|
// Track error state to prevent spam
|
|
@@ -444,11 +400,11 @@ module.exports = function (RED) {
|
|
|
444
400
|
// Handle connection errors
|
|
445
401
|
client.on("error", (e) => {
|
|
446
402
|
const now = Date.now();
|
|
447
|
-
|
|
403
|
+
|
|
448
404
|
// Only report errors once per interval to prevent spam
|
|
449
405
|
if (!errorReported || (now - lastErrorTime) > ERROR_REPORT_INTERVAL) {
|
|
450
406
|
let errorMsg = `Redis connection error: ${e.message}`;
|
|
451
|
-
|
|
407
|
+
|
|
452
408
|
// Add specific diagnostics for common SSL issues
|
|
453
409
|
if (e.message.includes("Protocol error") || e.message.includes("\\u0015")) {
|
|
454
410
|
errorMsg += "\nThis usually indicates an SSL/TLS configuration issue. Try:";
|
|
@@ -457,13 +413,13 @@ module.exports = function (RED) {
|
|
|
457
413
|
errorMsg += "\n3. Check if your Redis server is configured for SSL";
|
|
458
414
|
errorMsg += "\n4. Enable 'Debug: Force No SSL' option for testing";
|
|
459
415
|
}
|
|
460
|
-
|
|
416
|
+
|
|
461
417
|
if (executingNode) {
|
|
462
418
|
executingNode.error(errorMsg, {});
|
|
463
419
|
} else {
|
|
464
420
|
this.error(errorMsg, {});
|
|
465
421
|
}
|
|
466
|
-
|
|
422
|
+
|
|
467
423
|
errorReported = true;
|
|
468
424
|
lastErrorTime = now;
|
|
469
425
|
}
|
package/redis-variable.js
CHANGED
|
@@ -4,7 +4,7 @@ module.exports = function (RED) {
|
|
|
4
4
|
function RedisVariableNode(config) {
|
|
5
5
|
RED.nodes.createNode(this, config);
|
|
6
6
|
const node = this;
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
// Node configuration
|
|
9
9
|
this.operation = config.operation || "get";
|
|
10
10
|
this.timeout = config.timeout || 0;
|
|
@@ -52,6 +52,19 @@ module.exports = function (RED) {
|
|
|
52
52
|
return str;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function stableStringify(value) {
|
|
56
|
+
if (value === null || value === undefined) return String(value);
|
|
57
|
+
if (Array.isArray(value)) {
|
|
58
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === 'object') {
|
|
61
|
+
const keys = Object.keys(value).sort();
|
|
62
|
+
const entries = keys.map((key) => `"${key}":${stableStringify(value[key])}`);
|
|
63
|
+
return `{${entries.join(',')}}`;
|
|
64
|
+
}
|
|
65
|
+
return JSON.stringify(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
55
68
|
// Handle different operations
|
|
56
69
|
switch (this.operation) {
|
|
57
70
|
case "subscribe":
|
|
@@ -309,6 +322,26 @@ module.exports = function (RED) {
|
|
|
309
322
|
return;
|
|
310
323
|
}
|
|
311
324
|
|
|
325
|
+
// Recreate client when connection options change (e.g., context-based config)
|
|
326
|
+
try {
|
|
327
|
+
const options = redisConfig.getConnectionOptions(msg, node);
|
|
328
|
+
const configKey = stableStringify({
|
|
329
|
+
cluster: redisConfig.cluster === true,
|
|
330
|
+
options
|
|
331
|
+
});
|
|
332
|
+
if (node._lastConnKey && node._lastConnKey !== configKey) {
|
|
333
|
+
redisConfig.forceDisconnect(node.id);
|
|
334
|
+
client = null;
|
|
335
|
+
}
|
|
336
|
+
node._lastConnKey = configKey;
|
|
337
|
+
} catch (configError) {
|
|
338
|
+
node.error(configError.message, msg);
|
|
339
|
+
msg.payload = { error: configError.message };
|
|
340
|
+
send(msg);
|
|
341
|
+
done();
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
312
345
|
// Only create client if not already created
|
|
313
346
|
try {
|
|
314
347
|
if (!client) {
|
|
@@ -322,11 +355,13 @@ module.exports = function (RED) {
|
|
|
322
355
|
return;
|
|
323
356
|
}
|
|
324
357
|
}
|
|
325
|
-
|
|
358
|
+
|
|
326
359
|
// Check if client is connected before proceeding
|
|
327
360
|
if (client.status !== 'ready' && client.status !== 'connect') {
|
|
328
361
|
// Try to connect if not connected
|
|
329
|
-
if (client.status === '
|
|
362
|
+
if (client.status === 'wait') {
|
|
363
|
+
await client.connect();
|
|
364
|
+
} else if (client.status === 'disconnect' || client.status === 'end') {
|
|
330
365
|
await client.connect();
|
|
331
366
|
} else {
|
|
332
367
|
// Force disconnect and recreate client for other error states
|
|
@@ -380,12 +415,25 @@ module.exports = function (RED) {
|
|
|
380
415
|
if (!payload.key) {
|
|
381
416
|
throw new Error("Missing key for SET operation. Use payload.key");
|
|
382
417
|
}
|
|
383
|
-
|
|
418
|
+
const payloadIsObject = payload && typeof payload === 'object' && !Array.isArray(payload);
|
|
419
|
+
const payloadHasKey = payloadIsObject && Object.prototype.hasOwnProperty.call(payload, 'key');
|
|
420
|
+
const payloadHasValue = payloadIsObject && (
|
|
421
|
+
Object.prototype.hasOwnProperty.call(payload, 'value') ||
|
|
422
|
+
Object.prototype.hasOwnProperty.call(payload, 'data')
|
|
423
|
+
);
|
|
424
|
+
let setValue;
|
|
425
|
+
if (payloadHasValue) {
|
|
426
|
+
setValue = payload.value !== undefined ? payload.value : payload.data;
|
|
427
|
+
} else if (payloadHasKey) {
|
|
428
|
+
setValue = undefined;
|
|
429
|
+
} else {
|
|
430
|
+
setValue = payload;
|
|
431
|
+
}
|
|
384
432
|
if (setValue === undefined) {
|
|
385
|
-
throw new Error("Missing value for SET operation. Use payload.value or payload
|
|
433
|
+
throw new Error("Missing value for SET operation. Use payload.value, payload.data, or payload as value");
|
|
386
434
|
}
|
|
387
435
|
setValue = smartSerialize(setValue);
|
|
388
|
-
|
|
436
|
+
|
|
389
437
|
// Support TTL
|
|
390
438
|
if (payload.ttl && payload.ttl > 0) {
|
|
391
439
|
response = await client.setex(payload.key, payload.ttl, setValue);
|
|
@@ -420,14 +468,14 @@ module.exports = function (RED) {
|
|
|
420
468
|
if (!pattern || typeof pattern !== 'string') {
|
|
421
469
|
throw new Error("Missing pattern for MATCH operation. Use payload.pattern or payload as string");
|
|
422
470
|
}
|
|
423
|
-
|
|
471
|
+
|
|
424
472
|
let count = payload.count || 100; // Default scan count
|
|
425
473
|
let startCursor = payload.cursor || payload.skip || 0; // Support cursor/skip for pagination
|
|
426
474
|
let allKeys = [];
|
|
427
475
|
let cursor = startCursor;
|
|
428
476
|
let maxIterations = 1000; // Prevent infinite loops
|
|
429
477
|
let iterations = 0;
|
|
430
|
-
|
|
478
|
+
|
|
431
479
|
// If skip is specified, we need to scan through keys without collecting them
|
|
432
480
|
if (payload.skip && payload.skip > 0) {
|
|
433
481
|
let skipped = 0;
|
|
@@ -438,7 +486,7 @@ module.exports = function (RED) {
|
|
|
438
486
|
const scanResult = await client.scan(cursor, 'MATCH', pattern, 'COUNT', count);
|
|
439
487
|
cursor = scanResult[0];
|
|
440
488
|
skipped += scanResult[1].length;
|
|
441
|
-
|
|
489
|
+
|
|
442
490
|
if (skipped >= payload.skip) {
|
|
443
491
|
// We've skipped enough, start collecting from this point
|
|
444
492
|
const excessSkipped = skipped - payload.skip;
|
|
@@ -450,7 +498,7 @@ module.exports = function (RED) {
|
|
|
450
498
|
}
|
|
451
499
|
} while (cursor !== 0 && cursor !== '0');
|
|
452
500
|
}
|
|
453
|
-
|
|
501
|
+
|
|
454
502
|
// Continue scanning to collect keys up to the limit
|
|
455
503
|
do {
|
|
456
504
|
if (iterations++ > maxIterations) {
|
|
@@ -459,15 +507,15 @@ module.exports = function (RED) {
|
|
|
459
507
|
const scanResult = await client.scan(cursor, 'MATCH', pattern, 'COUNT', count);
|
|
460
508
|
cursor = scanResult[0];
|
|
461
509
|
allKeys = allKeys.concat(scanResult[1]);
|
|
462
|
-
|
|
510
|
+
|
|
463
511
|
// Break early if we've found enough keys
|
|
464
512
|
if (allKeys.length >= count) {
|
|
465
513
|
allKeys = allKeys.slice(0, count); // Trim to exact count
|
|
466
514
|
break;
|
|
467
515
|
}
|
|
468
516
|
} while (cursor !== 0 && cursor !== '0');
|
|
469
|
-
|
|
470
|
-
msg.payload = {
|
|
517
|
+
|
|
518
|
+
msg.payload = {
|
|
471
519
|
pattern: pattern,
|
|
472
520
|
keys: allKeys,
|
|
473
521
|
count: allKeys.length,
|
|
@@ -487,7 +535,7 @@ module.exports = function (RED) {
|
|
|
487
535
|
throw new Error("Missing key for TTL operation. Use payload.key or payload as string");
|
|
488
536
|
}
|
|
489
537
|
response = await client.ttl(ttlKey);
|
|
490
|
-
msg.payload = {
|
|
538
|
+
msg.payload = {
|
|
491
539
|
key: ttlKey,
|
|
492
540
|
ttl: response,
|
|
493
541
|
status: response === -1 ? "no expiration" : response === -2 ? "key not found" : "expires in " + response + " seconds"
|
|
@@ -500,7 +548,7 @@ module.exports = function (RED) {
|
|
|
500
548
|
}
|
|
501
549
|
let expireSeconds = payload.ttl || payload.seconds || payload.value || 3600;
|
|
502
550
|
response = await client.expire(payload.key, expireSeconds);
|
|
503
|
-
msg.payload = {
|
|
551
|
+
msg.payload = {
|
|
504
552
|
success: response === 1,
|
|
505
553
|
key: payload.key,
|
|
506
554
|
ttl: expireSeconds,
|
|
@@ -514,7 +562,7 @@ module.exports = function (RED) {
|
|
|
514
562
|
throw new Error("Missing key for PERSIST operation. Use payload.key or payload as string");
|
|
515
563
|
}
|
|
516
564
|
response = await client.persist(persistKey);
|
|
517
|
-
msg.payload = {
|
|
565
|
+
msg.payload = {
|
|
518
566
|
success: response === 1,
|
|
519
567
|
key: persistKey,
|
|
520
568
|
message: response === 1 ? "Expiration removed" : "Key not found or no expiration"
|
|
@@ -695,7 +743,7 @@ module.exports = function (RED) {
|
|
|
695
743
|
}
|
|
696
744
|
} catch (redisError) {
|
|
697
745
|
// Handle Redis-specific errors (connection, command errors, etc.)
|
|
698
|
-
if (redisError.message.includes('ECONNREFUSED') ||
|
|
746
|
+
if (redisError.message.includes('ECONNREFUSED') ||
|
|
699
747
|
redisError.message.includes('ENOTFOUND') ||
|
|
700
748
|
redisError.message.includes('ETIMEDOUT')) {
|
|
701
749
|
// Connection-related errors - force disconnect to prevent dead connections
|
|
@@ -707,8 +755,8 @@ module.exports = function (RED) {
|
|
|
707
755
|
// Ignore disconnect errors
|
|
708
756
|
}
|
|
709
757
|
}
|
|
710
|
-
|
|
711
|
-
msg.payload = {
|
|
758
|
+
|
|
759
|
+
msg.payload = {
|
|
712
760
|
error: `Redis connection failed: ${redisError.message}`,
|
|
713
761
|
operation: node.operation,
|
|
714
762
|
retryable: true
|
|
@@ -716,7 +764,7 @@ module.exports = function (RED) {
|
|
|
716
764
|
node.status({ fill: "red", shape: "ring", text: "connection failed" });
|
|
717
765
|
} else {
|
|
718
766
|
// Other Redis errors (command errors, etc.)
|
|
719
|
-
msg.payload = {
|
|
767
|
+
msg.payload = {
|
|
720
768
|
error: `Redis operation failed: ${redisError.message}`,
|
|
721
769
|
operation: node.operation,
|
|
722
770
|
retryable: false
|
|
@@ -763,7 +811,7 @@ module.exports = function (RED) {
|
|
|
763
811
|
node.on("close", async (undeploy, done) => {
|
|
764
812
|
node.status({});
|
|
765
813
|
running = false;
|
|
766
|
-
|
|
814
|
+
|
|
767
815
|
if (node.operation === "instance" && node.location && node.topic) {
|
|
768
816
|
try {
|
|
769
817
|
node.context()[node.location].set(node.topic, null);
|
|
@@ -771,7 +819,7 @@ module.exports = function (RED) {
|
|
|
771
819
|
// Ignore errors when cleaning up context
|
|
772
820
|
}
|
|
773
821
|
}
|
|
774
|
-
|
|
822
|
+
|
|
775
823
|
if (client) {
|
|
776
824
|
try {
|
|
777
825
|
if (node.operation === 'subscribe' && node.topic) {
|
|
@@ -788,7 +836,7 @@ module.exports = function (RED) {
|
|
|
788
836
|
const nodeId = node.block ? node.id : redisConfig.id;
|
|
789
837
|
redisConfig.disconnect(nodeId);
|
|
790
838
|
}
|
|
791
|
-
|
|
839
|
+
|
|
792
840
|
client = null;
|
|
793
841
|
done();
|
|
794
842
|
});
|