@keyv/redis 4.4.1 → 4.6.0
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 +119 -3
- package/dist/index.cjs +228 -52
- package/dist/index.d.cts +73 -3
- package/dist/index.d.ts +73 -3
- package/dist/index.js +225 -51
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -26,9 +26,10 @@ Redis storage adapter for [Keyv](https://github.com/jaredwray/keyv).
|
|
|
26
26
|
# Table of Contents
|
|
27
27
|
* [Usage](#usage)
|
|
28
28
|
* [Namespaces](#namespaces)
|
|
29
|
-
* [
|
|
29
|
+
* [Using Generic Types](#using-generic-types)
|
|
30
30
|
* [Performance Considerations](#performance-considerations)
|
|
31
31
|
* [High Memory Usage on Redis Server](#high-memory-usage-on-redis-server)
|
|
32
|
+
* [Gracefully Handling Errors and Timeouts](#gracefully-handling-errors-and-timeouts)
|
|
32
33
|
* [Using Cacheable with Redis](#using-cacheable-with-redis)
|
|
33
34
|
* [Clustering and TLS Support](#clustering-and-tls-support)
|
|
34
35
|
* [API](#api)
|
|
@@ -99,6 +100,87 @@ const keyvRedis = new KeyvRedis(redis);
|
|
|
99
100
|
const keyv = new Keyv({ store: keyvRedis});
|
|
100
101
|
```
|
|
101
102
|
|
|
103
|
+
# Keyv Redis Options
|
|
104
|
+
|
|
105
|
+
You can pass in options to the `KeyvRedis` constructor. Here are the available options:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
export type KeyvRedisOptions = {
|
|
109
|
+
/**
|
|
110
|
+
* Namespace for the current instance.
|
|
111
|
+
*/
|
|
112
|
+
namespace?: string;
|
|
113
|
+
/**
|
|
114
|
+
* Separator to use between namespace and key.
|
|
115
|
+
*/
|
|
116
|
+
keyPrefixSeparator?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Number of keys to delete in a single batch.
|
|
119
|
+
*/
|
|
120
|
+
clearBatchSize?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Enable Unlink instead of using Del for clearing keys. This is more performant but may not be supported by all Redis versions.
|
|
123
|
+
*/
|
|
124
|
+
useUnlink?: boolean;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Whether to allow clearing all keys when no namespace is set.
|
|
128
|
+
* If set to true and no namespace is set, iterate() will return all keys.
|
|
129
|
+
* Defaults to `false`.
|
|
130
|
+
*/
|
|
131
|
+
noNamespaceAffectsAll?: boolean;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
135
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
136
|
+
*/
|
|
137
|
+
throwOnConnectError?: boolean;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
141
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
142
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
143
|
+
* and returns no-op responses.
|
|
144
|
+
* @default false
|
|
145
|
+
*/
|
|
146
|
+
throwErrors?: boolean;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
|
|
150
|
+
* If set, it will throw an error if the connection does not succeed within the specified time.
|
|
151
|
+
* @default undefined
|
|
152
|
+
*/
|
|
153
|
+
connectionTimeout?: number;
|
|
154
|
+
};
|
|
155
|
+
```
|
|
156
|
+
You can pass these options when creating a new `KeyvRedis` instance:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
import Keyv from 'keyv';
|
|
160
|
+
import KeyvRedis from '@keyv/redis';
|
|
161
|
+
|
|
162
|
+
const keyvRedis = new KeyvRedis({
|
|
163
|
+
namespace: 'my-namespace',
|
|
164
|
+
keyPrefixSeparator: ':',
|
|
165
|
+
clearBatchSize: 1000,
|
|
166
|
+
useUnlink: true,
|
|
167
|
+
noNamespaceAffectsAll: false,
|
|
168
|
+
connectTimeout: 200
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const keyv = new Keyv({ store: keyvRedis });
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
You can also set these options after the fact by using the `KeyvRedis` instance properties:
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
import {createKeyv} from '@keyv/redis';
|
|
178
|
+
|
|
179
|
+
const keyv = createKeyv('redis://user:pass@localhost:6379');
|
|
180
|
+
keyv.store.namespace = 'my-namespace';
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
|
|
102
184
|
# Namespaces
|
|
103
185
|
|
|
104
186
|
You can set a namespace for your keys. This is useful if you want to manage your keys in a more organized way. Here is an example of how to set a `namespace` with the `store` option:
|
|
@@ -120,7 +202,7 @@ keyv.namespace = 'my-namespace';
|
|
|
120
202
|
|
|
121
203
|
NOTE: If you plan to do many clears or deletes, it is recommended to read the [Performance Considerations](#performance-considerations) section.
|
|
122
204
|
|
|
123
|
-
##
|
|
205
|
+
## Using Generic Types
|
|
124
206
|
|
|
125
207
|
When initializing `KeyvRedis`, you can specify the type of the values you are storing and you can also specify types when calling methods:
|
|
126
208
|
|
|
@@ -129,7 +211,7 @@ import Keyv from 'keyv';
|
|
|
129
211
|
import KeyvRedis, { createClient } from '@keyv/redis';
|
|
130
212
|
|
|
131
213
|
|
|
132
|
-
|
|
214
|
+
type User {
|
|
133
215
|
id: number
|
|
134
216
|
name: string
|
|
135
217
|
}
|
|
@@ -175,6 +257,40 @@ const keyv = new Keyv(new KeyvRedis('redis://user:pass@localhost:6379', { useUnl
|
|
|
175
257
|
keyv.useUnlink = false;
|
|
176
258
|
```
|
|
177
259
|
|
|
260
|
+
# Gracefully Handling Errors and Timeouts
|
|
261
|
+
|
|
262
|
+
When using `@keyv/redis`, it is important to handle connection errors gracefully. You can do this by listening to the `error` event on the `KeyvRedis` instance. Here is an example of how to do that:
|
|
263
|
+
|
|
264
|
+
```js
|
|
265
|
+
import Keyv from 'keyv';
|
|
266
|
+
import KeyvRedis from '@keyv/redis';
|
|
267
|
+
const keyv = new Keyv(new KeyvRedis('redis://user:pass@localhost:6379'));
|
|
268
|
+
keyv.on('error', (error) => {
|
|
269
|
+
console.error('error', error);
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
By default, the `KeyvRedis` instance will `throw an error` if the connection fails to connect. You can disable this behavior by setting the `throwOnConnectError` option to `false` when creating the `KeyvRedis` instance:
|
|
274
|
+
|
|
275
|
+
On `get`, `getMany`, `set`, `setMany`, `delete`, and `deleteMany`, if the connection is lost, it will emit an error and return a no-op value. You can catch this error and handle it accordingly. This is important to ensure that your application does not crash due to a lost connection to Redis.
|
|
276
|
+
|
|
277
|
+
If you want to handle connection errors, retries, and timeouts more gracefully, you can use the `throwErrors` option. This will throw an error if any operation fails, allowing you to catch it and handle it accordingly:
|
|
278
|
+
|
|
279
|
+
There is a default `Reconnect Strategy` if you pass in just a `uri` connection string we will automatically create a Redis client for you with the following reconnect strategy:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
export const defaultReconnectStrategy = (attempts: number): number | Error => {
|
|
283
|
+
// Exponential backoff base: double each time, capped at 2s.
|
|
284
|
+
// Parentheses make it clear we do (2 ** attempts) first, then * 100
|
|
285
|
+
const backoff = Math.min((2 ** attempts) * 100, 2000);
|
|
286
|
+
|
|
287
|
+
// Add random jitter of up to ±50ms to avoid thundering herds:
|
|
288
|
+
const jitter = (Math.random() - 0.5) * 100;
|
|
289
|
+
|
|
290
|
+
return backoff + jitter;
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
178
294
|
# Using Cacheable with Redis
|
|
179
295
|
|
|
180
296
|
If you are wanting to see even better performance with Redis, you can use [Cacheable](https://npmjs.org/package/cacheable) which is a multi-layered cache library that has in-memory primary caching and non-blocking secondary caching. Here is an example of how to use it with Redis:
|
package/dist/index.cjs
CHANGED
|
@@ -31,25 +31,39 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
Keyv: () => import_keyv2.Keyv,
|
|
34
|
+
RedisErrorMessages: () => RedisErrorMessages,
|
|
34
35
|
createClient: () => import_client2.createClient,
|
|
35
36
|
createCluster: () => import_client2.createCluster,
|
|
36
37
|
createKeyv: () => createKeyv,
|
|
37
|
-
default: () => KeyvRedis
|
|
38
|
+
default: () => KeyvRedis,
|
|
39
|
+
defaultReconnectStrategy: () => defaultReconnectStrategy
|
|
38
40
|
});
|
|
39
41
|
module.exports = __toCommonJS(index_exports);
|
|
40
|
-
var import_node_events = __toESM(require("events"), 1);
|
|
41
42
|
var import_client = require("@redis/client");
|
|
43
|
+
var import_hookified = require("hookified");
|
|
42
44
|
var import_keyv = require("keyv");
|
|
43
45
|
var import_cluster_key_slot = __toESM(require("cluster-key-slot"), 1);
|
|
44
46
|
var import_client2 = require("@redis/client");
|
|
45
47
|
var import_keyv2 = require("keyv");
|
|
46
|
-
var
|
|
48
|
+
var RedisErrorMessages = /* @__PURE__ */ ((RedisErrorMessages2) => {
|
|
49
|
+
RedisErrorMessages2["RedisClientNotConnectedThrown"] = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true.";
|
|
50
|
+
return RedisErrorMessages2;
|
|
51
|
+
})(RedisErrorMessages || {});
|
|
52
|
+
var defaultReconnectStrategy = (attempts) => {
|
|
53
|
+
const backoff = Math.min(2 ** attempts * 100, 2e3);
|
|
54
|
+
const jitter = (Math.random() - 0.5) * 100;
|
|
55
|
+
return backoff + jitter;
|
|
56
|
+
};
|
|
57
|
+
var KeyvRedis = class extends import_hookified.Hookified {
|
|
47
58
|
_client = (0, import_client.createClient)();
|
|
48
59
|
_namespace;
|
|
49
60
|
_keyPrefixSeparator = "::";
|
|
50
61
|
_clearBatchSize = 1e3;
|
|
51
62
|
_useUnlink = true;
|
|
52
63
|
_noNamespaceAffectsAll = false;
|
|
64
|
+
_throwOnConnectError = true;
|
|
65
|
+
_throwErrors = false;
|
|
66
|
+
_connectionTimeout;
|
|
53
67
|
/**
|
|
54
68
|
* KeyvRedis constructor.
|
|
55
69
|
* @param {string | RedisClientOptions | RedisClientType} [connect] How to connect to the Redis server. If string pass in the url, if object pass in the options, if RedisClient pass in the client.
|
|
@@ -57,9 +71,13 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
57
71
|
*/
|
|
58
72
|
constructor(connect, options) {
|
|
59
73
|
super();
|
|
74
|
+
const socket = {
|
|
75
|
+
reconnectStrategy: defaultReconnectStrategy
|
|
76
|
+
// Default timeout for the connection
|
|
77
|
+
};
|
|
60
78
|
if (connect) {
|
|
61
79
|
if (typeof connect === "string") {
|
|
62
|
-
this._client = (0, import_client.createClient)({ url: connect });
|
|
80
|
+
this._client = (0, import_client.createClient)({ url: connect, socket });
|
|
63
81
|
} else if (connect.connect !== void 0) {
|
|
64
82
|
this._client = this.isClientCluster(connect) ? connect : connect;
|
|
65
83
|
} else if (connect instanceof Object) {
|
|
@@ -98,6 +116,10 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
98
116
|
keyPrefixSeparator: this._keyPrefixSeparator,
|
|
99
117
|
clearBatchSize: this._clearBatchSize,
|
|
100
118
|
noNamespaceAffectsAll: this._noNamespaceAffectsAll,
|
|
119
|
+
useUnlink: this._useUnlink,
|
|
120
|
+
throwOnConnectError: this._throwOnConnectError,
|
|
121
|
+
throwErrors: this._throwErrors,
|
|
122
|
+
connectionTimeout: this._connectionTimeout,
|
|
101
123
|
dialect: "redis",
|
|
102
124
|
url
|
|
103
125
|
};
|
|
@@ -180,17 +202,82 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
180
202
|
set noNamespaceAffectsAll(value) {
|
|
181
203
|
this._noNamespaceAffectsAll = value;
|
|
182
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Get if throwOnConnectError is set to true.
|
|
207
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
208
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
209
|
+
* @default true
|
|
210
|
+
*/
|
|
211
|
+
get throwOnConnectError() {
|
|
212
|
+
return this._throwOnConnectError;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Set if throwOnConnectError is set to true.
|
|
216
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
217
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
218
|
+
*/
|
|
219
|
+
set throwOnConnectError(value) {
|
|
220
|
+
this._throwOnConnectError = value;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get if throwErrors is set to true.
|
|
224
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
225
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
226
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
227
|
+
* and returns no-op responses.
|
|
228
|
+
* @default false
|
|
229
|
+
*/
|
|
230
|
+
get throwErrors() {
|
|
231
|
+
return this._throwErrors;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Set if throwErrors is set to true.
|
|
235
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
236
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
237
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
238
|
+
* and returns no-op responses.
|
|
239
|
+
*/
|
|
240
|
+
set throwErrors(value) {
|
|
241
|
+
this._throwErrors = value;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
|
|
245
|
+
* @default undefined
|
|
246
|
+
*/
|
|
247
|
+
get connectionTimeout() {
|
|
248
|
+
return this._connectionTimeout;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Set the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
|
|
252
|
+
* @default undefined
|
|
253
|
+
*/
|
|
254
|
+
set connectionTimeout(value) {
|
|
255
|
+
this._connectionTimeout = value;
|
|
256
|
+
}
|
|
183
257
|
/**
|
|
184
258
|
* Get the Redis URL used to connect to the server. This is used to get a connected client.
|
|
185
259
|
*/
|
|
186
260
|
async getClient() {
|
|
261
|
+
if (this._client.isOpen) {
|
|
262
|
+
return this._client;
|
|
263
|
+
}
|
|
187
264
|
try {
|
|
188
|
-
if (
|
|
265
|
+
if (this._connectionTimeout === void 0) {
|
|
189
266
|
await this._client.connect();
|
|
267
|
+
} else {
|
|
268
|
+
await Promise.race([
|
|
269
|
+
this._client.connect(),
|
|
270
|
+
this.createTimeoutPromise(this._connectionTimeout)
|
|
271
|
+
]);
|
|
190
272
|
}
|
|
191
273
|
} catch (error) {
|
|
192
274
|
this.emit("error", error);
|
|
275
|
+
if (this._throwOnConnectError) {
|
|
276
|
+
throw new Error("Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */);
|
|
277
|
+
}
|
|
278
|
+
await this.disconnect(true);
|
|
193
279
|
}
|
|
280
|
+
this.initClient();
|
|
194
281
|
return this._client;
|
|
195
282
|
}
|
|
196
283
|
/**
|
|
@@ -201,11 +288,18 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
201
288
|
*/
|
|
202
289
|
async set(key, value, ttl) {
|
|
203
290
|
const client = await this.getClient();
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
291
|
+
try {
|
|
292
|
+
key = this.createKeyPrefix(key, this._namespace);
|
|
293
|
+
if (ttl) {
|
|
294
|
+
await client.set(key, value, { PX: ttl });
|
|
295
|
+
} else {
|
|
296
|
+
await client.set(key, value);
|
|
297
|
+
}
|
|
298
|
+
} catch (error) {
|
|
299
|
+
this.emit("error", error);
|
|
300
|
+
if (this._throwErrors) {
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
209
303
|
}
|
|
210
304
|
}
|
|
211
305
|
/**
|
|
@@ -214,16 +308,23 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
214
308
|
*/
|
|
215
309
|
async setMany(entries) {
|
|
216
310
|
const client = await this.getClient();
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
311
|
+
try {
|
|
312
|
+
const multi = client.multi();
|
|
313
|
+
for (const { key, value, ttl } of entries) {
|
|
314
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
315
|
+
if (ttl) {
|
|
316
|
+
multi.set(prefixedKey, value, { PX: ttl });
|
|
317
|
+
} else {
|
|
318
|
+
multi.set(prefixedKey, value);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
await multi.exec();
|
|
322
|
+
} catch (error) {
|
|
323
|
+
this.emit("error", error);
|
|
324
|
+
if (this._throwErrors) {
|
|
325
|
+
throw error;
|
|
224
326
|
}
|
|
225
327
|
}
|
|
226
|
-
await multi.exec();
|
|
227
328
|
}
|
|
228
329
|
/**
|
|
229
330
|
* Check if a key exists in the store.
|
|
@@ -232,9 +333,17 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
232
333
|
*/
|
|
233
334
|
async has(key) {
|
|
234
335
|
const client = await this.getClient();
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
336
|
+
try {
|
|
337
|
+
key = this.createKeyPrefix(key, this._namespace);
|
|
338
|
+
const exists = await client.exists(key);
|
|
339
|
+
return exists === 1;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
this.emit("error", error);
|
|
342
|
+
if (this._throwErrors) {
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
238
347
|
}
|
|
239
348
|
/**
|
|
240
349
|
* Check if many keys exist in the store. This will be done as a single transaction.
|
|
@@ -243,13 +352,21 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
243
352
|
*/
|
|
244
353
|
async hasMany(keys) {
|
|
245
354
|
const client = await this.getClient();
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
355
|
+
try {
|
|
356
|
+
const multi = client.multi();
|
|
357
|
+
for (const key of keys) {
|
|
358
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
359
|
+
multi.exists(prefixedKey);
|
|
360
|
+
}
|
|
361
|
+
const results = await multi.exec();
|
|
362
|
+
return results.map((result) => result === 1);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
this.emit("error", error);
|
|
365
|
+
if (this._throwErrors) {
|
|
366
|
+
throw error;
|
|
367
|
+
}
|
|
368
|
+
return Array.from({ length: keys.length }).fill(false);
|
|
250
369
|
}
|
|
251
|
-
const results = await multi.exec();
|
|
252
|
-
return results.map((result) => result === 1);
|
|
253
370
|
}
|
|
254
371
|
/**
|
|
255
372
|
* Get a value from the store. If the key does not exist, it will return undefined.
|
|
@@ -258,12 +375,20 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
258
375
|
*/
|
|
259
376
|
async get(key) {
|
|
260
377
|
const client = await this.getClient();
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
378
|
+
try {
|
|
379
|
+
key = this.createKeyPrefix(key, this._namespace);
|
|
380
|
+
const value = await client.get(key);
|
|
381
|
+
if (value === null) {
|
|
382
|
+
return void 0;
|
|
383
|
+
}
|
|
384
|
+
return value;
|
|
385
|
+
} catch (error) {
|
|
386
|
+
this.emit("error", error);
|
|
387
|
+
if (this._throwErrors) {
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
264
390
|
return void 0;
|
|
265
391
|
}
|
|
266
|
-
return value;
|
|
267
392
|
}
|
|
268
393
|
/**
|
|
269
394
|
* Get many values from the store. If a key does not exist, it will return undefined.
|
|
@@ -275,8 +400,16 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
275
400
|
return [];
|
|
276
401
|
}
|
|
277
402
|
keys = keys.map((key) => this.createKeyPrefix(key, this._namespace));
|
|
278
|
-
|
|
279
|
-
|
|
403
|
+
try {
|
|
404
|
+
const values = await this.mget(keys);
|
|
405
|
+
return values;
|
|
406
|
+
} catch (error) {
|
|
407
|
+
this.emit("error", error);
|
|
408
|
+
if (this._throwErrors) {
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
411
|
+
return Array.from({ length: keys.length }).fill(void 0);
|
|
412
|
+
}
|
|
280
413
|
}
|
|
281
414
|
/**
|
|
282
415
|
* Delete a key from the store.
|
|
@@ -285,10 +418,18 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
285
418
|
*/
|
|
286
419
|
async delete(key) {
|
|
287
420
|
const client = await this.getClient();
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
421
|
+
try {
|
|
422
|
+
key = this.createKeyPrefix(key, this._namespace);
|
|
423
|
+
let deleted = 0;
|
|
424
|
+
deleted = await (this._useUnlink ? client.unlink(key) : client.del(key));
|
|
425
|
+
return deleted > 0;
|
|
426
|
+
} catch (error) {
|
|
427
|
+
this.emit("error", error);
|
|
428
|
+
if (this._throwErrors) {
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
292
433
|
}
|
|
293
434
|
/**
|
|
294
435
|
* Delete many keys from the store. This will be done as a single transaction.
|
|
@@ -298,19 +439,26 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
298
439
|
async deleteMany(keys) {
|
|
299
440
|
let result = false;
|
|
300
441
|
const client = await this.getClient();
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
442
|
+
try {
|
|
443
|
+
const multi = client.multi();
|
|
444
|
+
for (const key of keys) {
|
|
445
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
446
|
+
if (this._useUnlink) {
|
|
447
|
+
multi.unlink(prefixedKey);
|
|
448
|
+
} else {
|
|
449
|
+
multi.del(prefixedKey);
|
|
450
|
+
}
|
|
308
451
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
452
|
+
const results = await multi.exec();
|
|
453
|
+
for (const deleted of results) {
|
|
454
|
+
if (typeof deleted === "number" && deleted > 0) {
|
|
455
|
+
result = true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
} catch (error) {
|
|
459
|
+
this.emit("error", error);
|
|
460
|
+
if (this._throwErrors) {
|
|
461
|
+
throw error;
|
|
314
462
|
}
|
|
315
463
|
}
|
|
316
464
|
return result;
|
|
@@ -520,11 +668,37 @@ var KeyvRedis = class extends import_node_events.default {
|
|
|
520
668
|
if (options.noNamespaceAffectsAll !== void 0) {
|
|
521
669
|
this._noNamespaceAffectsAll = options.noNamespaceAffectsAll;
|
|
522
670
|
}
|
|
671
|
+
if (options.throwOnConnectError !== void 0) {
|
|
672
|
+
this._throwOnConnectError = options.throwOnConnectError;
|
|
673
|
+
}
|
|
674
|
+
if (options.throwErrors !== void 0) {
|
|
675
|
+
this._throwErrors = options.throwErrors;
|
|
676
|
+
}
|
|
677
|
+
if (options.connectionTimeout !== void 0) {
|
|
678
|
+
this._connectionTimeout = options.connectionTimeout;
|
|
679
|
+
}
|
|
523
680
|
}
|
|
524
681
|
initClient() {
|
|
525
|
-
this._client.on("
|
|
526
|
-
this.emit("
|
|
682
|
+
this._client.on("connect", () => {
|
|
683
|
+
this.emit("connect", this._client);
|
|
684
|
+
});
|
|
685
|
+
this._client.on("disconnect", () => {
|
|
686
|
+
this.emit("disconnect", this._client);
|
|
527
687
|
});
|
|
688
|
+
this._client.on("reconnecting", (reconnectInfo) => {
|
|
689
|
+
this.emit("reconnecting", reconnectInfo);
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
async createTimeoutPromise(timeoutMs) {
|
|
693
|
+
return new Promise((_, reject) => (
|
|
694
|
+
// eslint-disable-next-line no-promise-executor-return
|
|
695
|
+
setTimeout(
|
|
696
|
+
() => {
|
|
697
|
+
reject(new Error(`Redis timed out after ${timeoutMs}ms`));
|
|
698
|
+
},
|
|
699
|
+
timeoutMs
|
|
700
|
+
)
|
|
701
|
+
));
|
|
528
702
|
}
|
|
529
703
|
};
|
|
530
704
|
function createKeyv(connect, options) {
|
|
@@ -536,7 +710,9 @@ function createKeyv(connect, options) {
|
|
|
536
710
|
// Annotate the CommonJS export names for ESM import in node:
|
|
537
711
|
0 && (module.exports = {
|
|
538
712
|
Keyv,
|
|
713
|
+
RedisErrorMessages,
|
|
539
714
|
createClient,
|
|
540
715
|
createCluster,
|
|
541
|
-
createKeyv
|
|
716
|
+
createKeyv,
|
|
717
|
+
defaultReconnectStrategy
|
|
542
718
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import EventEmitter from 'node:events';
|
|
2
1
|
import { RedisClientOptions, RedisClusterOptions, RedisClientType, RedisClusterType, RedisModules, RedisFunctions, RedisScripts } from '@redis/client';
|
|
3
2
|
export { RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType, createClient, createCluster } from '@redis/client';
|
|
3
|
+
import { Hookified } from 'hookified';
|
|
4
4
|
import { KeyvStoreAdapter, KeyvEntry, Keyv } from 'keyv';
|
|
5
5
|
export { Keyv } from 'keyv';
|
|
6
6
|
|
|
@@ -27,6 +27,25 @@ type KeyvRedisOptions = {
|
|
|
27
27
|
* Defaults to `false`.
|
|
28
28
|
*/
|
|
29
29
|
noNamespaceAffectsAll?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
32
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
33
|
+
*/
|
|
34
|
+
throwOnConnectError?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
37
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
38
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
39
|
+
* and returns no-op responses.
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
throwErrors?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
|
|
45
|
+
* If set, it will throw an error if the connection does not succeed within the specified time.
|
|
46
|
+
* @default undefined
|
|
47
|
+
*/
|
|
48
|
+
connectionTimeout?: number;
|
|
30
49
|
};
|
|
31
50
|
type KeyvRedisPropertyOptions = KeyvRedisOptions & {
|
|
32
51
|
/**
|
|
@@ -52,14 +71,24 @@ type KeyvRedisEntry<T> = {
|
|
|
52
71
|
*/
|
|
53
72
|
ttl?: number;
|
|
54
73
|
};
|
|
74
|
+
declare enum RedisErrorMessages {
|
|
75
|
+
/**
|
|
76
|
+
* Error message when the Redis client is not connected and throwOnConnectError is set to true.
|
|
77
|
+
*/
|
|
78
|
+
RedisClientNotConnectedThrown = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true."
|
|
79
|
+
}
|
|
80
|
+
declare const defaultReconnectStrategy: (attempts: number) => number | Error;
|
|
55
81
|
type RedisClientConnectionType = RedisClientType | RedisClusterType<RedisModules, RedisFunctions, RedisScripts>;
|
|
56
|
-
declare class KeyvRedis<T> extends
|
|
82
|
+
declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
|
|
57
83
|
private _client;
|
|
58
84
|
private _namespace;
|
|
59
85
|
private _keyPrefixSeparator;
|
|
60
86
|
private _clearBatchSize;
|
|
61
87
|
private _useUnlink;
|
|
62
88
|
private _noNamespaceAffectsAll;
|
|
89
|
+
private _throwOnConnectError;
|
|
90
|
+
private _throwErrors;
|
|
91
|
+
private _connectionTimeout;
|
|
63
92
|
/**
|
|
64
93
|
* KeyvRedis constructor.
|
|
65
94
|
* @param {string | RedisClientOptions | RedisClientType} [connect] How to connect to the Redis server. If string pass in the url, if object pass in the options, if RedisClient pass in the client.
|
|
@@ -129,6 +158,46 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
|
|
|
129
158
|
* Set if not namespace affects all keys.
|
|
130
159
|
*/
|
|
131
160
|
set noNamespaceAffectsAll(value: boolean);
|
|
161
|
+
/**
|
|
162
|
+
* Get if throwOnConnectError is set to true.
|
|
163
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
164
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
165
|
+
* @default true
|
|
166
|
+
*/
|
|
167
|
+
get throwOnConnectError(): boolean;
|
|
168
|
+
/**
|
|
169
|
+
* Set if throwOnConnectError is set to true.
|
|
170
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
171
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
172
|
+
*/
|
|
173
|
+
set throwOnConnectError(value: boolean);
|
|
174
|
+
/**
|
|
175
|
+
* Get if throwErrors is set to true.
|
|
176
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
177
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
178
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
179
|
+
* and returns no-op responses.
|
|
180
|
+
* @default false
|
|
181
|
+
*/
|
|
182
|
+
get throwErrors(): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Set if throwErrors is set to true.
|
|
185
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
186
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
187
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
188
|
+
* and returns no-op responses.
|
|
189
|
+
*/
|
|
190
|
+
set throwErrors(value: boolean);
|
|
191
|
+
/**
|
|
192
|
+
* Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
|
|
193
|
+
* @default undefined
|
|
194
|
+
*/
|
|
195
|
+
get connectionTimeout(): number | undefined;
|
|
196
|
+
/**
|
|
197
|
+
* Set the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
|
|
198
|
+
* @default undefined
|
|
199
|
+
*/
|
|
200
|
+
set connectionTimeout(value: number | undefined);
|
|
132
201
|
/**
|
|
133
202
|
* Get the Redis URL used to connect to the server. This is used to get a connected client.
|
|
134
203
|
*/
|
|
@@ -251,6 +320,7 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
|
|
|
251
320
|
private isClientCluster;
|
|
252
321
|
private setOptions;
|
|
253
322
|
private initClient;
|
|
323
|
+
private createTimeoutPromise;
|
|
254
324
|
}
|
|
255
325
|
/**
|
|
256
326
|
* Will create a Keyv instance with the Redis adapter. This will also set the namespace and useKeyPrefix to false.
|
|
@@ -260,4 +330,4 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
|
|
|
260
330
|
*/
|
|
261
331
|
declare function createKeyv(connect?: string | RedisClientOptions | RedisClientType, options?: KeyvRedisOptions): Keyv;
|
|
262
332
|
|
|
263
|
-
export { type KeyvRedisEntry, type KeyvRedisOptions, type KeyvRedisPropertyOptions, type RedisClientConnectionType, createKeyv, KeyvRedis as default };
|
|
333
|
+
export { type KeyvRedisEntry, type KeyvRedisOptions, type KeyvRedisPropertyOptions, type RedisClientConnectionType, RedisErrorMessages, createKeyv, KeyvRedis as default, defaultReconnectStrategy };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import EventEmitter from 'node:events';
|
|
2
1
|
import { RedisClientOptions, RedisClusterOptions, RedisClientType, RedisClusterType, RedisModules, RedisFunctions, RedisScripts } from '@redis/client';
|
|
3
2
|
export { RedisClientOptions, RedisClientType, RedisClusterOptions, RedisClusterType, createClient, createCluster } from '@redis/client';
|
|
3
|
+
import { Hookified } from 'hookified';
|
|
4
4
|
import { KeyvStoreAdapter, KeyvEntry, Keyv } from 'keyv';
|
|
5
5
|
export { Keyv } from 'keyv';
|
|
6
6
|
|
|
@@ -27,6 +27,25 @@ type KeyvRedisOptions = {
|
|
|
27
27
|
* Defaults to `false`.
|
|
28
28
|
*/
|
|
29
29
|
noNamespaceAffectsAll?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
32
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
33
|
+
*/
|
|
34
|
+
throwOnConnectError?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
37
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
38
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
39
|
+
* and returns no-op responses.
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
throwErrors?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Timeout in milliseconds for the connection. Default is undefined, which uses the default timeout of the Redis client.
|
|
45
|
+
* If set, it will throw an error if the connection does not succeed within the specified time.
|
|
46
|
+
* @default undefined
|
|
47
|
+
*/
|
|
48
|
+
connectionTimeout?: number;
|
|
30
49
|
};
|
|
31
50
|
type KeyvRedisPropertyOptions = KeyvRedisOptions & {
|
|
32
51
|
/**
|
|
@@ -52,14 +71,24 @@ type KeyvRedisEntry<T> = {
|
|
|
52
71
|
*/
|
|
53
72
|
ttl?: number;
|
|
54
73
|
};
|
|
74
|
+
declare enum RedisErrorMessages {
|
|
75
|
+
/**
|
|
76
|
+
* Error message when the Redis client is not connected and throwOnConnectError is set to true.
|
|
77
|
+
*/
|
|
78
|
+
RedisClientNotConnectedThrown = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true."
|
|
79
|
+
}
|
|
80
|
+
declare const defaultReconnectStrategy: (attempts: number) => number | Error;
|
|
55
81
|
type RedisClientConnectionType = RedisClientType | RedisClusterType<RedisModules, RedisFunctions, RedisScripts>;
|
|
56
|
-
declare class KeyvRedis<T> extends
|
|
82
|
+
declare class KeyvRedis<T> extends Hookified implements KeyvStoreAdapter {
|
|
57
83
|
private _client;
|
|
58
84
|
private _namespace;
|
|
59
85
|
private _keyPrefixSeparator;
|
|
60
86
|
private _clearBatchSize;
|
|
61
87
|
private _useUnlink;
|
|
62
88
|
private _noNamespaceAffectsAll;
|
|
89
|
+
private _throwOnConnectError;
|
|
90
|
+
private _throwErrors;
|
|
91
|
+
private _connectionTimeout;
|
|
63
92
|
/**
|
|
64
93
|
* KeyvRedis constructor.
|
|
65
94
|
* @param {string | RedisClientOptions | RedisClientType} [connect] How to connect to the Redis server. If string pass in the url, if object pass in the options, if RedisClient pass in the client.
|
|
@@ -129,6 +158,46 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
|
|
|
129
158
|
* Set if not namespace affects all keys.
|
|
130
159
|
*/
|
|
131
160
|
set noNamespaceAffectsAll(value: boolean);
|
|
161
|
+
/**
|
|
162
|
+
* Get if throwOnConnectError is set to true.
|
|
163
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
164
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
165
|
+
* @default true
|
|
166
|
+
*/
|
|
167
|
+
get throwOnConnectError(): boolean;
|
|
168
|
+
/**
|
|
169
|
+
* Set if throwOnConnectError is set to true.
|
|
170
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
171
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
172
|
+
*/
|
|
173
|
+
set throwOnConnectError(value: boolean);
|
|
174
|
+
/**
|
|
175
|
+
* Get if throwErrors is set to true.
|
|
176
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
177
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
178
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
179
|
+
* and returns no-op responses.
|
|
180
|
+
* @default false
|
|
181
|
+
*/
|
|
182
|
+
get throwErrors(): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Set if throwErrors is set to true.
|
|
185
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
186
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
187
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
188
|
+
* and returns no-op responses.
|
|
189
|
+
*/
|
|
190
|
+
set throwErrors(value: boolean);
|
|
191
|
+
/**
|
|
192
|
+
* Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
|
|
193
|
+
* @default undefined
|
|
194
|
+
*/
|
|
195
|
+
get connectionTimeout(): number | undefined;
|
|
196
|
+
/**
|
|
197
|
+
* Set the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
|
|
198
|
+
* @default undefined
|
|
199
|
+
*/
|
|
200
|
+
set connectionTimeout(value: number | undefined);
|
|
132
201
|
/**
|
|
133
202
|
* Get the Redis URL used to connect to the server. This is used to get a connected client.
|
|
134
203
|
*/
|
|
@@ -251,6 +320,7 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
|
|
|
251
320
|
private isClientCluster;
|
|
252
321
|
private setOptions;
|
|
253
322
|
private initClient;
|
|
323
|
+
private createTimeoutPromise;
|
|
254
324
|
}
|
|
255
325
|
/**
|
|
256
326
|
* Will create a Keyv instance with the Redis adapter. This will also set the namespace and useKeyPrefix to false.
|
|
@@ -260,4 +330,4 @@ declare class KeyvRedis<T> extends EventEmitter implements KeyvStoreAdapter {
|
|
|
260
330
|
*/
|
|
261
331
|
declare function createKeyv(connect?: string | RedisClientOptions | RedisClientType, options?: KeyvRedisOptions): Keyv;
|
|
262
332
|
|
|
263
|
-
export { type KeyvRedisEntry, type KeyvRedisOptions, type KeyvRedisPropertyOptions, type RedisClientConnectionType, createKeyv, KeyvRedis as default };
|
|
333
|
+
export { type KeyvRedisEntry, type KeyvRedisOptions, type KeyvRedisPropertyOptions, type RedisClientConnectionType, RedisErrorMessages, createKeyv, KeyvRedis as default, defaultReconnectStrategy };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import EventEmitter from "events";
|
|
3
2
|
import {
|
|
4
3
|
createClient,
|
|
5
4
|
createCluster
|
|
6
5
|
} from "@redis/client";
|
|
6
|
+
import { Hookified } from "hookified";
|
|
7
7
|
import { Keyv } from "keyv";
|
|
8
8
|
import calculateSlot from "cluster-key-slot";
|
|
9
9
|
import {
|
|
@@ -13,13 +13,25 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
Keyv as Keyv2
|
|
15
15
|
} from "keyv";
|
|
16
|
-
var
|
|
16
|
+
var RedisErrorMessages = /* @__PURE__ */ ((RedisErrorMessages2) => {
|
|
17
|
+
RedisErrorMessages2["RedisClientNotConnectedThrown"] = "Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true.";
|
|
18
|
+
return RedisErrorMessages2;
|
|
19
|
+
})(RedisErrorMessages || {});
|
|
20
|
+
var defaultReconnectStrategy = (attempts) => {
|
|
21
|
+
const backoff = Math.min(2 ** attempts * 100, 2e3);
|
|
22
|
+
const jitter = (Math.random() - 0.5) * 100;
|
|
23
|
+
return backoff + jitter;
|
|
24
|
+
};
|
|
25
|
+
var KeyvRedis = class extends Hookified {
|
|
17
26
|
_client = createClient();
|
|
18
27
|
_namespace;
|
|
19
28
|
_keyPrefixSeparator = "::";
|
|
20
29
|
_clearBatchSize = 1e3;
|
|
21
30
|
_useUnlink = true;
|
|
22
31
|
_noNamespaceAffectsAll = false;
|
|
32
|
+
_throwOnConnectError = true;
|
|
33
|
+
_throwErrors = false;
|
|
34
|
+
_connectionTimeout;
|
|
23
35
|
/**
|
|
24
36
|
* KeyvRedis constructor.
|
|
25
37
|
* @param {string | RedisClientOptions | RedisClientType} [connect] How to connect to the Redis server. If string pass in the url, if object pass in the options, if RedisClient pass in the client.
|
|
@@ -27,9 +39,13 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
27
39
|
*/
|
|
28
40
|
constructor(connect, options) {
|
|
29
41
|
super();
|
|
42
|
+
const socket = {
|
|
43
|
+
reconnectStrategy: defaultReconnectStrategy
|
|
44
|
+
// Default timeout for the connection
|
|
45
|
+
};
|
|
30
46
|
if (connect) {
|
|
31
47
|
if (typeof connect === "string") {
|
|
32
|
-
this._client = createClient({ url: connect });
|
|
48
|
+
this._client = createClient({ url: connect, socket });
|
|
33
49
|
} else if (connect.connect !== void 0) {
|
|
34
50
|
this._client = this.isClientCluster(connect) ? connect : connect;
|
|
35
51
|
} else if (connect instanceof Object) {
|
|
@@ -68,6 +84,10 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
68
84
|
keyPrefixSeparator: this._keyPrefixSeparator,
|
|
69
85
|
clearBatchSize: this._clearBatchSize,
|
|
70
86
|
noNamespaceAffectsAll: this._noNamespaceAffectsAll,
|
|
87
|
+
useUnlink: this._useUnlink,
|
|
88
|
+
throwOnConnectError: this._throwOnConnectError,
|
|
89
|
+
throwErrors: this._throwErrors,
|
|
90
|
+
connectionTimeout: this._connectionTimeout,
|
|
71
91
|
dialect: "redis",
|
|
72
92
|
url
|
|
73
93
|
};
|
|
@@ -150,17 +170,82 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
150
170
|
set noNamespaceAffectsAll(value) {
|
|
151
171
|
this._noNamespaceAffectsAll = value;
|
|
152
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Get if throwOnConnectError is set to true.
|
|
175
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
176
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
177
|
+
* @default true
|
|
178
|
+
*/
|
|
179
|
+
get throwOnConnectError() {
|
|
180
|
+
return this._throwOnConnectError;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Set if throwOnConnectError is set to true.
|
|
184
|
+
* This is used to throw an error if the client is not connected when trying to connect. By default, this is
|
|
185
|
+
* set to true so that it throws an error when trying to connect to the Redis server fails.
|
|
186
|
+
*/
|
|
187
|
+
set throwOnConnectError(value) {
|
|
188
|
+
this._throwOnConnectError = value;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get if throwErrors is set to true.
|
|
192
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
193
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
194
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
195
|
+
* and returns no-op responses.
|
|
196
|
+
* @default false
|
|
197
|
+
*/
|
|
198
|
+
get throwErrors() {
|
|
199
|
+
return this._throwErrors;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Set if throwErrors is set to true.
|
|
203
|
+
* This is used to throw an error if at any point there is a failure. Use this if you want to
|
|
204
|
+
* ensure that all operations are successful and you want to handle errors. By default, this is
|
|
205
|
+
* set to false so that it does not throw an error on every operation and instead emits an error event
|
|
206
|
+
* and returns no-op responses.
|
|
207
|
+
*/
|
|
208
|
+
set throwErrors(value) {
|
|
209
|
+
this._throwErrors = value;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
|
|
213
|
+
* @default undefined
|
|
214
|
+
*/
|
|
215
|
+
get connectionTimeout() {
|
|
216
|
+
return this._connectionTimeout;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Set the connection timeout in milliseconds such as 5000 (5 seconds). Default is undefined. If undefined, it will use the default.
|
|
220
|
+
* @default undefined
|
|
221
|
+
*/
|
|
222
|
+
set connectionTimeout(value) {
|
|
223
|
+
this._connectionTimeout = value;
|
|
224
|
+
}
|
|
153
225
|
/**
|
|
154
226
|
* Get the Redis URL used to connect to the server. This is used to get a connected client.
|
|
155
227
|
*/
|
|
156
228
|
async getClient() {
|
|
229
|
+
if (this._client.isOpen) {
|
|
230
|
+
return this._client;
|
|
231
|
+
}
|
|
157
232
|
try {
|
|
158
|
-
if (
|
|
233
|
+
if (this._connectionTimeout === void 0) {
|
|
159
234
|
await this._client.connect();
|
|
235
|
+
} else {
|
|
236
|
+
await Promise.race([
|
|
237
|
+
this._client.connect(),
|
|
238
|
+
this.createTimeoutPromise(this._connectionTimeout)
|
|
239
|
+
]);
|
|
160
240
|
}
|
|
161
241
|
} catch (error) {
|
|
162
242
|
this.emit("error", error);
|
|
243
|
+
if (this._throwOnConnectError) {
|
|
244
|
+
throw new Error("Redis client is not connected or has failed to connect. This is thrown because throwOnConnectError is set to true." /* RedisClientNotConnectedThrown */);
|
|
245
|
+
}
|
|
246
|
+
await this.disconnect(true);
|
|
163
247
|
}
|
|
248
|
+
this.initClient();
|
|
164
249
|
return this._client;
|
|
165
250
|
}
|
|
166
251
|
/**
|
|
@@ -171,11 +256,18 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
171
256
|
*/
|
|
172
257
|
async set(key, value, ttl) {
|
|
173
258
|
const client = await this.getClient();
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
259
|
+
try {
|
|
260
|
+
key = this.createKeyPrefix(key, this._namespace);
|
|
261
|
+
if (ttl) {
|
|
262
|
+
await client.set(key, value, { PX: ttl });
|
|
263
|
+
} else {
|
|
264
|
+
await client.set(key, value);
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
this.emit("error", error);
|
|
268
|
+
if (this._throwErrors) {
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
179
271
|
}
|
|
180
272
|
}
|
|
181
273
|
/**
|
|
@@ -184,16 +276,23 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
184
276
|
*/
|
|
185
277
|
async setMany(entries) {
|
|
186
278
|
const client = await this.getClient();
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
279
|
+
try {
|
|
280
|
+
const multi = client.multi();
|
|
281
|
+
for (const { key, value, ttl } of entries) {
|
|
282
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
283
|
+
if (ttl) {
|
|
284
|
+
multi.set(prefixedKey, value, { PX: ttl });
|
|
285
|
+
} else {
|
|
286
|
+
multi.set(prefixedKey, value);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
await multi.exec();
|
|
290
|
+
} catch (error) {
|
|
291
|
+
this.emit("error", error);
|
|
292
|
+
if (this._throwErrors) {
|
|
293
|
+
throw error;
|
|
194
294
|
}
|
|
195
295
|
}
|
|
196
|
-
await multi.exec();
|
|
197
296
|
}
|
|
198
297
|
/**
|
|
199
298
|
* Check if a key exists in the store.
|
|
@@ -202,9 +301,17 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
202
301
|
*/
|
|
203
302
|
async has(key) {
|
|
204
303
|
const client = await this.getClient();
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
304
|
+
try {
|
|
305
|
+
key = this.createKeyPrefix(key, this._namespace);
|
|
306
|
+
const exists = await client.exists(key);
|
|
307
|
+
return exists === 1;
|
|
308
|
+
} catch (error) {
|
|
309
|
+
this.emit("error", error);
|
|
310
|
+
if (this._throwErrors) {
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
208
315
|
}
|
|
209
316
|
/**
|
|
210
317
|
* Check if many keys exist in the store. This will be done as a single transaction.
|
|
@@ -213,13 +320,21 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
213
320
|
*/
|
|
214
321
|
async hasMany(keys) {
|
|
215
322
|
const client = await this.getClient();
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
323
|
+
try {
|
|
324
|
+
const multi = client.multi();
|
|
325
|
+
for (const key of keys) {
|
|
326
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
327
|
+
multi.exists(prefixedKey);
|
|
328
|
+
}
|
|
329
|
+
const results = await multi.exec();
|
|
330
|
+
return results.map((result) => result === 1);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
this.emit("error", error);
|
|
333
|
+
if (this._throwErrors) {
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
return Array.from({ length: keys.length }).fill(false);
|
|
220
337
|
}
|
|
221
|
-
const results = await multi.exec();
|
|
222
|
-
return results.map((result) => result === 1);
|
|
223
338
|
}
|
|
224
339
|
/**
|
|
225
340
|
* Get a value from the store. If the key does not exist, it will return undefined.
|
|
@@ -228,12 +343,20 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
228
343
|
*/
|
|
229
344
|
async get(key) {
|
|
230
345
|
const client = await this.getClient();
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
346
|
+
try {
|
|
347
|
+
key = this.createKeyPrefix(key, this._namespace);
|
|
348
|
+
const value = await client.get(key);
|
|
349
|
+
if (value === null) {
|
|
350
|
+
return void 0;
|
|
351
|
+
}
|
|
352
|
+
return value;
|
|
353
|
+
} catch (error) {
|
|
354
|
+
this.emit("error", error);
|
|
355
|
+
if (this._throwErrors) {
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
234
358
|
return void 0;
|
|
235
359
|
}
|
|
236
|
-
return value;
|
|
237
360
|
}
|
|
238
361
|
/**
|
|
239
362
|
* Get many values from the store. If a key does not exist, it will return undefined.
|
|
@@ -245,8 +368,16 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
245
368
|
return [];
|
|
246
369
|
}
|
|
247
370
|
keys = keys.map((key) => this.createKeyPrefix(key, this._namespace));
|
|
248
|
-
|
|
249
|
-
|
|
371
|
+
try {
|
|
372
|
+
const values = await this.mget(keys);
|
|
373
|
+
return values;
|
|
374
|
+
} catch (error) {
|
|
375
|
+
this.emit("error", error);
|
|
376
|
+
if (this._throwErrors) {
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
return Array.from({ length: keys.length }).fill(void 0);
|
|
380
|
+
}
|
|
250
381
|
}
|
|
251
382
|
/**
|
|
252
383
|
* Delete a key from the store.
|
|
@@ -255,10 +386,18 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
255
386
|
*/
|
|
256
387
|
async delete(key) {
|
|
257
388
|
const client = await this.getClient();
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
389
|
+
try {
|
|
390
|
+
key = this.createKeyPrefix(key, this._namespace);
|
|
391
|
+
let deleted = 0;
|
|
392
|
+
deleted = await (this._useUnlink ? client.unlink(key) : client.del(key));
|
|
393
|
+
return deleted > 0;
|
|
394
|
+
} catch (error) {
|
|
395
|
+
this.emit("error", error);
|
|
396
|
+
if (this._throwErrors) {
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
262
401
|
}
|
|
263
402
|
/**
|
|
264
403
|
* Delete many keys from the store. This will be done as a single transaction.
|
|
@@ -268,19 +407,26 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
268
407
|
async deleteMany(keys) {
|
|
269
408
|
let result = false;
|
|
270
409
|
const client = await this.getClient();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
410
|
+
try {
|
|
411
|
+
const multi = client.multi();
|
|
412
|
+
for (const key of keys) {
|
|
413
|
+
const prefixedKey = this.createKeyPrefix(key, this._namespace);
|
|
414
|
+
if (this._useUnlink) {
|
|
415
|
+
multi.unlink(prefixedKey);
|
|
416
|
+
} else {
|
|
417
|
+
multi.del(prefixedKey);
|
|
418
|
+
}
|
|
278
419
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
420
|
+
const results = await multi.exec();
|
|
421
|
+
for (const deleted of results) {
|
|
422
|
+
if (typeof deleted === "number" && deleted > 0) {
|
|
423
|
+
result = true;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
} catch (error) {
|
|
427
|
+
this.emit("error", error);
|
|
428
|
+
if (this._throwErrors) {
|
|
429
|
+
throw error;
|
|
284
430
|
}
|
|
285
431
|
}
|
|
286
432
|
return result;
|
|
@@ -490,11 +636,37 @@ var KeyvRedis = class extends EventEmitter {
|
|
|
490
636
|
if (options.noNamespaceAffectsAll !== void 0) {
|
|
491
637
|
this._noNamespaceAffectsAll = options.noNamespaceAffectsAll;
|
|
492
638
|
}
|
|
639
|
+
if (options.throwOnConnectError !== void 0) {
|
|
640
|
+
this._throwOnConnectError = options.throwOnConnectError;
|
|
641
|
+
}
|
|
642
|
+
if (options.throwErrors !== void 0) {
|
|
643
|
+
this._throwErrors = options.throwErrors;
|
|
644
|
+
}
|
|
645
|
+
if (options.connectionTimeout !== void 0) {
|
|
646
|
+
this._connectionTimeout = options.connectionTimeout;
|
|
647
|
+
}
|
|
493
648
|
}
|
|
494
649
|
initClient() {
|
|
495
|
-
this._client.on("
|
|
496
|
-
this.emit("
|
|
650
|
+
this._client.on("connect", () => {
|
|
651
|
+
this.emit("connect", this._client);
|
|
652
|
+
});
|
|
653
|
+
this._client.on("disconnect", () => {
|
|
654
|
+
this.emit("disconnect", this._client);
|
|
497
655
|
});
|
|
656
|
+
this._client.on("reconnecting", (reconnectInfo) => {
|
|
657
|
+
this.emit("reconnecting", reconnectInfo);
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
async createTimeoutPromise(timeoutMs) {
|
|
661
|
+
return new Promise((_, reject) => (
|
|
662
|
+
// eslint-disable-next-line no-promise-executor-return
|
|
663
|
+
setTimeout(
|
|
664
|
+
() => {
|
|
665
|
+
reject(new Error(`Redis timed out after ${timeoutMs}ms`));
|
|
666
|
+
},
|
|
667
|
+
timeoutMs
|
|
668
|
+
)
|
|
669
|
+
));
|
|
498
670
|
}
|
|
499
671
|
};
|
|
500
672
|
function createKeyv(connect, options) {
|
|
@@ -505,8 +677,10 @@ function createKeyv(connect, options) {
|
|
|
505
677
|
}
|
|
506
678
|
export {
|
|
507
679
|
Keyv2 as Keyv,
|
|
680
|
+
RedisErrorMessages,
|
|
508
681
|
createClient2 as createClient,
|
|
509
682
|
createCluster2 as createCluster,
|
|
510
683
|
createKeyv,
|
|
511
|
-
KeyvRedis as default
|
|
684
|
+
KeyvRedis as default,
|
|
685
|
+
defaultReconnectStrategy
|
|
512
686
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyv/redis",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "Redis storage adapter for Keyv",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -35,18 +35,20 @@
|
|
|
35
35
|
"homepage": "https://github.com/jaredwray/keyv",
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@redis/client": "^1.6.0",
|
|
38
|
-
"cluster-key-slot": "^1.1.2"
|
|
38
|
+
"cluster-key-slot": "^1.1.2",
|
|
39
|
+
"hookified": "^1.10.0"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
42
|
"keyv": "^5.3.4"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
|
-
"@
|
|
45
|
+
"@faker-js/faker": "^9.9.0",
|
|
46
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
45
47
|
"rimraf": "^6.0.1",
|
|
46
48
|
"timekeeper": "^2.3.1",
|
|
47
49
|
"tsd": "^0.32.0",
|
|
48
|
-
"vitest": "^3.2.
|
|
49
|
-
"xo": "^1.1.
|
|
50
|
+
"vitest": "^3.2.4",
|
|
51
|
+
"xo": "^1.1.1",
|
|
50
52
|
"@keyv/test-suite": "^2.0.8"
|
|
51
53
|
},
|
|
52
54
|
"tsd": {
|