@rapidrest/core 3.1.0 → 4.0.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/dist/lib/CacheUtils.js +7 -4
- package/dist/lib/CacheUtils.js.map +1 -1
- package/dist/lib/MemoryStore.js +61 -2
- package/dist/lib/MemoryStore.js.map +1 -1
- package/dist/lib/RedisStore.js +195 -20
- package/dist/lib/RedisStore.js.map +1 -1
- package/dist/types/CacheUtils.d.ts +2 -1
- package/dist/types/MemoryStore.d.ts +9 -1
- package/dist/types/RedisStore.d.ts +19 -6
- package/dist/types/SimpleStore.d.ts +44 -5
- package/package.json +3 -3
package/dist/lib/CacheUtils.js
CHANGED
|
@@ -12,11 +12,14 @@ export class CacheUtils {
|
|
|
12
12
|
* whatever entry was `set()` least recently among those still present.
|
|
13
13
|
*
|
|
14
14
|
* @param map The map to evict the oldest entry from.
|
|
15
|
+
* @param num The total number of items to evict. Default is `1`.
|
|
15
16
|
*/
|
|
16
|
-
static evictOldest(map) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
static evictOldest(map, num = 1) {
|
|
18
|
+
for (let i = 0; i < num; i++) {
|
|
19
|
+
const oldestKey = map.keys().next().value;
|
|
20
|
+
if (oldestKey !== undefined) {
|
|
21
|
+
map.delete(oldestKey);
|
|
22
|
+
}
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CacheUtils.js","sourceRoot":"","sources":["../../src/CacheUtils.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,OAAO,UAAU;IACnB
|
|
1
|
+
{"version":3,"file":"CacheUtils.js","sourceRoot":"","sources":["../../src/CacheUtils.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,OAAO,UAAU;IACnB;;;;;;OAMG;IACI,MAAM,CAAC,WAAW,CAAO,GAAc,EAAE,MAAc,CAAC;QAC3D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAkB,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YACzD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC1B,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC1B,CAAC;QACL,CAAC;IACL,CAAC;CACJ"}
|
package/dist/lib/MemoryStore.js
CHANGED
|
@@ -26,9 +26,28 @@ export class MemoryStore {
|
|
|
26
26
|
this.entries = new Map();
|
|
27
27
|
/** The maximum number of records to store. */
|
|
28
28
|
this.maxSize = 10000;
|
|
29
|
+
this.sets = new Map();
|
|
29
30
|
this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
30
31
|
this.sweepTimer.unref?.();
|
|
31
32
|
}
|
|
33
|
+
clear() {
|
|
34
|
+
this.entries.clear();
|
|
35
|
+
this.sets.clear();
|
|
36
|
+
}
|
|
37
|
+
delete(id) {
|
|
38
|
+
this.entries.delete(id);
|
|
39
|
+
}
|
|
40
|
+
deleteMany(ids) {
|
|
41
|
+
for (const id of ids) {
|
|
42
|
+
this.entries.delete(id);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
deleteSet(id) {
|
|
46
|
+
// We purposefully don't delete the individually stored records
|
|
47
|
+
// as they may be relevant to other valid queries. Let them expire
|
|
48
|
+
// naturally.
|
|
49
|
+
this.sets.delete(id);
|
|
50
|
+
}
|
|
32
51
|
/** Stops the background sweep timer. Call when the owning `SessionManager` is destroyed. */
|
|
33
52
|
destroy() {
|
|
34
53
|
clearInterval(this.sweepTimer);
|
|
@@ -45,6 +64,20 @@ export class MemoryStore {
|
|
|
45
64
|
}
|
|
46
65
|
return entry.data;
|
|
47
66
|
}
|
|
67
|
+
loadMany(ids) {
|
|
68
|
+
const results = [];
|
|
69
|
+
for (const id of ids) {
|
|
70
|
+
results.push(this.load(id));
|
|
71
|
+
}
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
loadSet(id) {
|
|
75
|
+
const ids = this.sets.get(id);
|
|
76
|
+
if (!ids) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
return this.loadMany(ids);
|
|
80
|
+
}
|
|
48
81
|
save(id, data, ttl = this.defaultTTL) {
|
|
49
82
|
if (!this.entries.has(id) && this.entries.size >= this.maxSize) {
|
|
50
83
|
// Reclaim space by sweeping expired entries first, then evicting the oldest surviving entries
|
|
@@ -60,8 +93,34 @@ export class MemoryStore {
|
|
|
60
93
|
this.entries.delete(id);
|
|
61
94
|
this.entries.set(id, { data, expiresAt: Date.now() + ttl * 1000 });
|
|
62
95
|
}
|
|
63
|
-
|
|
64
|
-
|
|
96
|
+
saveMany(ids, data, ttl = this.defaultTTL) {
|
|
97
|
+
if (ids.length !== data.length) {
|
|
98
|
+
throw new Error("The ids and data arrays have different lengths.");
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i < ids.length; i++) {
|
|
101
|
+
this.save(ids[i], data[i], ttl);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
saveSet(id, data, idProp = "uid", ttl = this.defaultTTL) {
|
|
105
|
+
// Extract the list of ids from the data set, skipping any record that has no idProp value — otherwise
|
|
106
|
+
// every such record would collide under the same String(undefined) === "undefined" key and silently
|
|
107
|
+
// overwrite one another. We convert this to string so that it's a usable reference in our primary
|
|
108
|
+
// storage map.
|
|
109
|
+
const ids = [];
|
|
110
|
+
const records = [];
|
|
111
|
+
for (const record of data) {
|
|
112
|
+
const recordId = record[idProp];
|
|
113
|
+
if (recordId !== undefined && recordId !== null) {
|
|
114
|
+
ids.push(String(recordId));
|
|
115
|
+
records.push(record);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Now store each record
|
|
119
|
+
for (let i = 0; i < ids.length; i++) {
|
|
120
|
+
this.save(ids[i], records[i], ttl);
|
|
121
|
+
}
|
|
122
|
+
// Now store the id set
|
|
123
|
+
this.sets.set(id, ids);
|
|
65
124
|
}
|
|
66
125
|
sweep() {
|
|
67
126
|
const now = Date.now();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoryStore.js","sourceRoot":"","sources":["../../src/MemoryStore.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAG3D,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,KAAM,CAAC;AAEjC;;;;GAIG;AACH,MAAM,OAAO,WAAW;
|
|
1
|
+
{"version":3,"file":"MemoryStore.js","sourceRoot":"","sources":["../../src/MemoryStore.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/RedisStore.js
CHANGED
|
@@ -22,22 +22,69 @@ const SWEEP_INTERVAL_MS = 60000;
|
|
|
22
22
|
*/
|
|
23
23
|
export class RedisStore {
|
|
24
24
|
/**
|
|
25
|
+
* @param baseKey The base key that is pre-pended to all IDs.
|
|
25
26
|
* @param client The Redis client to back this store with. When omitted, the store operates purely in-memory.
|
|
26
27
|
*/
|
|
27
|
-
constructor(client) {
|
|
28
|
+
constructor(baseKey = "store", client) {
|
|
28
29
|
/** The default record TTL (in seconds). */
|
|
29
30
|
this.defaultTTL = 60;
|
|
30
31
|
this.entries = new Map();
|
|
31
32
|
/** The maximum number of records to store. */
|
|
32
33
|
this.maxSize = 10000;
|
|
34
|
+
this.sets = new Map();
|
|
35
|
+
this.baseKey = baseKey;
|
|
33
36
|
this.client = client;
|
|
34
37
|
this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
35
38
|
this.sweepTimer.unref?.();
|
|
36
39
|
}
|
|
40
|
+
async clear() {
|
|
41
|
+
this.entries.clear();
|
|
42
|
+
this.sets.clear();
|
|
43
|
+
// Delete all entries in redis
|
|
44
|
+
if (this.client) {
|
|
45
|
+
let keys = [];
|
|
46
|
+
for await (const results of this.client.scanIterator({ MATCH: this.baseKey + "*" })) {
|
|
47
|
+
keys.push(...results);
|
|
48
|
+
}
|
|
49
|
+
if (keys.length > 0) {
|
|
50
|
+
await this.client.unlink(keys);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async delete(id) {
|
|
55
|
+
// Prepend the base key to the id
|
|
56
|
+
id = this.baseKey + id;
|
|
57
|
+
this.entries.delete(id);
|
|
58
|
+
await this.client?.del(id);
|
|
59
|
+
}
|
|
60
|
+
async deleteMany(ids) {
|
|
61
|
+
if (ids.length === 0) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Pre-pend the baseKey to all ids
|
|
65
|
+
ids = ids.map((id) => this.baseKey + id);
|
|
66
|
+
// Delete all local entries
|
|
67
|
+
for (const id of ids) {
|
|
68
|
+
this.entries.delete(id);
|
|
69
|
+
}
|
|
70
|
+
// Now delete all entries in redis
|
|
71
|
+
await this.client?.del(ids);
|
|
72
|
+
}
|
|
73
|
+
async deleteSet(id) {
|
|
74
|
+
// Prepend the base key to the id
|
|
75
|
+
id = this.baseKey + id;
|
|
76
|
+
// We purposefully don't delete the individually stored records
|
|
77
|
+
// as they may be relevant to other valid queries. Let them expire
|
|
78
|
+
// naturally.
|
|
79
|
+
this.sets.delete(id);
|
|
80
|
+
await this.client?.del(id);
|
|
81
|
+
}
|
|
37
82
|
destroy() {
|
|
38
83
|
clearInterval(this.sweepTimer);
|
|
39
84
|
}
|
|
40
|
-
async load(id) {
|
|
85
|
+
async load(id, skipRedis = false) {
|
|
86
|
+
// Prepend the base key to the id
|
|
87
|
+
id = this.baseKey + id;
|
|
41
88
|
const entry = this.entries.get(id);
|
|
42
89
|
if (entry) {
|
|
43
90
|
// If the local copy hasn't expired, it's safe to serve directly.
|
|
@@ -50,22 +97,12 @@ export class RedisStore {
|
|
|
50
97
|
this.entries.delete(id);
|
|
51
98
|
}
|
|
52
99
|
// Without a Redis client, this is a pure in-memory store: a local miss/expiry is a real miss.
|
|
53
|
-
if (!this.client) {
|
|
100
|
+
if (!this.client || skipRedis) {
|
|
54
101
|
return undefined;
|
|
55
102
|
}
|
|
56
103
|
// The entry wasn't found locally. Try retrieving from redis.
|
|
57
104
|
// We'll also grab the ttl so we can add it to our local map.
|
|
58
|
-
const
|
|
59
|
-
.multi([
|
|
60
|
-
["get", id],
|
|
61
|
-
["ttl", id],
|
|
62
|
-
])
|
|
63
|
-
.exec();
|
|
64
|
-
if (!result) {
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
const data = result[0][1] ? result[0][1] : undefined;
|
|
68
|
-
const ttl = result[1][1] ?? undefined;
|
|
105
|
+
const [data, ttl] = await this.client.multi().get(id).ttl(id).execAsPipelineTyped();
|
|
69
106
|
if (!data) {
|
|
70
107
|
return undefined;
|
|
71
108
|
}
|
|
@@ -85,12 +122,98 @@ export class RedisStore {
|
|
|
85
122
|
this.entries.set(id, newEntry);
|
|
86
123
|
return newEntry.data;
|
|
87
124
|
}
|
|
88
|
-
async
|
|
125
|
+
async loadMany(ids) {
|
|
126
|
+
const results = new Array(ids.length);
|
|
127
|
+
// Load as many from the local store as possible
|
|
128
|
+
let missing = [];
|
|
129
|
+
for (let idx = 0; idx < ids.length; idx++) {
|
|
130
|
+
const id = ids[idx];
|
|
131
|
+
const result = await this.load(id, true);
|
|
132
|
+
if (result) {
|
|
133
|
+
results[idx] = result;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
missing.push({ idx, id });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!this.client) {
|
|
140
|
+
return results;
|
|
141
|
+
}
|
|
142
|
+
// Now load all remaining items from redis. Note that we don't call load() here, instead we use the multi
|
|
143
|
+
// command to reduce the number of times we hit redis (1 call vs n*2 calls).
|
|
144
|
+
if (missing.length > 0) {
|
|
145
|
+
let pipe = this.client.multi();
|
|
146
|
+
for (const pair of missing) {
|
|
147
|
+
const id = this.baseKey + pair.id;
|
|
148
|
+
pipe.get(id).ttl(id);
|
|
149
|
+
}
|
|
150
|
+
const result = await pipe.execAsPipeline();
|
|
151
|
+
let toSave = [];
|
|
152
|
+
for (let i = 0; i < missing.length; i++) {
|
|
153
|
+
const pair = missing[i];
|
|
154
|
+
const data = result[i * 2];
|
|
155
|
+
const ttl = result[i * 2 + 1];
|
|
156
|
+
if (data) {
|
|
157
|
+
const record = JSON.parse(data);
|
|
158
|
+
// Store the result in the same position in our final array
|
|
159
|
+
results[pair.idx] = record;
|
|
160
|
+
// Now add the list of items to store locally
|
|
161
|
+
toSave.push({ id: pair.id, data: record, ttl });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Finally go through all items to save.
|
|
165
|
+
if (toSave.length > 0) {
|
|
166
|
+
// Let's do a sweep on the local map and free up space while we're at it. Every id in `toSave` is
|
|
167
|
+
// guaranteed to be a genuinely new key (load() already deleted any expired local copy before
|
|
168
|
+
// reporting a miss), so `toSave.length` is exactly how much the map is about to grow by.
|
|
169
|
+
if (this.entries.size + toSave.length > this.maxSize) {
|
|
170
|
+
// Reclaim space by sweeping expired entries first, then evicting exactly enough of the oldest
|
|
171
|
+
// surviving entries for the whole batch to fit — evicting a fixed `toSave.length` regardless
|
|
172
|
+
// of how much the sweep already freed would evict more live entries than necessary.
|
|
173
|
+
this.sweep();
|
|
174
|
+
const overflow = this.entries.size + toSave.length - this.maxSize;
|
|
175
|
+
if (overflow > 0) {
|
|
176
|
+
CacheUtils.evictOldest(this.entries, Math.min(overflow, this.entries.size));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const entry of toSave) {
|
|
180
|
+
this.entries.set(this.baseKey + entry.id, {
|
|
181
|
+
data: entry.data,
|
|
182
|
+
expiresAt: Date.now() + entry.ttl * 1000,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return results;
|
|
188
|
+
}
|
|
189
|
+
async loadSet(id) {
|
|
190
|
+
id = this.baseKey + id;
|
|
191
|
+
// First look up the list of ids from local storage
|
|
192
|
+
const ids = this.sets.get(id);
|
|
193
|
+
if (ids) {
|
|
194
|
+
return this.loadMany(ids);
|
|
195
|
+
}
|
|
196
|
+
if (!this.client) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
// The set ids aren't stored locally, so let's try redis.
|
|
200
|
+
const json = await this.client.get(id);
|
|
201
|
+
if (json) {
|
|
202
|
+
const ids = JSON.parse(json);
|
|
203
|
+
// Store the list of ids locally for faster retrieval next time
|
|
204
|
+
this.sets.set(id, ids);
|
|
205
|
+
return this.loadMany(ids);
|
|
206
|
+
}
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
async save(id, data, ttl = this.defaultTTL, skipRedis = false) {
|
|
210
|
+
// Prepend the base key to the id
|
|
211
|
+
id = this.baseKey + id;
|
|
89
212
|
// Write to Redis first (when configured): if it fails (bad connection, etc.), the local cache is left
|
|
90
213
|
// untouched instead of reporting a "saved" value locally that never made it to the shared store other
|
|
91
214
|
// instances read from.
|
|
92
|
-
if (this.client) {
|
|
93
|
-
await this.client.
|
|
215
|
+
if (this.client && !skipRedis) {
|
|
216
|
+
await this.client.setEx(id, ttl, JSON.stringify(data));
|
|
94
217
|
}
|
|
95
218
|
if (!this.entries.has(id) && this.entries.size >= this.maxSize) {
|
|
96
219
|
// Reclaim space by sweeping expired entries first, then evicting the oldest surviving entries
|
|
@@ -106,11 +229,63 @@ export class RedisStore {
|
|
|
106
229
|
this.entries.delete(id);
|
|
107
230
|
this.entries.set(id, { data, expiresAt: Date.now() + ttl * 1000 });
|
|
108
231
|
}
|
|
109
|
-
async
|
|
110
|
-
|
|
232
|
+
async saveMany(ids, data, ttl = this.defaultTTL) {
|
|
233
|
+
if (ids.length !== data.length) {
|
|
234
|
+
throw new Error("The ids and data arguments must have the same length.");
|
|
235
|
+
}
|
|
236
|
+
// Prepend the base key to each id
|
|
237
|
+
ids = ids.map((id) => this.baseKey + id);
|
|
238
|
+
// Write to Redis first (when configured): if it fails (bad connection, etc.), the local cache is left
|
|
239
|
+
// untouched instead of reporting a "saved" value locally that never made it to the shared store other
|
|
240
|
+
// instances read from.
|
|
111
241
|
if (this.client) {
|
|
112
|
-
|
|
242
|
+
// Insert into redis using a pipeline to redis network traffic
|
|
243
|
+
const pipe = this.client.multi();
|
|
244
|
+
for (let i = 0; i < ids.length; i++) {
|
|
245
|
+
const id = ids[i];
|
|
246
|
+
const record = data[i];
|
|
247
|
+
pipe.setEx(id, ttl, JSON.stringify(record));
|
|
248
|
+
}
|
|
249
|
+
await pipe.execAsPipeline();
|
|
250
|
+
}
|
|
251
|
+
// Evict per-id, exactly like save() does, rather than pre-computing a single batch eviction count: a
|
|
252
|
+
// batched "evict N oldest, then insert the batch" approach can evict an entry that is itself one of the
|
|
253
|
+
// ids being (re-)saved in this same batch — that id then gets reinserted right after anyway, so the
|
|
254
|
+
// eviction bought no real space and the map ends up larger than maxSize.
|
|
255
|
+
for (let i = 0; i < ids.length; i++) {
|
|
256
|
+
const id = ids[i];
|
|
257
|
+
const record = data[i];
|
|
258
|
+
if (!this.entries.has(id) && this.entries.size >= this.maxSize) {
|
|
259
|
+
// Reclaim space by sweeping expired entries first, then evicting the oldest surviving entries
|
|
260
|
+
this.sweep();
|
|
261
|
+
while (this.entries.size >= this.maxSize && this.entries.size > 0) {
|
|
262
|
+
CacheUtils.evictOldest(this.entries);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Delete before re-inserting so a renewed entry moves to the end of the Map's iteration order. A
|
|
266
|
+
// `Map` does not reorder on `set()` of an already-present key, so without this, an actively-renewed
|
|
267
|
+
// entry stays at its original (oldest) position and `evictOldest()` would evict it ahead of a
|
|
268
|
+
// genuinely idle entry inserted later.
|
|
269
|
+
this.entries.delete(id);
|
|
270
|
+
this.entries.set(id, { data: record, expiresAt: Date.now() + ttl * 1000 });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async saveSet(id, data, idProp = "uid", ttl = this.defaultTTL) {
|
|
274
|
+
const ids = [];
|
|
275
|
+
const records = [];
|
|
276
|
+
// Create a list of ids that we'll store as a set
|
|
277
|
+
for (const record of data) {
|
|
278
|
+
const recordId = record[idProp];
|
|
279
|
+
if (recordId !== undefined && recordId !== null) {
|
|
280
|
+
ids.push(recordId);
|
|
281
|
+
records.push(record);
|
|
282
|
+
}
|
|
113
283
|
}
|
|
284
|
+
// Now store all the records in the cache
|
|
285
|
+
await this.saveMany(ids, records, ttl);
|
|
286
|
+
// Finally, store our set of ids
|
|
287
|
+
await this.client?.setEx(this.baseKey + id, ttl, JSON.stringify(ids));
|
|
288
|
+
this.sets.set(this.baseKey + id, ids);
|
|
114
289
|
}
|
|
115
290
|
sweep() {
|
|
116
291
|
const now = Date.now();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisStore.js","sourceRoot":"","sources":["../../src/RedisStore.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAI3D,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,KAAM,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,OAAO,UAAU;
|
|
1
|
+
{"version":3,"file":"RedisStore.js","sourceRoot":"","sources":["../../src/RedisStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ export declare class CacheUtils {
|
|
|
9
9
|
* whatever entry was `set()` least recently among those still present.
|
|
10
10
|
*
|
|
11
11
|
* @param map The map to evict the oldest entry from.
|
|
12
|
+
* @param num The total number of items to evict. Default is `1`.
|
|
12
13
|
*/
|
|
13
|
-
static evictOldest<K, V>(map: Map<K, V
|
|
14
|
+
static evictOldest<K, V>(map: Map<K, V>, num?: number): void;
|
|
14
15
|
}
|
|
@@ -10,12 +10,20 @@ export declare class MemoryStore implements SimpleStore {
|
|
|
10
10
|
protected entries: Map<string, MemoryStoreEntry>;
|
|
11
11
|
/** The maximum number of records to store. */
|
|
12
12
|
maxSize: number;
|
|
13
|
+
protected sets: Map<string, any[]>;
|
|
13
14
|
private sweepTimer;
|
|
14
15
|
constructor();
|
|
16
|
+
clear(): void;
|
|
17
|
+
delete(id: string): void;
|
|
18
|
+
deleteMany(ids: string[]): void;
|
|
19
|
+
deleteSet(id: string): void;
|
|
15
20
|
/** Stops the background sweep timer. Call when the owning `SessionManager` is destroyed. */
|
|
16
21
|
destroy(): void;
|
|
17
22
|
load(id: string): Record<string, any> | undefined;
|
|
23
|
+
loadMany(ids: string[]): (Record<string, any> | undefined)[];
|
|
24
|
+
loadSet(id: string): (Record<string, any> | undefined)[] | undefined;
|
|
18
25
|
save(id: string, data: Record<string, any>, ttl?: number): void;
|
|
19
|
-
|
|
26
|
+
saveMany(ids: string[], data: Record<string, any>[], ttl?: number): void;
|
|
27
|
+
saveSet(id: string, data: Record<string, any>[], idProp?: string, ttl?: number): void;
|
|
20
28
|
private sweep;
|
|
21
29
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MemoryStoreEntry, SimpleStore } from "./SimpleStore.js";
|
|
2
|
-
import
|
|
2
|
+
import { RedisClientType } from "redis";
|
|
3
3
|
/**
|
|
4
4
|
* Implements a SimpleStore storage system that combines a local in-memory store optionally backed by Redis. When
|
|
5
5
|
* no Redis client is given, the store behaves as a plain in-memory store (equivalent to `MemoryStore`).
|
|
@@ -7,20 +7,33 @@ import type * as ioredis from "ioredis";
|
|
|
7
7
|
* @author Jean-Philippe Steinmetz
|
|
8
8
|
*/
|
|
9
9
|
export declare class RedisStore implements SimpleStore {
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The base key that is pre-pended to all IDs.
|
|
12
|
+
*/
|
|
13
|
+
readonly baseKey: string;
|
|
14
|
+
client?: RedisClientType;
|
|
11
15
|
/** The default record TTL (in seconds). */
|
|
12
16
|
defaultTTL: number;
|
|
13
17
|
protected entries: Map<string, MemoryStoreEntry>;
|
|
14
18
|
/** The maximum number of records to store. */
|
|
15
19
|
maxSize: number;
|
|
20
|
+
protected sets: Map<string, any[]>;
|
|
16
21
|
private sweepTimer;
|
|
17
22
|
/**
|
|
23
|
+
* @param baseKey The base key that is pre-pended to all IDs.
|
|
18
24
|
* @param client The Redis client to back this store with. When omitted, the store operates purely in-memory.
|
|
19
25
|
*/
|
|
20
|
-
constructor(client?:
|
|
21
|
-
|
|
22
|
-
load(id: string): Promise<Record<string, any> | undefined>;
|
|
23
|
-
save(id: string, data: Record<string, any>, ttl?: number): Promise<void>;
|
|
26
|
+
constructor(baseKey?: string, client?: RedisClientType);
|
|
27
|
+
clear(): Promise<void>;
|
|
24
28
|
delete(id: string): Promise<void>;
|
|
29
|
+
deleteMany(ids: string[]): Promise<void>;
|
|
30
|
+
deleteSet(id: string): Promise<void>;
|
|
31
|
+
destroy(): void;
|
|
32
|
+
load(id: string, skipRedis?: boolean): Promise<Record<string, any> | undefined>;
|
|
33
|
+
loadMany(ids: string[]): Promise<(Record<string, any> | undefined)[]>;
|
|
34
|
+
loadSet(id: string): Promise<(Record<string, any> | undefined)[] | undefined>;
|
|
35
|
+
save(id: string, data: Record<string, any>, ttl?: number, skipRedis?: boolean): Promise<void>;
|
|
36
|
+
saveMany(ids: string[], data: Record<string, any>[], ttl?: number): Promise<void>;
|
|
37
|
+
saveSet(id: string, data: Record<string, any>[], idProp?: string, ttl?: number): Promise<void>;
|
|
25
38
|
private sweep;
|
|
26
39
|
}
|
|
@@ -4,7 +4,7 @@ export interface MemoryStoreEntry {
|
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
6
|
* Defines an interface for a simple key-value storage system that stores temporary records with a specified
|
|
7
|
-
* lifetime (TTL) and size.
|
|
7
|
+
* lifetime (TTL) and size. Supports the storage and retrieval of individual records as well as sets of records.
|
|
8
8
|
*
|
|
9
9
|
* @author Jean-Philippe Steinmetz
|
|
10
10
|
*/
|
|
@@ -14,20 +14,59 @@ export interface SimpleStore {
|
|
|
14
14
|
/** The maximum number of records to store. */
|
|
15
15
|
maxSize: number;
|
|
16
16
|
/**
|
|
17
|
-
* Deletes
|
|
17
|
+
* Deletes all entries in the storage system.
|
|
18
|
+
*/
|
|
19
|
+
clear(): Promise<void> | void;
|
|
20
|
+
/**
|
|
21
|
+
* Deletes a single record with the given id.
|
|
18
22
|
* @param id The id of the record to delete.
|
|
19
23
|
*/
|
|
20
24
|
delete(id: string): Promise<void> | void;
|
|
21
25
|
/**
|
|
22
|
-
*
|
|
26
|
+
* Deletes the records associated with the list of given ids.
|
|
27
|
+
* @param ids The list of ids to delete.
|
|
28
|
+
*/
|
|
29
|
+
deleteMany(ids: string[]): Promise<void> | void;
|
|
30
|
+
/**
|
|
31
|
+
* Deletes the set of records associated with the given id.
|
|
32
|
+
* @param ids The id of the set to delete.
|
|
33
|
+
*/
|
|
34
|
+
deleteSet(id: string): Promise<void> | void;
|
|
35
|
+
/**
|
|
36
|
+
* Retrieves a single record with the given id.
|
|
23
37
|
* @param id The id of the record to retrieve.
|
|
24
38
|
*/
|
|
25
39
|
load(id: string): Promise<Record<string, any> | undefined> | Record<string, any> | undefined;
|
|
26
40
|
/**
|
|
27
|
-
*
|
|
41
|
+
* Retrieves the list of records for the given list of ids.
|
|
42
|
+
* @param ids The list of ids of records to retrieve.
|
|
43
|
+
*/
|
|
44
|
+
loadMany(ids: string[]): Promise<(Record<string, any> | undefined)[]> | (Record<string, any> | undefined)[];
|
|
45
|
+
/**
|
|
46
|
+
* Retrieves the set of records with the given id.
|
|
47
|
+
* @param ids The id associated with the set of records to retrieve.
|
|
48
|
+
*/
|
|
49
|
+
loadSet(id: string): Promise<(Record<string, any> | undefined)[] | undefined> | (Record<string, any> | undefined)[] | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Stores a single record with the given id for the specified TTL.
|
|
28
52
|
* @param id The id of the record to retrieve.
|
|
29
53
|
* @param data The record to store.
|
|
30
54
|
* @param ttl The number of seconds that the record will be stored.
|
|
31
55
|
*/
|
|
32
|
-
save(id: string, data: Record<string, any>, ttl
|
|
56
|
+
save(id: string, data: Record<string, any>, ttl?: number): Promise<void> | void;
|
|
57
|
+
/**
|
|
58
|
+
* Stores a list of records with the given list of ids for the specified TTL.
|
|
59
|
+
* @param id The list of ids to store records for.
|
|
60
|
+
* @param data The list of records to store.
|
|
61
|
+
* @param ttl The number of seconds that each record will be stored.
|
|
62
|
+
*/
|
|
63
|
+
saveMany(ids: string[], data: Record<string, any>[], ttl?: number): Promise<void> | void;
|
|
64
|
+
/**
|
|
65
|
+
* Stores a set of records with the given id for the specified TTL.
|
|
66
|
+
* @param id The id of the record set to store.
|
|
67
|
+
* @param data The record to store.
|
|
68
|
+
* @param idProp The name of the property in data to use as an id for storage.
|
|
69
|
+
* @param ttl The number of seconds that the set will be stored.
|
|
70
|
+
*/
|
|
71
|
+
saveSet(id: string, data: Record<string, any>[], idProp: string, ttl?: number): Promise<void> | void;
|
|
33
72
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rapidrest/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "A collection of common utilities and core functionality for rapidly building RESTful applications.",
|
|
5
5
|
"repository": "https://github.com/rapidrest/core.git",
|
|
6
6
|
"author": "RapidREST <rapidrests@gmail.com>",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"axios": "1.x",
|
|
37
|
-
"
|
|
37
|
+
"redis": "6.x",
|
|
38
38
|
"reflect-metadata": "0.2.x",
|
|
39
39
|
"winston": "3.x",
|
|
40
40
|
"winston-transport": "4.x"
|
|
@@ -59,12 +59,12 @@
|
|
|
59
59
|
"eslint-config-prettier": "^10.1.8",
|
|
60
60
|
"eslint-plugin-import": "^2.32.0",
|
|
61
61
|
"eslint-plugin-jsdoc": "^63.0.2",
|
|
62
|
-
"ioredis": "^6.0.0",
|
|
63
62
|
"mkdirp": "^3.0.1",
|
|
64
63
|
"nconf": "^0.13.0",
|
|
65
64
|
"nock": "^14.0.15",
|
|
66
65
|
"nodemailer": "^8.0.10",
|
|
67
66
|
"prettier": "^3.8.4",
|
|
67
|
+
"redis": "^6.2.1",
|
|
68
68
|
"reflect-metadata": "^0.2.2",
|
|
69
69
|
"rimraf": "^6.1.3",
|
|
70
70
|
"ts-node": "^10.9.2",
|