@keyv/redis 5.1.0 → 5.1.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/README.md +66 -16
- package/dist/index.cjs +75 -50
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +76 -53
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ Redis storage adapter for [Keyv](https://github.com/jaredwray/keyv).
|
|
|
38
38
|
* [Clustering](#clustering)
|
|
39
39
|
* [Sentinel](#sentinel)
|
|
40
40
|
* [TLS Support](#tls-support)
|
|
41
|
+
* [Keyv Redis Options](#keyv-redis-options)
|
|
41
42
|
* [API](#api)
|
|
42
43
|
* [Using Custom Redis Client Events](#using-custom-redis-client-events)
|
|
43
44
|
* [Migrating from v3 to v4](#migrating-from-v3-to-v4)
|
|
@@ -79,21 +80,20 @@ Here you can pass in the Redis options directly:
|
|
|
79
80
|
import Keyv from 'keyv';
|
|
80
81
|
import KeyvRedis from '@keyv/redis';
|
|
81
82
|
|
|
82
|
-
const
|
|
83
|
-
url: 'redis://localhost:6379', // The Redis server URL (use 'rediss' for TLS)
|
|
84
|
-
password: 'your_password', // Optional password if Redis has authentication enabled
|
|
83
|
+
const uri = "redis://localhost:6379";
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
85
|
+
// NOTE: please use the settings that you need to configure. Check out Keyv Redis Options section
|
|
86
|
+
const options = {
|
|
87
|
+
namespace: "test",
|
|
88
|
+
keyPrefixSeparator: "->",
|
|
89
|
+
clearBatchSize: 100,
|
|
90
|
+
useUnlink: true,
|
|
91
|
+
noNamespaceAffectsAll: true,
|
|
94
92
|
};
|
|
95
93
|
|
|
96
|
-
const
|
|
94
|
+
const keyvRedis = new KeyvRedis(uri, options);
|
|
95
|
+
|
|
96
|
+
const keyv = new Keyv(keyvRedis);
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
Or you can create a new Redis instance and pass it in with `KeyvOptions` such as setting the `store`:
|
|
@@ -465,6 +465,60 @@ const tlsOptions = {
|
|
|
465
465
|
const keyv = new Keyv({ store: new KeyvRedis(tlsOptions) });
|
|
466
466
|
```
|
|
467
467
|
|
|
468
|
+
# Keyv Redis Options
|
|
469
|
+
|
|
470
|
+
Here are all the options that you can set on the constructor
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
export type KeyvRedisOptions = {
|
|
474
|
+
/**
|
|
475
|
+
* Namespace for the current instance.
|
|
476
|
+
*/
|
|
477
|
+
namespace?: string;
|
|
478
|
+
/**
|
|
479
|
+
* Separator to use between namespace and key.
|
|
480
|
+
*/
|
|
481
|
+
keyPrefixSeparator?: string;
|
|
482
|
+
/**
|
|
483
|
+
* Number of keys to delete in a single batch.
|
|
484
|
+
*/
|
|
485
|
+
clearBatchSize?: number;
|
|
486
|
+
/**
|
|
487
|
+
* Enable Unlink instead of using Del for clearing keys. This is more performant but may not be supported by all Redis versions.
|
|
488
|
+
*/
|
|
489
|
+
useUnlink?: boolean;
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Whether to allow clearing all keys when no namespace is set.
|
|
493
|
+
* If set to true and no namespace is set, iterate() will return all keys.
|
|
494
|
+
* Defaults to `false`.
|
|
495
|
+
*/
|
|
496
|
+
noNamespaceAffectsAll?: boolean;
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
500
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
501
|
+
*/
|
|
502
|
+
throwOnConnectError?: boolean;
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
506
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
507
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
508
|
+
* and returns no-op responses.
|
|
509
|
+
* @default false
|
|
510
|
+
*/
|
|
511
|
+
throwOnErrors?: boolean;
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
|
|
515
|
+
* If set, it will throw an error if the connection does not succeed within the specified time.
|
|
516
|
+
* @default undefined
|
|
517
|
+
*/
|
|
518
|
+
connectionTimeout?: number;
|
|
519
|
+
};
|
|
520
|
+
```
|
|
521
|
+
|
|
468
522
|
# API
|
|
469
523
|
* **constructor([connection], [options])**
|
|
470
524
|
* **namespace** - The namespace to use for the keys.
|
|
@@ -530,14 +584,10 @@ const keyv = new Keyv({ store: redis, namespace: 'my-namespace', useKeyPrefix: f
|
|
|
530
584
|
|
|
531
585
|
This will make it so the storage adapter `@keyv/redis` will handle the namespace and not the `keyv` instance. If you leave it on it will just look duplicated like `my-namespace:my-namespace:key`.
|
|
532
586
|
|
|
533
|
-
|
|
534
|
-
|
|
535
587
|
# About Redis Sets and its Support in v4
|
|
536
588
|
|
|
537
589
|
We no longer support redis sets. This is due to the fact that it caused significant performance issues and was not a good fit for the library.
|
|
538
590
|
|
|
539
|
-
|
|
540
|
-
|
|
541
591
|
# Using with NestJS
|
|
542
592
|
|
|
543
593
|
> You can integrate `@keyv/redis` with NestJS by creating a custom `CacheModule`. This allows you to use Keyv as a cache store in your application.
|
package/dist/index.cjs
CHANGED
|
@@ -42,9 +42,9 @@ __export(index_exports, {
|
|
|
42
42
|
});
|
|
43
43
|
module.exports = __toCommonJS(index_exports);
|
|
44
44
|
var import_client = require("@redis/client");
|
|
45
|
+
var import_cluster_key_slot = __toESM(require("cluster-key-slot"), 1);
|
|
45
46
|
var import_hookified = require("hookified");
|
|
46
47
|
var import_keyv = require("keyv");
|
|
47
|
-
var import_cluster_key_slot = __toESM(require("cluster-key-slot"), 1);
|
|
48
48
|
var import_client2 = require("@redis/client");
|
|
49
49
|
var import_keyv2 = require("keyv");
|
|
50
50
|
var RedisErrorMessages = /* @__PURE__ */ ((RedisErrorMessages2) => {
|
|
@@ -79,7 +79,10 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
79
79
|
};
|
|
80
80
|
if (connect) {
|
|
81
81
|
if (typeof connect === "string") {
|
|
82
|
-
this._client = (0, import_client.createClient)({
|
|
82
|
+
this._client = (0, import_client.createClient)({
|
|
83
|
+
url: connect,
|
|
84
|
+
socket
|
|
85
|
+
});
|
|
83
86
|
} else if (connect.connect !== void 0) {
|
|
84
87
|
if (this.isClientSentinel(connect)) {
|
|
85
88
|
this._client = connect;
|
|
@@ -90,9 +93,13 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
90
93
|
}
|
|
91
94
|
} else if (connect instanceof Object) {
|
|
92
95
|
if (connect.sentinelRootNodes !== void 0) {
|
|
93
|
-
this._client = (0, import_client.createSentinel)(
|
|
96
|
+
this._client = (0, import_client.createSentinel)(
|
|
97
|
+
connect
|
|
98
|
+
);
|
|
94
99
|
} else if (connect.rootNodes === void 0) {
|
|
95
|
-
this._client = (0, import_client.createClient)(
|
|
100
|
+
this._client = (0, import_client.createClient)(
|
|
101
|
+
connect
|
|
102
|
+
);
|
|
96
103
|
} else {
|
|
97
104
|
this._client = (0, import_client.createCluster)(connect);
|
|
98
105
|
}
|
|
@@ -373,7 +380,9 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
373
380
|
multi.exists(prefixedKey);
|
|
374
381
|
}
|
|
375
382
|
const results = await multi.exec();
|
|
376
|
-
return results.map(
|
|
383
|
+
return results.map(
|
|
384
|
+
(result) => typeof result === "number" && result === 1
|
|
385
|
+
);
|
|
377
386
|
} catch (error) {
|
|
378
387
|
this.emit("error", error);
|
|
379
388
|
if (this._throwOnErrors) {
|
|
@@ -534,7 +543,9 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
534
543
|
async getMasterNodes() {
|
|
535
544
|
if (this.isCluster()) {
|
|
536
545
|
const cluster = await this.getClient();
|
|
537
|
-
const nodes = cluster.masters.map(
|
|
546
|
+
const nodes = cluster.masters.map(
|
|
547
|
+
async (main) => cluster.nodeClient(main)
|
|
548
|
+
);
|
|
538
549
|
return Promise.all(nodes);
|
|
539
550
|
}
|
|
540
551
|
return [await this.getClient()];
|
|
@@ -550,7 +561,10 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
550
561
|
const match = namespace ? `${namespace}${this._keyPrefixSeparator}*` : "*";
|
|
551
562
|
let cursor = "0";
|
|
552
563
|
do {
|
|
553
|
-
const result = await client.scan(cursor, {
|
|
564
|
+
const result = await client.scan(cursor, {
|
|
565
|
+
MATCH: match,
|
|
566
|
+
TYPE: "string"
|
|
567
|
+
});
|
|
554
568
|
cursor = result.cursor.toString();
|
|
555
569
|
let { keys } = result;
|
|
556
570
|
if (!namespace && !this._noNamespaceAffectsAll) {
|
|
@@ -577,29 +591,37 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
577
591
|
async clear() {
|
|
578
592
|
try {
|
|
579
593
|
const clients = await this.getMasterNodes();
|
|
580
|
-
await Promise.all(
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
let cursor = "0";
|
|
586
|
-
const batchSize = this._clearBatchSize;
|
|
587
|
-
const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
|
|
588
|
-
const deletePromises = [];
|
|
589
|
-
do {
|
|
590
|
-
const result = await client.scan(cursor, { MATCH: match, COUNT: batchSize, TYPE: "string" });
|
|
591
|
-
cursor = result.cursor.toString();
|
|
592
|
-
let { keys } = result;
|
|
593
|
-
if (keys.length === 0) {
|
|
594
|
-
continue;
|
|
594
|
+
await Promise.all(
|
|
595
|
+
clients.map(async (client) => {
|
|
596
|
+
if (!this._namespace && this._noNamespaceAffectsAll) {
|
|
597
|
+
await client.flushDb();
|
|
598
|
+
return;
|
|
595
599
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
-
deletePromises
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
600
|
+
let cursor = "0";
|
|
601
|
+
const batchSize = this._clearBatchSize;
|
|
602
|
+
const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
|
|
603
|
+
const deletePromises = [];
|
|
604
|
+
do {
|
|
605
|
+
const result = await client.scan(cursor, {
|
|
606
|
+
MATCH: match,
|
|
607
|
+
COUNT: batchSize,
|
|
608
|
+
TYPE: "string"
|
|
609
|
+
});
|
|
610
|
+
cursor = result.cursor.toString();
|
|
611
|
+
let { keys } = result;
|
|
612
|
+
if (keys.length === 0) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (!this._namespace) {
|
|
616
|
+
keys = keys.filter(
|
|
617
|
+
(key) => !key.includes(this._keyPrefixSeparator)
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
deletePromises.push(this.clearWithClusterSupport(keys));
|
|
621
|
+
} while (cursor !== "0");
|
|
622
|
+
await Promise.all(deletePromises);
|
|
623
|
+
})
|
|
624
|
+
);
|
|
603
625
|
} catch (error) {
|
|
604
626
|
this.emit("error", error);
|
|
605
627
|
}
|
|
@@ -611,13 +633,15 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
611
633
|
async mget(keys) {
|
|
612
634
|
const slotMap = this.getSlotMap(keys);
|
|
613
635
|
const valueMap = /* @__PURE__ */ new Map();
|
|
614
|
-
await Promise.all(
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
636
|
+
await Promise.all(
|
|
637
|
+
Array.from(slotMap.entries(), async ([slot, keys2]) => {
|
|
638
|
+
const client = await this.getSlotMaster(slot);
|
|
639
|
+
const values = await client.mGet(keys2);
|
|
640
|
+
for (const [index, value] of values.entries()) {
|
|
641
|
+
valueMap.set(keys2[index], value ?? void 0);
|
|
642
|
+
}
|
|
643
|
+
})
|
|
644
|
+
);
|
|
621
645
|
return keys.map((key) => valueMap.get(key));
|
|
622
646
|
}
|
|
623
647
|
/**
|
|
@@ -627,10 +651,12 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
627
651
|
async clearWithClusterSupport(keys) {
|
|
628
652
|
if (keys.length > 0) {
|
|
629
653
|
const slotMap = this.getSlotMap(keys);
|
|
630
|
-
await Promise.all(
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
654
|
+
await Promise.all(
|
|
655
|
+
Array.from(slotMap.entries(), async ([slot, keys2]) => {
|
|
656
|
+
const client = await this.getSlotMaster(slot);
|
|
657
|
+
return this._useUnlink ? client.unlink(keys2) : client.del(keys2);
|
|
658
|
+
})
|
|
659
|
+
);
|
|
634
660
|
}
|
|
635
661
|
}
|
|
636
662
|
/**
|
|
@@ -712,15 +738,11 @@ var KeyvRedis = class extends import_hookified.Hookified {
|
|
|
712
738
|
});
|
|
713
739
|
}
|
|
714
740
|
async createTimeoutPromise(timeoutMs) {
|
|
715
|
-
return new Promise(
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
},
|
|
721
|
-
timeoutMs
|
|
722
|
-
)
|
|
723
|
-
));
|
|
741
|
+
return new Promise(
|
|
742
|
+
(_, reject) => setTimeout(() => {
|
|
743
|
+
reject(new Error(`Redis timed out after ${timeoutMs}ms`));
|
|
744
|
+
}, timeoutMs)
|
|
745
|
+
);
|
|
724
746
|
}
|
|
725
747
|
};
|
|
726
748
|
function createKeyv(connect, options) {
|
|
@@ -728,7 +750,10 @@ function createKeyv(connect, options) {
|
|
|
728
750
|
const adapter = new KeyvRedis(connect, options);
|
|
729
751
|
if (options?.namespace) {
|
|
730
752
|
adapter.namespace = options.namespace;
|
|
731
|
-
const keyv2 = new import_keyv.Keyv(adapter, {
|
|
753
|
+
const keyv2 = new import_keyv.Keyv(adapter, {
|
|
754
|
+
namespace: options?.namespace,
|
|
755
|
+
useKeyPrefix: false
|
|
756
|
+
});
|
|
732
757
|
if (options?.throwOnConnectError) {
|
|
733
758
|
keyv2.throwOnErrors = true;
|
|
734
759
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -51,7 +51,7 @@ type KeyvRedisPropertyOptions = KeyvRedisOptions & {
|
|
|
51
51
|
/**
|
|
52
52
|
* Dialect used by the adapter. This is legacy so Keyv knows what is iteratable.
|
|
53
53
|
*/
|
|
54
|
-
dialect:
|
|
54
|
+
dialect: "redis";
|
|
55
55
|
/**
|
|
56
56
|
* URL used to connect to the Redis server. This is legacy so Keyv knows what is iteratable.
|
|
57
57
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ type KeyvRedisPropertyOptions = KeyvRedisOptions & {
|
|
|
51
51
|
/**
|
|
52
52
|
* Dialect used by the adapter. This is legacy so Keyv knows what is iteratable.
|
|
53
53
|
*/
|
|
54
|
-
dialect:
|
|
54
|
+
dialect: "redis";
|
|
55
55
|
/**
|
|
56
56
|
* URL used to connect to the Redis server. This is legacy so Keyv knows what is iteratable.
|
|
57
57
|
*/
|
package/dist/index.js
CHANGED
|
@@ -4,17 +4,15 @@ import {
|
|
|
4
4
|
createCluster,
|
|
5
5
|
createSentinel
|
|
6
6
|
} from "@redis/client";
|
|
7
|
+
import calculateSlot from "cluster-key-slot";
|
|
7
8
|
import { Hookified } from "hookified";
|
|
8
9
|
import { Keyv } from "keyv";
|
|
9
|
-
import calculateSlot from "cluster-key-slot";
|
|
10
10
|
import {
|
|
11
11
|
createClient as createClient2,
|
|
12
12
|
createCluster as createCluster2,
|
|
13
13
|
createSentinel as createSentinel2
|
|
14
14
|
} from "@redis/client";
|
|
15
|
-
import {
|
|
16
|
-
Keyv as Keyv2
|
|
17
|
-
} from "keyv";
|
|
15
|
+
import { Keyv as Keyv2 } from "keyv";
|
|
18
16
|
var RedisErrorMessages = /* @__PURE__ */ ((RedisErrorMessages2) => {
|
|
19
17
|
RedisErrorMessages2["RedisClientNotConnectedThrown"] = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true.";
|
|
20
18
|
return RedisErrorMessages2;
|
|
@@ -47,7 +45,10 @@ var KeyvRedis = class extends Hookified {
|
|
|
47
45
|
};
|
|
48
46
|
if (connect) {
|
|
49
47
|
if (typeof connect === "string") {
|
|
50
|
-
this._client = createClient({
|
|
48
|
+
this._client = createClient({
|
|
49
|
+
url: connect,
|
|
50
|
+
socket
|
|
51
|
+
});
|
|
51
52
|
} else if (connect.connect !== void 0) {
|
|
52
53
|
if (this.isClientSentinel(connect)) {
|
|
53
54
|
this._client = connect;
|
|
@@ -58,9 +59,13 @@ var KeyvRedis = class extends Hookified {
|
|
|
58
59
|
}
|
|
59
60
|
} else if (connect instanceof Object) {
|
|
60
61
|
if (connect.sentinelRootNodes !== void 0) {
|
|
61
|
-
this._client = createSentinel(
|
|
62
|
+
this._client = createSentinel(
|
|
63
|
+
connect
|
|
64
|
+
);
|
|
62
65
|
} else if (connect.rootNodes === void 0) {
|
|
63
|
-
this._client = createClient(
|
|
66
|
+
this._client = createClient(
|
|
67
|
+
connect
|
|
68
|
+
);
|
|
64
69
|
} else {
|
|
65
70
|
this._client = createCluster(connect);
|
|
66
71
|
}
|
|
@@ -341,7 +346,9 @@ var KeyvRedis = class extends Hookified {
|
|
|
341
346
|
multi.exists(prefixedKey);
|
|
342
347
|
}
|
|
343
348
|
const results = await multi.exec();
|
|
344
|
-
return results.map(
|
|
349
|
+
return results.map(
|
|
350
|
+
(result) => typeof result === "number" && result === 1
|
|
351
|
+
);
|
|
345
352
|
} catch (error) {
|
|
346
353
|
this.emit("error", error);
|
|
347
354
|
if (this._throwOnErrors) {
|
|
@@ -502,7 +509,9 @@ var KeyvRedis = class extends Hookified {
|
|
|
502
509
|
async getMasterNodes() {
|
|
503
510
|
if (this.isCluster()) {
|
|
504
511
|
const cluster = await this.getClient();
|
|
505
|
-
const nodes = cluster.masters.map(
|
|
512
|
+
const nodes = cluster.masters.map(
|
|
513
|
+
async (main) => cluster.nodeClient(main)
|
|
514
|
+
);
|
|
506
515
|
return Promise.all(nodes);
|
|
507
516
|
}
|
|
508
517
|
return [await this.getClient()];
|
|
@@ -518,7 +527,10 @@ var KeyvRedis = class extends Hookified {
|
|
|
518
527
|
const match = namespace ? `${namespace}${this._keyPrefixSeparator}*` : "*";
|
|
519
528
|
let cursor = "0";
|
|
520
529
|
do {
|
|
521
|
-
const result = await client.scan(cursor, {
|
|
530
|
+
const result = await client.scan(cursor, {
|
|
531
|
+
MATCH: match,
|
|
532
|
+
TYPE: "string"
|
|
533
|
+
});
|
|
522
534
|
cursor = result.cursor.toString();
|
|
523
535
|
let { keys } = result;
|
|
524
536
|
if (!namespace && !this._noNamespaceAffectsAll) {
|
|
@@ -545,29 +557,37 @@ var KeyvRedis = class extends Hookified {
|
|
|
545
557
|
async clear() {
|
|
546
558
|
try {
|
|
547
559
|
const clients = await this.getMasterNodes();
|
|
548
|
-
await Promise.all(
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
let cursor = "0";
|
|
554
|
-
const batchSize = this._clearBatchSize;
|
|
555
|
-
const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
|
|
556
|
-
const deletePromises = [];
|
|
557
|
-
do {
|
|
558
|
-
const result = await client.scan(cursor, { MATCH: match, COUNT: batchSize, TYPE: "string" });
|
|
559
|
-
cursor = result.cursor.toString();
|
|
560
|
-
let { keys } = result;
|
|
561
|
-
if (keys.length === 0) {
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
if (!this._namespace) {
|
|
565
|
-
keys = keys.filter((key) => !key.includes(this._keyPrefixSeparator));
|
|
560
|
+
await Promise.all(
|
|
561
|
+
clients.map(async (client) => {
|
|
562
|
+
if (!this._namespace && this._noNamespaceAffectsAll) {
|
|
563
|
+
await client.flushDb();
|
|
564
|
+
return;
|
|
566
565
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
566
|
+
let cursor = "0";
|
|
567
|
+
const batchSize = this._clearBatchSize;
|
|
568
|
+
const match = this._namespace ? `${this._namespace}${this._keyPrefixSeparator}*` : "*";
|
|
569
|
+
const deletePromises = [];
|
|
570
|
+
do {
|
|
571
|
+
const result = await client.scan(cursor, {
|
|
572
|
+
MATCH: match,
|
|
573
|
+
COUNT: batchSize,
|
|
574
|
+
TYPE: "string"
|
|
575
|
+
});
|
|
576
|
+
cursor = result.cursor.toString();
|
|
577
|
+
let { keys } = result;
|
|
578
|
+
if (keys.length === 0) {
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (!this._namespace) {
|
|
582
|
+
keys = keys.filter(
|
|
583
|
+
(key) => !key.includes(this._keyPrefixSeparator)
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
deletePromises.push(this.clearWithClusterSupport(keys));
|
|
587
|
+
} while (cursor !== "0");
|
|
588
|
+
await Promise.all(deletePromises);
|
|
589
|
+
})
|
|
590
|
+
);
|
|
571
591
|
} catch (error) {
|
|
572
592
|
this.emit("error", error);
|
|
573
593
|
}
|
|
@@ -579,13 +599,15 @@ var KeyvRedis = class extends Hookified {
|
|
|
579
599
|
async mget(keys) {
|
|
580
600
|
const slotMap = this.getSlotMap(keys);
|
|
581
601
|
const valueMap = /* @__PURE__ */ new Map();
|
|
582
|
-
await Promise.all(
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
602
|
+
await Promise.all(
|
|
603
|
+
Array.from(slotMap.entries(), async ([slot, keys2]) => {
|
|
604
|
+
const client = await this.getSlotMaster(slot);
|
|
605
|
+
const values = await client.mGet(keys2);
|
|
606
|
+
for (const [index, value] of values.entries()) {
|
|
607
|
+
valueMap.set(keys2[index], value ?? void 0);
|
|
608
|
+
}
|
|
609
|
+
})
|
|
610
|
+
);
|
|
589
611
|
return keys.map((key) => valueMap.get(key));
|
|
590
612
|
}
|
|
591
613
|
/**
|
|
@@ -595,10 +617,12 @@ var KeyvRedis = class extends Hookified {
|
|
|
595
617
|
async clearWithClusterSupport(keys) {
|
|
596
618
|
if (keys.length > 0) {
|
|
597
619
|
const slotMap = this.getSlotMap(keys);
|
|
598
|
-
await Promise.all(
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
620
|
+
await Promise.all(
|
|
621
|
+
Array.from(slotMap.entries(), async ([slot, keys2]) => {
|
|
622
|
+
const client = await this.getSlotMaster(slot);
|
|
623
|
+
return this._useUnlink ? client.unlink(keys2) : client.del(keys2);
|
|
624
|
+
})
|
|
625
|
+
);
|
|
602
626
|
}
|
|
603
627
|
}
|
|
604
628
|
/**
|
|
@@ -680,15 +704,11 @@ var KeyvRedis = class extends Hookified {
|
|
|
680
704
|
});
|
|
681
705
|
}
|
|
682
706
|
async createTimeoutPromise(timeoutMs) {
|
|
683
|
-
return new Promise(
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
},
|
|
689
|
-
timeoutMs
|
|
690
|
-
)
|
|
691
|
-
));
|
|
707
|
+
return new Promise(
|
|
708
|
+
(_, reject) => setTimeout(() => {
|
|
709
|
+
reject(new Error(`Redis timed out after ${timeoutMs}ms`));
|
|
710
|
+
}, timeoutMs)
|
|
711
|
+
);
|
|
692
712
|
}
|
|
693
713
|
};
|
|
694
714
|
function createKeyv(connect, options) {
|
|
@@ -696,7 +716,10 @@ function createKeyv(connect, options) {
|
|
|
696
716
|
const adapter = new KeyvRedis(connect, options);
|
|
697
717
|
if (options?.namespace) {
|
|
698
718
|
adapter.namespace = options.namespace;
|
|
699
|
-
const keyv2 = new Keyv(adapter, {
|
|
719
|
+
const keyv2 = new Keyv(adapter, {
|
|
720
|
+
namespace: options?.namespace,
|
|
721
|
+
useKeyPrefix: false
|
|
722
|
+
});
|
|
700
723
|
if (options?.throwOnConnectError) {
|
|
701
724
|
keyv2.throwOnErrors = true;
|
|
702
725
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyv/redis",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"description": "Redis storage adapter for Keyv",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
"keyv": "^5.5.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
+
"@biomejs/biome": "^2.2.0",
|
|
45
46
|
"@faker-js/faker": "^9.9.0",
|
|
46
47
|
"@vitest/coverage-v8": "^3.2.4",
|
|
47
48
|
"rimraf": "^6.0.1",
|
|
48
49
|
"timekeeper": "^2.3.1",
|
|
49
50
|
"tsd": "^0.32.0",
|
|
50
51
|
"vitest": "^3.2.4",
|
|
51
|
-
"
|
|
52
|
-
"@keyv/test-suite": "^2.1.0"
|
|
52
|
+
"@keyv/test-suite": "^2.1.1"
|
|
53
53
|
},
|
|
54
54
|
"tsd": {
|
|
55
55
|
"directory": "test"
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
],
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
|
|
66
|
-
"test": "
|
|
67
|
-
"test:ci": "
|
|
66
|
+
"test": "biome check --write && vitest run --coverage",
|
|
67
|
+
"test:ci": "biome check && vitest --run --sequence.setupFiles=list --coverage",
|
|
68
68
|
"clean": "rimraf ./node_modules ./coverage ./dist"
|
|
69
69
|
}
|
|
70
70
|
}
|