@pi-r/redis 0.7.3 → 0.8.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/client/index.js +65 -50
- package/client/pool.d.ts +9 -0
- package/client/pool.js +36 -0
- package/package.json +4 -4
- package/types/index.d.ts +13 -5
package/client/index.js
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
exports.DB_SOURCE_TYPE = exports.DB_SOURCE_CLIENT =
|
|
2
|
+
exports.DB_SOURCE_TYPE = exports.DB_SOURCE_CLIENT = void 0;
|
|
3
|
+
exports.setCredential = setCredential;
|
|
4
|
+
exports.executeQuery = executeQuery;
|
|
5
|
+
exports.executeBatchQuery = executeBatchQuery;
|
|
6
|
+
exports.checkTimeout = checkTimeout;
|
|
3
7
|
const redis = require("redis");
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
const Db = require("@e-mc/db");
|
|
4
10
|
const util_1 = require("@e-mc/db/util");
|
|
5
11
|
const types_1 = require("@e-mc/types");
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
async close() {
|
|
13
|
-
return this.client.disconnect();
|
|
14
|
-
}
|
|
15
|
-
isEmpty() {
|
|
16
|
-
return this.closed;
|
|
12
|
+
const DbPool = require("@pi-r/redis/client/pool");
|
|
13
|
+
const POOL_STATE = {};
|
|
14
|
+
async function doScan(client, key, index, cursor = [0], iterations = [Infinity], options) {
|
|
15
|
+
if (!Array.isArray(cursor)) {
|
|
16
|
+
cursor = [cursor];
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
if (!Array.isArray(iterations)) {
|
|
19
|
+
iterations = [iterations];
|
|
20
20
|
}
|
|
21
|
+
let result = [], current = cursor[index] ?? 0, length = iterations[index] ?? Infinity;
|
|
22
|
+
do {
|
|
23
|
+
const item = await client.hScan(key, current, options);
|
|
24
|
+
result = result.concat(item.tuples);
|
|
25
|
+
current = item.cursor;
|
|
26
|
+
} while (current > 0 && --length > 0);
|
|
27
|
+
return result;
|
|
21
28
|
}
|
|
22
|
-
const POOL_STATE = {};
|
|
23
|
-
const POOL_ACTIVE = new WeakSet();
|
|
24
29
|
async function getClient(db, options, poolKey) {
|
|
25
30
|
const socket = options.socket ||= {};
|
|
26
31
|
if (!poolKey) {
|
|
@@ -36,16 +41,13 @@ async function getClient(db, options, poolKey) {
|
|
|
36
41
|
delete POOL_STATE[poolKey];
|
|
37
42
|
});
|
|
38
43
|
}
|
|
39
|
-
client.connect().then(() =>
|
|
44
|
+
client.connect().then(() => {
|
|
45
|
+
resolve(client);
|
|
46
|
+
}).catch((err) => {
|
|
47
|
+
reject(err);
|
|
48
|
+
});
|
|
40
49
|
});
|
|
41
50
|
}
|
|
42
|
-
function removePoolProperties(options) {
|
|
43
|
-
if (!options.uuidKey && ('socket' in options || 'isolationPoolOptions' in options) && POOL_ACTIVE.has(options)) {
|
|
44
|
-
return { ...options, socket: options.socket?.tls ? { tls: true } : undefined, isolationPoolOptions: undefined };
|
|
45
|
-
}
|
|
46
|
-
return options;
|
|
47
|
-
}
|
|
48
|
-
const getPoolKey = (options) => options.url && JSON.stringify(options);
|
|
49
51
|
async function setCredential(item) {
|
|
50
52
|
const credential = this.getCredential(item);
|
|
51
53
|
const options = item.options ||= {};
|
|
@@ -122,26 +124,24 @@ async function setCredential(item) {
|
|
|
122
124
|
poolOptions.acquireTimeoutMillis ??= timeout;
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
|
-
const poolKey =
|
|
127
|
+
const poolKey = DbPool.asString(client);
|
|
126
128
|
if ((pool = POOL_STATE[poolKey]) && !pool.closed) {
|
|
127
129
|
pool.add(item);
|
|
128
130
|
return;
|
|
129
131
|
}
|
|
130
132
|
try {
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
const instance = new DbPool(await getClient(redis, client, poolKey), poolKey, username && password ? { username, password } : undefined).add(item);
|
|
134
|
+
instance.parent = POOL_STATE;
|
|
135
|
+
instance.success = 1;
|
|
134
136
|
}
|
|
135
137
|
catch {
|
|
136
138
|
item.usePool = false;
|
|
137
139
|
delete client.isolationPoolOptions;
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
|
-
exports.setCredential = setCredential;
|
|
141
142
|
async function executeQuery(item, options) {
|
|
142
143
|
return (await executeBatchQuery.call(this, [item], options))[0] || [];
|
|
143
144
|
}
|
|
144
|
-
exports.executeQuery = executeQuery;
|
|
145
145
|
async function executeBatchQuery(batch, options = '', outResult) {
|
|
146
146
|
const length = batch.length;
|
|
147
147
|
if (length === 0) {
|
|
@@ -162,7 +162,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
162
162
|
parallel = false;
|
|
163
163
|
}
|
|
164
164
|
else if (parallel === undefined) {
|
|
165
|
-
parallel = !batch.some(item => item.parallel === false
|
|
165
|
+
parallel = !batch.some(item => item.parallel === false);
|
|
166
166
|
}
|
|
167
167
|
if (!parallel) {
|
|
168
168
|
outResult ||= new Array(length);
|
|
@@ -175,11 +175,10 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
175
175
|
item.transactionState = 64;
|
|
176
176
|
let client;
|
|
177
177
|
if (item.usePool) {
|
|
178
|
-
const pool = DbPool.findKey(POOL_STATE, item.usePool,
|
|
178
|
+
const pool = DbPool.findKey(POOL_STATE, item.usePool, DbPool.asString(credential), ...connectOnce ? [item, batch[0]] : [item]);
|
|
179
179
|
if (pool) {
|
|
180
|
-
client = pool.
|
|
180
|
+
client = await pool.getConnection(credential);
|
|
181
181
|
pool.connected = true;
|
|
182
|
-
POOL_ACTIVE.add(credential);
|
|
183
182
|
}
|
|
184
183
|
}
|
|
185
184
|
if (!client) {
|
|
@@ -245,7 +244,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
245
244
|
queryString += '_' + format;
|
|
246
245
|
}
|
|
247
246
|
if (ignoreCache !== 1) {
|
|
248
|
-
if (rows = this.getQueryResult(source,
|
|
247
|
+
if (rows = this.getQueryResult(source, DbPool.sanitize(credential), queryString, cacheValue)) {
|
|
249
248
|
++redisCache;
|
|
250
249
|
if (parallel) {
|
|
251
250
|
tasks[i] = Promise.resolve(rows);
|
|
@@ -288,8 +287,12 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
288
287
|
return;
|
|
289
288
|
}
|
|
290
289
|
const has = (n) => typeof n === 'number' && n > 0;
|
|
291
|
-
const failKey = () =>
|
|
292
|
-
|
|
290
|
+
const failKey = () => {
|
|
291
|
+
failed((0, types_1.errorMessage)(f, "Invalid key", Db.asString(k) || "Unknown"));
|
|
292
|
+
};
|
|
293
|
+
const failValue = () => {
|
|
294
|
+
failed((0, types_1.errorMessage)(f, "Invalid value", Db.asString(v) || "Unknown"));
|
|
295
|
+
};
|
|
293
296
|
const commandSet = (0, types_1.isPlainObject)(setOptions.command) && redis.commandOptions(setOptions.command);
|
|
294
297
|
let pending, codeMin = 0, EX, PX, EXAT, PXAT, KEEPTTL;
|
|
295
298
|
if (f === 'JSON') {
|
|
@@ -342,7 +345,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
342
345
|
m.path ||= path;
|
|
343
346
|
return m;
|
|
344
347
|
});
|
|
345
|
-
if (data.length) {
|
|
348
|
+
if (data.length > 0) {
|
|
346
349
|
pending = commandSet ? client.json.mSet(commandSet, data) : client.json.mSet(data);
|
|
347
350
|
}
|
|
348
351
|
break;
|
|
@@ -482,13 +485,17 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
482
485
|
failed((0, types_1.errorMessage)(f, "Invalid value", has(EX) ? 'EX: ' + EX : has(PX) ? 'PX: ' + PX : has(EXAT) ? 'EXAT: ' + EX : 'PXAT: ' + PXAT));
|
|
483
486
|
}
|
|
484
487
|
})
|
|
485
|
-
.catch((err) =>
|
|
488
|
+
.catch((err) => {
|
|
489
|
+
failed(err);
|
|
490
|
+
});
|
|
486
491
|
}
|
|
487
492
|
else {
|
|
488
493
|
success(target);
|
|
489
494
|
}
|
|
490
495
|
})
|
|
491
|
-
.catch((err) =>
|
|
496
|
+
.catch((err) => {
|
|
497
|
+
failed(err);
|
|
498
|
+
});
|
|
492
499
|
}
|
|
493
500
|
else {
|
|
494
501
|
failValue();
|
|
@@ -516,7 +523,7 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
516
523
|
const { query = '', schema, index } = target;
|
|
517
524
|
let idx = '';
|
|
518
525
|
if ((0, types_1.isObject)(schema)) {
|
|
519
|
-
idx = index || (0,
|
|
526
|
+
idx = index || (0, crypto_1.randomUUID)();
|
|
520
527
|
await (command ? client.ft.create(command, idx, schema, target.options) : client.ft.create(idx, schema, target.options));
|
|
521
528
|
}
|
|
522
529
|
try {
|
|
@@ -535,7 +542,9 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
535
542
|
}
|
|
536
543
|
finally {
|
|
537
544
|
if (idx && idx !== index) {
|
|
538
|
-
client.ft.dropIndex(idx).catch((err) =>
|
|
545
|
+
client.ft.dropIndex(idx).catch((err) => {
|
|
546
|
+
this.writeFail([`Unable to drop ${target === search ? 'search' : 'aggregate'} index`, idx], err, { type: 65536, fatal: false });
|
|
547
|
+
});
|
|
539
548
|
}
|
|
540
549
|
}
|
|
541
550
|
}
|
|
@@ -549,6 +558,9 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
549
558
|
case 'HVALS':
|
|
550
559
|
data = await Promise.all(key.map(async (k) => client.hVals(...command ? [command, k] : [k])));
|
|
551
560
|
break;
|
|
561
|
+
case 'HSCAN':
|
|
562
|
+
data = await Promise.all(key.map(async (k, index) => doScan(client, k, index, item.cursor, item.iterations, clientOptions.scan)));
|
|
563
|
+
break;
|
|
552
564
|
case 'JSON':
|
|
553
565
|
data = (await (command ? client.json.mGet(command, key.map(c => b(c)), item.path || '$') : client.json.mGet(key.map(c => b(c)), item.path || '$'))).flat();
|
|
554
566
|
break;
|
|
@@ -565,6 +577,9 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
565
577
|
case 'HVALS':
|
|
566
578
|
data = await client.hVals(...command ? [command, key] : [key]);
|
|
567
579
|
break;
|
|
580
|
+
case 'HSCAN':
|
|
581
|
+
data = await doScan(client, key, 0, item.cursor, item.iterations, clientOptions.scan);
|
|
582
|
+
break;
|
|
568
583
|
case 'JSON':
|
|
569
584
|
data = await (clientOptions.get ? client.json.get(...command ? [command, b(key), clientOptions.get] : [b(key), clientOptions.get]) : client.json.get(...command ? [command, b(key)] : [b(key)]));
|
|
570
585
|
break;
|
|
@@ -584,14 +599,14 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
584
599
|
throw (0, types_1.errorMessage)(source, "Missing database query");
|
|
585
600
|
}
|
|
586
601
|
this.add(item, 4);
|
|
587
|
-
resolve(this.setQueryResult(source,
|
|
602
|
+
resolve(this.setQueryResult(source, DbPool.sanitize(redisCredential || credential), queryString, rows, cacheValue));
|
|
588
603
|
}
|
|
589
604
|
catch (err) {
|
|
590
605
|
if (err instanceof Error) {
|
|
591
606
|
switch (err.message) {
|
|
592
607
|
case "The client is closed":
|
|
593
608
|
case "Disconnects client": {
|
|
594
|
-
const pool = item.usePool && DbPool.findKey(POOL_STATE, item.usePool,
|
|
609
|
+
const pool = item.usePool && DbPool.findKey(POOL_STATE, item.usePool, DbPool.asString(credential));
|
|
595
610
|
if (pool) {
|
|
596
611
|
await pool.detach(true);
|
|
597
612
|
}
|
|
@@ -618,16 +633,16 @@ async function executeBatchQuery(batch, options = '', outResult) {
|
|
|
618
633
|
}
|
|
619
634
|
}
|
|
620
635
|
return this.processRows(batch, tasks, {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
636
|
+
parallel,
|
|
637
|
+
disconnect() {
|
|
638
|
+
for (const item of clients) {
|
|
639
|
+
void item.disconnect();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
625
642
|
}, outResult);
|
|
626
643
|
}
|
|
627
|
-
exports.executeBatchQuery = executeBatchQuery;
|
|
628
644
|
async function checkTimeout(value, limit = 0) {
|
|
629
645
|
return DbPool.checkTimeout(POOL_STATE, value, limit);
|
|
630
646
|
}
|
|
631
|
-
exports.checkTimeout = checkTimeout;
|
|
632
647
|
exports.DB_SOURCE_CLIENT = true;
|
|
633
648
|
exports.DB_SOURCE_TYPE = types_1.DB_TYPE.NOSQL | types_1.DB_TYPE.KEYVALUE;
|
package/client/pool.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DbPoolConstructor } from '@e-mc/types/lib/db';
|
|
2
|
+
|
|
3
|
+
import type { DbPoolCredential, RedisDataSource } from '../types';
|
|
4
|
+
|
|
5
|
+
import type { RedisClientType } from 'redis';
|
|
6
|
+
|
|
7
|
+
declare const RedisPool: DbPoolConstructor<RedisDataSource, RedisClientType, RedisClientType, DbPoolCredential>;
|
|
8
|
+
|
|
9
|
+
export = RedisPool;
|
package/client/pool.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const DbPool = require('@e-mc/db/pool');
|
|
3
|
+
const POOL_ACTIVE = new WeakSet();
|
|
4
|
+
class RedisPool extends DbPool {
|
|
5
|
+
static asString(credential) {
|
|
6
|
+
if (credential.url) {
|
|
7
|
+
return JSON.stringify(credential);
|
|
8
|
+
}
|
|
9
|
+
return super.asString(credential);
|
|
10
|
+
}
|
|
11
|
+
static sanitize(credential) {
|
|
12
|
+
if (!POOL_ACTIVE.has(credential) || credential.uuidKey) {
|
|
13
|
+
return credential;
|
|
14
|
+
}
|
|
15
|
+
if ('socket' in credential || 'isolationPoolOptions' in credential) {
|
|
16
|
+
return { ...credential, socket: credential.socket?.tls ? { tls: true } : undefined, isolationPoolOptions: undefined };
|
|
17
|
+
}
|
|
18
|
+
return credential;
|
|
19
|
+
}
|
|
20
|
+
async getConnection(credential) {
|
|
21
|
+
if (credential) {
|
|
22
|
+
POOL_ACTIVE.add(credential);
|
|
23
|
+
}
|
|
24
|
+
return this.client;
|
|
25
|
+
}
|
|
26
|
+
async close() {
|
|
27
|
+
return this.client.disconnect();
|
|
28
|
+
}
|
|
29
|
+
isEmpty() {
|
|
30
|
+
return this.closed;
|
|
31
|
+
}
|
|
32
|
+
get closed() {
|
|
33
|
+
return !this.client.isOpen;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
module.exports = RedisPool;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-r/redis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Redis client driver for E-mc.",
|
|
5
5
|
"main": "client/index.js",
|
|
6
6
|
"types": "client/index.d.ts",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"homepage": "https://github.com/anpham6/pi-r#readme",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@e-mc/db": "^0.
|
|
25
|
-
"@e-mc/types": "^0.
|
|
26
|
-
"redis": "^4.
|
|
24
|
+
"@e-mc/db": "^0.10.0",
|
|
25
|
+
"@e-mc/types": "^0.10.0",
|
|
26
|
+
"redis": "^4.7.0"
|
|
27
27
|
}
|
|
28
28
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type { DbDataSource } from '@e-mc/types/lib/squared';
|
|
2
2
|
|
|
3
|
+
import type { IdentifierAction } from '@e-mc/types/lib/core';
|
|
3
4
|
import type { CascadeAction, ServerAuth } from '@e-mc/types/lib/db';
|
|
4
5
|
|
|
5
|
-
import type { CommandOptions } from '@redis/client/dist/lib/command-options';
|
|
6
6
|
import type { ClientCommandOptions } from '@redis/client/dist/lib/client';
|
|
7
|
-
import type { RedisClientOptions } from '@redis/client';
|
|
8
7
|
import type { RedisCommandArgument } from '@redis/client/dist/lib/commands';
|
|
8
|
+
import type { CommandOptions } from '@redis/client/dist/lib/command-options';
|
|
9
|
+
import type { RedisClientOptions } from '@redis/client';
|
|
9
10
|
import type { RedisJSON } from '@redis/json/dist/commands';
|
|
11
|
+
import type { AggregateOptions } from '@redis/search/dist/commands/AGGREGATE';
|
|
12
|
+
import type { HScanTuple } from '@redis//client/dist/lib/commands/HSCAN';
|
|
13
|
+
import type { ScanOptions } from '@redis/client/dist/lib/commands/generic-transformers';
|
|
10
14
|
import type { RediSearchSchema, SearchOptions, SetOptions } from 'redis';
|
|
11
15
|
|
|
12
16
|
export interface RedisDataSource extends DbDataSource<string, PlainObject, ArrayOf<RedisSetValue> | ArrayOf<RedisJSONValue>, RedisCredential, string>, CascadeAction, AuthValue {
|
|
@@ -14,15 +18,18 @@ export interface RedisDataSource extends DbDataSource<string, PlainObject, Array
|
|
|
14
18
|
key?: ArrayOf<RedisCommandArgument>;
|
|
15
19
|
field?: RedisCommandArgument;
|
|
16
20
|
path?: string;
|
|
17
|
-
format?: RedisFormat | "HKEYS" | "HVALS";
|
|
21
|
+
format?: RedisFormat | "HKEYS" | "HVALS" | "HSCAN";
|
|
18
22
|
search?: RedisQuery;
|
|
19
23
|
aggregate?: RedisQuery;
|
|
24
|
+
cursor?: ArrayOf<number>;
|
|
25
|
+
iterations?: ArrayOf<number>;
|
|
20
26
|
options?: {
|
|
21
27
|
client?: RedisClientOptions;
|
|
22
28
|
command?: RedisCommandOptions;
|
|
23
29
|
get?: PlainObject;
|
|
24
30
|
search?: SearchOptions;
|
|
25
|
-
aggregate?:
|
|
31
|
+
aggregate?: AggregateOptions;
|
|
32
|
+
scan?: ScanOptions;
|
|
26
33
|
};
|
|
27
34
|
database?: number;
|
|
28
35
|
}
|
|
@@ -79,5 +86,6 @@ export type RedisExpireCondition = "NX" | "XX" | "GT" | "LT";
|
|
|
79
86
|
export type RedisCommandValue = RedisCommandArgument | number;
|
|
80
87
|
export type RedisCommandOptions = CommandOptions<ClientCommandOptions>;
|
|
81
88
|
export type RedisHSETObject = Record<number | string, RedisCommandValue>;
|
|
89
|
+
export type DbPoolCredential = RedisClientOptions & IdentifierAction;
|
|
82
90
|
|
|
83
|
-
export type { RedisCommandArgument, RedisJSON };
|
|
91
|
+
export type { HScanTuple, RedisCommandArgument, RedisJSON, ScanOptions };
|