valkyries 0.1.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/LICENSE +21 -0
- package/README.md +120 -0
- package/dist/bloom-filter/batching.d.mts +3 -0
- package/dist/bloom-filter/batching.d.mts.map +1 -0
- package/dist/bloom-filter/batching.mjs +18 -0
- package/dist/bloom-filter/batching.mjs.map +1 -0
- package/dist/bloom-filter/lookups.d.mts +6 -0
- package/dist/bloom-filter/lookups.d.mts.map +1 -0
- package/dist/bloom-filter/lookups.mjs +101 -0
- package/dist/bloom-filter/lookups.mjs.map +1 -0
- package/dist/bloom-filter/rebuilds.d.mts +9 -0
- package/dist/bloom-filter/rebuilds.d.mts.map +1 -0
- package/dist/bloom-filter/rebuilds.mjs +66 -0
- package/dist/bloom-filter/rebuilds.mjs.map +1 -0
- package/dist/bloom-filter/results.d.mts +3 -0
- package/dist/bloom-filter/results.d.mts.map +1 -0
- package/dist/bloom-filter/results.mjs +23 -0
- package/dist/bloom-filter/results.mjs.map +1 -0
- package/dist/bloom-filter/scripts.d.mts +9 -0
- package/dist/bloom-filter/scripts.d.mts.map +1 -0
- package/dist/bloom-filter/scripts.mjs +10 -0
- package/dist/bloom-filter/scripts.mjs.map +1 -0
- package/dist/bloom-filter/types.d.mts +12 -0
- package/dist/bloom-filter/types.d.mts.map +1 -0
- package/dist/bloom-filter/types.mjs +2 -0
- package/dist/bloom-filter/types.mjs.map +1 -0
- package/dist/bloom-filter/writes.d.mts +5 -0
- package/dist/bloom-filter/writes.d.mts.map +1 -0
- package/dist/bloom-filter/writes.mjs +54 -0
- package/dist/bloom-filter/writes.mjs.map +1 -0
- package/dist/bloom-filter.d.mts +90 -0
- package/dist/bloom-filter.d.mts.map +1 -0
- package/dist/bloom-filter.mjs +154 -0
- package/dist/bloom-filter.mjs.map +1 -0
- package/dist/cache/batch-read.d.mts +7 -0
- package/dist/cache/batch-read.d.mts.map +1 -0
- package/dist/cache/batch-read.mjs +142 -0
- package/dist/cache/batch-read.mjs.map +1 -0
- package/dist/cache/constants.d.mts +7 -0
- package/dist/cache/constants.d.mts.map +1 -0
- package/dist/cache/constants.mjs +8 -0
- package/dist/cache/constants.mjs.map +1 -0
- package/dist/cache/core.d.mts +38 -0
- package/dist/cache/core.d.mts.map +1 -0
- package/dist/cache/core.mjs +171 -0
- package/dist/cache/core.mjs.map +1 -0
- package/dist/cache/deletes.d.mts +8 -0
- package/dist/cache/deletes.d.mts.map +1 -0
- package/dist/cache/deletes.mjs +44 -0
- package/dist/cache/deletes.mjs.map +1 -0
- package/dist/cache/mutations.d.mts +25 -0
- package/dist/cache/mutations.d.mts.map +1 -0
- package/dist/cache/mutations.mjs +181 -0
- package/dist/cache/mutations.mjs.map +1 -0
- package/dist/cache/set-if-not-invalidated-events.d.mts +4 -0
- package/dist/cache/set-if-not-invalidated-events.d.mts.map +1 -0
- package/dist/cache/set-if-not-invalidated-events.mjs +21 -0
- package/dist/cache/set-if-not-invalidated-events.mjs.map +1 -0
- package/dist/cache/single-read.d.mts +11 -0
- package/dist/cache/single-read.d.mts.map +1 -0
- package/dist/cache/single-read.mjs +73 -0
- package/dist/cache/single-read.mjs.map +1 -0
- package/dist/cache/stale-refresh.d.mts +15 -0
- package/dist/cache/stale-refresh.d.mts.map +1 -0
- package/dist/cache/stale-refresh.mjs +70 -0
- package/dist/cache/stale-refresh.mjs.map +1 -0
- package/dist/cache-metrics.d.mts +10 -0
- package/dist/cache-metrics.d.mts.map +1 -0
- package/dist/cache-metrics.mjs +12 -0
- package/dist/cache-metrics.mjs.map +1 -0
- package/dist/cache-utils.d.mts +20 -0
- package/dist/cache-utils.d.mts.map +1 -0
- package/dist/cache-utils.mjs +67 -0
- package/dist/cache-utils.mjs.map +1 -0
- package/dist/cache.d.mts +4 -0
- package/dist/cache.d.mts.map +1 -0
- package/dist/cache.mjs +4 -0
- package/dist/cache.mjs.map +1 -0
- package/dist/clients.d.mts +51 -0
- package/dist/clients.d.mts.map +1 -0
- package/dist/clients.mjs +95 -0
- package/dist/clients.mjs.map +1 -0
- package/dist/config.d.mts +6 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +10 -0
- package/dist/config.mjs.map +1 -0
- package/dist/delete.d.mts +3 -0
- package/dist/delete.d.mts.map +1 -0
- package/dist/delete.mjs +23 -0
- package/dist/delete.mjs.map +1 -0
- package/dist/dynamic-config/fields.d.mts +6 -0
- package/dist/dynamic-config/fields.d.mts.map +1 -0
- package/dist/dynamic-config/fields.mjs +50 -0
- package/dist/dynamic-config/fields.mjs.map +1 -0
- package/dist/dynamic-config/storage.d.mts +33 -0
- package/dist/dynamic-config/storage.d.mts.map +1 -0
- package/dist/dynamic-config/storage.mjs +55 -0
- package/dist/dynamic-config/storage.mjs.map +1 -0
- package/dist/dynamic-config.d.mts +34 -0
- package/dist/dynamic-config.d.mts.map +1 -0
- package/dist/dynamic-config.mjs +173 -0
- package/dist/dynamic-config.mjs.map +1 -0
- package/dist/errors.d.mts +4 -0
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +15 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/events.d.mts +84 -0
- package/dist/events.d.mts.map +1 -0
- package/dist/events.mjs +18 -0
- package/dist/events.mjs.map +1 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -0
- package/dist/key-normalization.d.mts +2 -0
- package/dist/key-normalization.d.mts.map +1 -0
- package/dist/key-normalization.mjs +4 -0
- package/dist/key-normalization.mjs.map +1 -0
- package/dist/rate-limiter.d.mts +40 -0
- package/dist/rate-limiter.d.mts.map +1 -0
- package/dist/rate-limiter.mjs +123 -0
- package/dist/rate-limiter.mjs.map +1 -0
- package/dist/scripts/bloom-filter-add.lua +26 -0
- package/dist/scripts/bloom-filter-ensure-exists.lua +13 -0
- package/dist/scripts/bloom-filter-exists-if-ready.lua +14 -0
- package/dist/scripts/bloom-filter-exists.lua +14 -0
- package/dist/scripts/bloom-filter-mexists-if-ready.lua +19 -0
- package/dist/scripts/bloom-filter-mexists.lua +17 -0
- package/dist/scripts/bloom-filter-reserve.lua +11 -0
- package/dist/scripts/cache-delete-with-invalidation.lua +29 -0
- package/dist/scripts/cache-set-if-not-invalidated.lua +47 -0
- package/dist/scripts/dynamic-config-set-fields.lua +23 -0
- package/dist/scripts/get-value-with-ttl.lua +35 -0
- package/dist/scripts/get-values-with-ttl.lua +81 -0
- package/dist/scripts/rate-limiter-add-and-check.lua +34 -0
- package/dist/scripts/rate-limiter-add.lua +26 -0
- package/dist/scripts/rate-limiter-get.lua +22 -0
- package/dist/scripts.d.mts +4 -0
- package/dist/scripts.d.mts.map +1 -0
- package/dist/scripts.mjs +31 -0
- package/dist/scripts.mjs.map +1 -0
- package/dist/shutdown.d.mts +3 -0
- package/dist/shutdown.d.mts.map +1 -0
- package/dist/shutdown.mjs +27 -0
- package/dist/shutdown.mjs.map +1 -0
- package/dist/types.d.mts +76 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +2 -0
- package/dist/types.mjs.map +1 -0
- package/dist/utils.d.mts +16 -0
- package/dist/utils.d.mts.map +1 -0
- package/dist/utils.mjs +35 -0
- package/dist/utils.mjs.map +1 -0
- package/docs/bloom-filters.md +12 -0
- package/docs/cache.md +19 -0
- package/docs/clients.md +13 -0
- package/docs/configuration.md +16 -0
- package/docs/dynamic-config.md +13 -0
- package/docs/events-and-metrics.md +15 -0
- package/docs/lua-scripts.md +7 -0
- package/docs/migration.md +74 -0
- package/docs/rate-limiter.md +17 -0
- package/docs/testing-and-ci.md +21 -0
- package/package.json +80 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bloom-filter.d.mts","sourceRoot":"","sources":["../bloom-filter.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAgB5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAE/F,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAc;gBAEhB,OAAO,EAAE,wBAAwB;IA0B7C;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAI7C;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;IAIrD;;;;OAIG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAItE;;;OAGG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;IAI9E;;;;;;;;;;;;OAYG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD;;;;OAIG;IACG,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE;;;OAGG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7C;;;;OAIG;IACG,iBAAiB,CACrB,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,CAAC,EAChC,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC;IAIV,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvB,wBAAwB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAI7B,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI3C,MAAM,IAAI,MAAM;IAIhB,cAAc,IAAI,MAAM;IAIxB,SAAS;;;;;;;;IAWH,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,OAAO,CAAC,KAAK;CAYd"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { cacheValkeyClient } from "./clients.mjs";
|
|
3
|
+
import { deleteBloomFilter, deleteWithAdditionalKeys, ensureExists, isReady, keyExists, rebuild, rebuildFromStream, } from "./bloom-filter/rebuilds.mjs";
|
|
4
|
+
import { exists, existsIfReady, mexists, mexistsIfReady } from "./bloom-filter/lookups.mjs";
|
|
5
|
+
import { add, addOrThrow, addStream } from "./bloom-filter/writes.mjs";
|
|
6
|
+
export { LUA_UNPACK_BATCH_SIZE } from "./bloom-filter/scripts.mjs";
|
|
7
|
+
export { isBloomMissingKeyError, normalizeBloomCheckResult } from "./bloom-filter/results.mjs";
|
|
8
|
+
export class ValkeyBloomFilter {
|
|
9
|
+
name;
|
|
10
|
+
capacity;
|
|
11
|
+
errorRate;
|
|
12
|
+
expansionRate;
|
|
13
|
+
batchSize;
|
|
14
|
+
liveKey;
|
|
15
|
+
buildingKey;
|
|
16
|
+
client;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
const { name, capacity, errorRate, expansionRate = 2, batchSize = 10_000, client = cacheValkeyClient, } = options;
|
|
19
|
+
assert(name && typeof name === "string", "name must be a non-empty string");
|
|
20
|
+
assert(capacity > 0, "capacity must be positive");
|
|
21
|
+
assert(errorRate > 0 && errorRate < 1, "errorRate must be between 0 and 1");
|
|
22
|
+
assert(expansionRate > 0, "expansionRate must be positive");
|
|
23
|
+
assert(batchSize > 0, "batchSize must be positive");
|
|
24
|
+
this.name = name;
|
|
25
|
+
this.capacity = capacity;
|
|
26
|
+
this.errorRate = errorRate;
|
|
27
|
+
this.expansionRate = expansionRate;
|
|
28
|
+
this.batchSize = batchSize;
|
|
29
|
+
this.liveKey = `bloom-filter:${name}`;
|
|
30
|
+
this.buildingKey = `bloom-filter:${name}:building`;
|
|
31
|
+
this.client = client;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* BF.EXISTS — check if a single item may be in the filter.
|
|
35
|
+
* Returns null if filter doesn't exist (caller should fall back to DB).
|
|
36
|
+
* Single roundtrip via Lua script (EXISTS + BF.EXISTS combined).
|
|
37
|
+
*/
|
|
38
|
+
exists(item) {
|
|
39
|
+
return exists(this.state(), item);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* BF.MEXISTS — check multiple items in the filter.
|
|
43
|
+
* Returns null per-item if filter doesn't exist (caller should fall back to DB).
|
|
44
|
+
* Single roundtrip via Lua script (EXISTS + BF.MEXISTS combined).
|
|
45
|
+
*/
|
|
46
|
+
mexists(items) {
|
|
47
|
+
return mexists(this.state(), items);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* BF.EXISTS guarded by a deterministic readiness key.
|
|
51
|
+
* Returns null when the ready marker or filter is absent, so callers fall back
|
|
52
|
+
* to their authoritative store instead of trusting an empty/partial filter.
|
|
53
|
+
*/
|
|
54
|
+
existsIfReady(readyKey, item) {
|
|
55
|
+
return existsIfReady(this.state(), readyKey, item);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* BF.MEXISTS guarded by a deterministic readiness key.
|
|
59
|
+
* Returns null per item when the ready marker or filter is absent.
|
|
60
|
+
*/
|
|
61
|
+
mexistsIfReady(readyKey, items) {
|
|
62
|
+
return mexistsIfReady(this.state(), readyKey, items);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Add items to bloom filter using BF.MADD. Returns a Promise that resolves when all
|
|
66
|
+
* batches are written (errors are logged via onError, not thrown).
|
|
67
|
+
* Items are chunked at batchSize to avoid hitting Valkey argument-count limits.
|
|
68
|
+
* Each chunk is a single Lua round-trip that also writes to buildingKey if an
|
|
69
|
+
* active rebuild is in progress, so entries are not silently dropped when
|
|
70
|
+
* rebuildFromStream renames buildingKey → liveKey.
|
|
71
|
+
*
|
|
72
|
+
* If neither the live key nor the building key exists (e.g. during the window
|
|
73
|
+
* between worker startup and backfill completion), add() is a no-op. This prevents
|
|
74
|
+
* BF.MADD from auto-creating an under-provisioned filter that would cause false
|
|
75
|
+
* negatives in cache reads. See backfill-bloom-filter.mts for the warmup design.
|
|
76
|
+
*/
|
|
77
|
+
add(items) {
|
|
78
|
+
return add(this.state(), items);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Add items to bloom filter — same as add(), but throws on error instead of absorbing via onError.
|
|
82
|
+
* Use when the caller needs to detect failures and take corrective action (e.g. clearing readiness).
|
|
83
|
+
*/
|
|
84
|
+
async addOrThrow(items) {
|
|
85
|
+
await addOrThrow(this.state(), items);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Awaitable add — streams batches from an AsyncIterable and BF.MADDs each batch.
|
|
89
|
+
* Use for large-dataset population to avoid loading everything into memory.
|
|
90
|
+
* Unlike add(), errors are propagated (not absorbed via onError).
|
|
91
|
+
*/
|
|
92
|
+
async addStream(batches) {
|
|
93
|
+
await addStream(this.state(), batches);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Rebuild the filter from a complete item list. Uses building key and atomic rename.
|
|
97
|
+
* Only deletes the building key at start (not the live key).
|
|
98
|
+
*/
|
|
99
|
+
async rebuild(items) {
|
|
100
|
+
await rebuild(this.state(), items);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Rebuild the filter from an async iterable of item batches. Uses building key and atomic rename.
|
|
104
|
+
* Only deletes the building key at start (not the live key).
|
|
105
|
+
* @param capacityOverride - Optional capacity for this rebuild, such as 2x the current row count.
|
|
106
|
+
*/
|
|
107
|
+
async rebuildFromStream(batches, capacityOverride) {
|
|
108
|
+
await rebuildFromStream(this.state(), batches, capacityOverride);
|
|
109
|
+
}
|
|
110
|
+
async delete() {
|
|
111
|
+
await deleteBloomFilter(this.state());
|
|
112
|
+
}
|
|
113
|
+
async deleteWithAdditionalKeys(additionalKeys) {
|
|
114
|
+
await deleteWithAdditionalKeys(this.state(), additionalKeys);
|
|
115
|
+
}
|
|
116
|
+
keyExists() {
|
|
117
|
+
return keyExists(this.state());
|
|
118
|
+
}
|
|
119
|
+
isReady(readyKey) {
|
|
120
|
+
return isReady(this.state(), readyKey);
|
|
121
|
+
}
|
|
122
|
+
getKey() {
|
|
123
|
+
return this.liveKey;
|
|
124
|
+
}
|
|
125
|
+
getBuildingKey() {
|
|
126
|
+
return this.buildingKey;
|
|
127
|
+
}
|
|
128
|
+
getConfig() {
|
|
129
|
+
return {
|
|
130
|
+
name: this.name,
|
|
131
|
+
capacity: this.capacity,
|
|
132
|
+
errorRate: this.errorRate,
|
|
133
|
+
batchSize: this.batchSize,
|
|
134
|
+
liveKey: this.liveKey,
|
|
135
|
+
buildingKey: this.buildingKey,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
async ensureExists(capacity) {
|
|
139
|
+
await ensureExists(this.state(), capacity);
|
|
140
|
+
}
|
|
141
|
+
state() {
|
|
142
|
+
return {
|
|
143
|
+
name: this.name,
|
|
144
|
+
capacity: this.capacity,
|
|
145
|
+
errorRate: this.errorRate,
|
|
146
|
+
expansionRate: this.expansionRate,
|
|
147
|
+
batchSize: this.batchSize,
|
|
148
|
+
liveKey: this.liveKey,
|
|
149
|
+
buildingKey: this.buildingKey,
|
|
150
|
+
client: this.client,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=bloom-filter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bloom-filter.mjs","sourceRoot":"","sources":["../bloom-filter.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ValkeyCacheSingleRead } from "./single-read.mts";
|
|
2
|
+
export declare abstract class ValkeyCacheBatchRead<K = string> extends ValkeyCacheSingleRead<K> {
|
|
3
|
+
cacheGetByAnyBatch<T>(batchFn: (keys: K[]) => Promise<Array<T | null | undefined>>): (keys: K[]) => Promise<Array<T | null>>;
|
|
4
|
+
getBatch(keys: K[]): Promise<Array<string | Buffer | Record<string, unknown> | null>>;
|
|
5
|
+
private trackBatchRead;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=batch-read.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-read.d.mts","sourceRoot":"","sources":["../../cache/batch-read.mts"],"names":[],"mappings":"AAGA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAW1D,8BAAsB,oBAAoB,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,qBAAqB,CAAC,CAAC,CAAC;IACrF,kBAAkB,CAAC,CAAC,EAClB,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,GAC3D,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAmDpC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAuC3F,OAAO,CAAC,cAAc;CAWvB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { durationInMilliseconds } from "../cache-utils.mjs";
|
|
2
|
+
import { trackCacheCall } from "../cache-metrics.mjs";
|
|
3
|
+
import { handleValkeyError } from "../errors.mjs";
|
|
4
|
+
import { ValkeyCacheSingleRead } from "./single-read.mjs";
|
|
5
|
+
class BatchReadStats {
|
|
6
|
+
hits = 0;
|
|
7
|
+
misses = 0;
|
|
8
|
+
bloomMisses = 0;
|
|
9
|
+
hitKeys = [];
|
|
10
|
+
missKeys = [];
|
|
11
|
+
bloomMissKeys = [];
|
|
12
|
+
}
|
|
13
|
+
export class ValkeyCacheBatchRead extends ValkeyCacheSingleRead {
|
|
14
|
+
cacheGetByAnyBatch(batchFn) {
|
|
15
|
+
return async (keys) => {
|
|
16
|
+
const { validKeys: normalizedKeys, serializedKeys, outputIndices, } = this.deduplicateKeys(keys);
|
|
17
|
+
if (normalizedKeys.length === 0) {
|
|
18
|
+
trackCacheCall({
|
|
19
|
+
cacheName: this.prefix,
|
|
20
|
+
batch: true,
|
|
21
|
+
hits: 0,
|
|
22
|
+
misses: 0,
|
|
23
|
+
bloomMisses: 0,
|
|
24
|
+
duration: 0,
|
|
25
|
+
});
|
|
26
|
+
return Array(keys.length).fill(null);
|
|
27
|
+
}
|
|
28
|
+
const scatter = (normalized) => outputIndices.map((idx) => (idx === -1 ? null : normalized[idx]));
|
|
29
|
+
const stats = new BatchReadStats();
|
|
30
|
+
const start = process.hrtime.bigint();
|
|
31
|
+
try {
|
|
32
|
+
const cachedEntries = await this.getValuesWithTtl(serializedKeys);
|
|
33
|
+
const cachedValues = cachedEntries.map((entry) => entry.value);
|
|
34
|
+
const missing = collectMissingKeys(cachedEntries, normalizedKeys, serializedKeys, cachedValues, stats);
|
|
35
|
+
stats.misses = missing.keys.length;
|
|
36
|
+
stats.hits = normalizedKeys.length - stats.misses - stats.bloomMisses;
|
|
37
|
+
this.refreshStaleBatchEntries(serializedKeys, normalizedKeys, cachedEntries, batchFn);
|
|
38
|
+
if (missing.keys.length === 0)
|
|
39
|
+
return scatter(cachedValues);
|
|
40
|
+
const fetchedResults = await batchFn(missing.keys);
|
|
41
|
+
assertBatchResultLength(fetchedResults, missing.keys.length);
|
|
42
|
+
const results = mergeFetchedResults(cachedValues, missing.indices, fetchedResults);
|
|
43
|
+
const setEntries = missing.keys.map((key, i) => {
|
|
44
|
+
const value = fetchedResults[i] ?? null;
|
|
45
|
+
return { key, value, ttl: value === null ? this.nullTtl : undefined };
|
|
46
|
+
});
|
|
47
|
+
this.setBatchIfNotInvalidated(setEntries).catch(handleValkeyError);
|
|
48
|
+
return scatter(results);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
this.trackBatchRead(start, stats);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async getBatch(keys) {
|
|
56
|
+
const { serializedKeys, outputIndices } = this.deduplicateKeys(keys);
|
|
57
|
+
if (serializedKeys.length === 0) {
|
|
58
|
+
trackCacheCall({
|
|
59
|
+
cacheName: this.prefix,
|
|
60
|
+
batch: true,
|
|
61
|
+
hits: 0,
|
|
62
|
+
misses: 0,
|
|
63
|
+
bloomMisses: 0,
|
|
64
|
+
duration: 0,
|
|
65
|
+
});
|
|
66
|
+
return Array(keys.length).fill(null);
|
|
67
|
+
}
|
|
68
|
+
const stats = new BatchReadStats();
|
|
69
|
+
const start = process.hrtime.bigint();
|
|
70
|
+
try {
|
|
71
|
+
const entries = await this.getValuesWithTtl(serializedKeys);
|
|
72
|
+
const values = entries.map((entry, i) => {
|
|
73
|
+
if (entry.bloomMiss) {
|
|
74
|
+
stats.bloomMisses++;
|
|
75
|
+
stats.bloomMissKeys.push(serializedKeys[i]);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const keyExists = entry.ttlSecondsRemaining !== null && entry.ttlSecondsRemaining !== -2;
|
|
79
|
+
if (entry.value !== null || keyExists) {
|
|
80
|
+
stats.hits++;
|
|
81
|
+
stats.hitKeys.push(serializedKeys[i]);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
stats.misses++;
|
|
85
|
+
stats.missKeys.push(serializedKeys[i]);
|
|
86
|
+
}
|
|
87
|
+
return entry.value;
|
|
88
|
+
});
|
|
89
|
+
return outputIndices.map((idx) => (idx === -1 ? null : values[idx]));
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
this.trackBatchRead(start, stats);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
trackBatchRead(start, stats) {
|
|
96
|
+
trackCacheCall({
|
|
97
|
+
cacheName: this.prefix,
|
|
98
|
+
batch: true,
|
|
99
|
+
hits: stats.hits,
|
|
100
|
+
misses: stats.misses,
|
|
101
|
+
bloomMisses: stats.bloomMisses,
|
|
102
|
+
duration: durationInMilliseconds(start),
|
|
103
|
+
});
|
|
104
|
+
this.emitCacheEvents(stats.hitKeys, stats.missKeys, stats.bloomMissKeys);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function collectMissingKeys(entries, normalizedKeys, serializedKeys, cachedValues, stats) {
|
|
108
|
+
const indices = [];
|
|
109
|
+
const keys = [];
|
|
110
|
+
for (let i = 0; i < cachedValues.length; i++) {
|
|
111
|
+
const entry = entries[i];
|
|
112
|
+
if (entry.bloomMiss) {
|
|
113
|
+
stats.bloomMisses++;
|
|
114
|
+
stats.bloomMissKeys.push(serializedKeys[i]);
|
|
115
|
+
cachedValues[i] = null;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const keyExists = entry.ttlSecondsRemaining !== null && entry.ttlSecondsRemaining !== -2;
|
|
119
|
+
if (entry.value === null && !keyExists) {
|
|
120
|
+
indices.push(i);
|
|
121
|
+
keys.push(normalizedKeys[i]);
|
|
122
|
+
stats.missKeys.push(serializedKeys[i]);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
stats.hitKeys.push(serializedKeys[i]);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { indices, keys };
|
|
129
|
+
}
|
|
130
|
+
function assertBatchResultLength(results, expectedLength) {
|
|
131
|
+
if (!Array.isArray(results) || results.length !== expectedLength) {
|
|
132
|
+
throw new Error(`Batch function returned invalid result: expected array of ${expectedLength} items, got ${Array.isArray(results) ? results.length : typeof results}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function mergeFetchedResults(cachedValues, missingIndices, fetchedResults) {
|
|
136
|
+
const results = [...cachedValues];
|
|
137
|
+
for (let i = 0; i < missingIndices.length; i++) {
|
|
138
|
+
results[missingIndices[i]] = fetchedResults[i] ?? null;
|
|
139
|
+
}
|
|
140
|
+
return results;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=batch-read.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-read.mjs","sourceRoot":"","sources":["../../cache/batch-read.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const CACHE_NAMESPACE = "cache";
|
|
2
|
+
export declare const INVALIDATION_MARKER_TTL_SECONDS = 60;
|
|
3
|
+
export declare const getValueWithTtlScript: import("@valkey/valkey-glide").Script;
|
|
4
|
+
export declare const getValuesWithTtlScript: import("@valkey/valkey-glide").Script;
|
|
5
|
+
export declare const cacheSetIfNotInvalidatedScript: import("@valkey/valkey-glide").Script;
|
|
6
|
+
export declare const cacheDeleteWithInvalidationScript: import("@valkey/valkey-glide").Script;
|
|
7
|
+
//# sourceMappingURL=constants.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.mts","sourceRoot":"","sources":["../../cache/constants.mts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAElD,eAAO,MAAM,qBAAqB,uCAEjC,CAAC;AACF,eAAO,MAAM,sBAAsB,uCAElC,CAAC;AACF,eAAO,MAAM,8BAA8B,uCAE1C,CAAC;AACF,eAAO,MAAM,iCAAiC,uCAE7C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { loadScript, registerScript } from "../scripts.mjs";
|
|
2
|
+
export const CACHE_NAMESPACE = "cache";
|
|
3
|
+
export const INVALIDATION_MARKER_TTL_SECONDS = 60;
|
|
4
|
+
export const getValueWithTtlScript = registerScript(loadScript("get-value-with-ttl.lua", new URL("../", import.meta.url)));
|
|
5
|
+
export const getValuesWithTtlScript = registerScript(loadScript("get-values-with-ttl.lua", new URL("../", import.meta.url)));
|
|
6
|
+
export const cacheSetIfNotInvalidatedScript = registerScript(loadScript("cache-set-if-not-invalidated.lua", new URL("../", import.meta.url)));
|
|
7
|
+
export const cacheDeleteWithInvalidationScript = registerScript(loadScript("cache-delete-with-invalidation.lua", new URL("../", import.meta.url)));
|
|
8
|
+
//# sourceMappingURL=constants.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../cache/constants.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE5D,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AACvC,MAAM,CAAC,MAAM,+BAA+B,GAAG,EAAE,CAAC;AAElD,MAAM,CAAC,MAAM,qBAAqB,GAAG,cAAc,CACjD,UAAU,CAAC,wBAAwB,EAAE,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CACtE,CAAC;AACF,MAAM,CAAC,MAAM,sBAAsB,GAAG,cAAc,CAClD,UAAU,CAAC,yBAAyB,EAAE,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CACvE,CAAC;AACF,MAAM,CAAC,MAAM,8BAA8B,GAAG,cAAc,CAC1D,UAAU,CAAC,kCAAkC,EAAE,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAChF,CAAC;AACF,MAAM,CAAC,MAAM,iCAAiC,GAAG,cAAc,CAC7D,UAAU,CAAC,oCAAoC,EAAE,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAClF,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ValkeyCacheMode, ValkeyCacheOptions } from "../types.mts";
|
|
2
|
+
import { type GlideClient, type GlideString } from "@valkey/valkey-glide";
|
|
3
|
+
import type { ValkeyBloomFilter } from "../bloom-filter.mts";
|
|
4
|
+
export type CacheEntry = {
|
|
5
|
+
value: string | Buffer | Record<string, unknown> | null;
|
|
6
|
+
ttlSecondsRemaining: number | null;
|
|
7
|
+
bloomMiss: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare abstract class ValkeyCacheCore<K = string> {
|
|
10
|
+
prefix: string;
|
|
11
|
+
ttl: number;
|
|
12
|
+
nullTtl: number;
|
|
13
|
+
mode: ValkeyCacheMode;
|
|
14
|
+
staleTtlAge: number;
|
|
15
|
+
protected bloomFilter?: ValkeyBloomFilter;
|
|
16
|
+
protected bloomFilterEnabled?: () => boolean;
|
|
17
|
+
protected refreshPromises: Map<string, Promise<void>>;
|
|
18
|
+
protected keySerializer: (key: K) => string;
|
|
19
|
+
protected client: GlideClient;
|
|
20
|
+
constructor({ prefix, ttlSeconds, nullTtlSeconds, mode, staleTtlAge, bloomFilter, bloomFilterEnabled, keySerializer, client, }: ValkeyCacheOptions<K>);
|
|
21
|
+
protected emitCacheEvents(hitKeys: string[], missKeys: string[], bloomMissKeys: string[]): void;
|
|
22
|
+
protected deduplicateKeys(keys: K[]): {
|
|
23
|
+
validKeys: K[];
|
|
24
|
+
serializedKeys: string[];
|
|
25
|
+
outputIndices: number[];
|
|
26
|
+
};
|
|
27
|
+
protected toSerializedKey(key: K): string | null;
|
|
28
|
+
protected getSerializedCacheKey(serializedKey: string): string;
|
|
29
|
+
protected getSerializedInvalidationKey(serializedKey: string): string;
|
|
30
|
+
protected getValueWithTtl(serializedKey: string): Promise<CacheEntry>;
|
|
31
|
+
protected getValuesWithTtl(serializedKeys: string[]): Promise<CacheEntry[]>;
|
|
32
|
+
protected shouldRefreshTtl(ttlSecondsRemaining: number | null): boolean;
|
|
33
|
+
protected decode(serializedKey: string, result: GlideString | null): Promise<string | Buffer | Record<string, unknown> | null>;
|
|
34
|
+
protected serializeValue(value: unknown): Promise<string | Buffer>;
|
|
35
|
+
protected abstract deleteBySerializedKey(...serializedKeys: string[]): Promise<number>;
|
|
36
|
+
private decodeCacheEntry;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=core.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.mts","sourceRoot":"","sources":["../../cache/core.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGxE,OAAO,EAAW,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG7D,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,8BAAsB,eAAe,CAAC,CAAC,GAAG,MAAM;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,eAAe,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAC1C,SAAS,CAAC,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IAC7C,SAAS,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,SAAS,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC;IAC5C,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC;gBAElB,EACV,MAAM,EACN,UAAU,EACV,cAAc,EACd,IAAa,EACb,WAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,MAA0B,GAC3B,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAmBxB,SAAS,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE;IAqBxF,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG;QACpC,SAAS,EAAE,CAAC,EAAE,CAAC;QACf,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,aAAa,EAAE,MAAM,EAAE,CAAC;KACzB;IAyBD,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,IAAI;IAMhD,SAAS,CAAC,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM;IAI9D,SAAS,CAAC,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM;cAIrD,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;cAmB3D,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAgBjF,SAAS,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI;cAO7C,MAAM,CACpB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,WAAW,GAAG,IAAI,GACzB,OAAO,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;cAkB5C,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAGxE,SAAS,CAAC,QAAQ,CAAC,qBAAqB,CAAC,GAAG,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;YACxE,gBAAgB;CAgB/B"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { cacheValkeyClient } from "../clients.mjs";
|
|
2
|
+
import { emitValkeyEvent } from "../events.mjs";
|
|
3
|
+
import { Decoder } from "@valkey/valkey-glide";
|
|
4
|
+
import { normalizeKey } from "../key-normalization.mjs";
|
|
5
|
+
import { handleValkeyError } from "../errors.mjs";
|
|
6
|
+
import { normalizeTtlResult } from "../utils.mjs";
|
|
7
|
+
import { decodeValue, serializeValue } from "../cache-utils.mjs";
|
|
8
|
+
import { CACHE_NAMESPACE, getValuesWithTtlScript, getValueWithTtlScript } from "./constants.mjs";
|
|
9
|
+
export class ValkeyCacheCore {
|
|
10
|
+
prefix;
|
|
11
|
+
ttl;
|
|
12
|
+
nullTtl;
|
|
13
|
+
mode;
|
|
14
|
+
staleTtlAge;
|
|
15
|
+
bloomFilter;
|
|
16
|
+
bloomFilterEnabled;
|
|
17
|
+
refreshPromises;
|
|
18
|
+
keySerializer;
|
|
19
|
+
client;
|
|
20
|
+
constructor({ prefix, ttlSeconds, nullTtlSeconds, mode = "json", staleTtlAge = 0.9, bloomFilter, bloomFilterEnabled, keySerializer, client = cacheValkeyClient, }) {
|
|
21
|
+
if (!prefix)
|
|
22
|
+
throw new Error("ValkeyCache requires a prefix");
|
|
23
|
+
if (!(ttlSeconds > 0))
|
|
24
|
+
throw new Error("ValkeyCache: ttlSeconds must be greater than 0");
|
|
25
|
+
this.prefix = prefix;
|
|
26
|
+
this.ttl = ttlSeconds;
|
|
27
|
+
this.nullTtl = nullTtlSeconds ?? Math.max(1, Math.floor(ttlSeconds / 60));
|
|
28
|
+
this.mode = mode;
|
|
29
|
+
if (Number.isNaN(staleTtlAge) || staleTtlAge < 0 || staleTtlAge > 1) {
|
|
30
|
+
throw new Error("ValkeyCache: staleTtlAge must be between 0 and 1");
|
|
31
|
+
}
|
|
32
|
+
this.staleTtlAge = staleTtlAge;
|
|
33
|
+
this.bloomFilter = bloomFilter;
|
|
34
|
+
this.bloomFilterEnabled = bloomFilterEnabled;
|
|
35
|
+
this.refreshPromises = new Map();
|
|
36
|
+
this.keySerializer =
|
|
37
|
+
keySerializer ?? ((key) => String(key));
|
|
38
|
+
this.client = client;
|
|
39
|
+
}
|
|
40
|
+
emitCacheEvents(hitKeys, missKeys, bloomMissKeys) {
|
|
41
|
+
if (hitKeys.length > 0)
|
|
42
|
+
emitValkeyEvent("cache:hit", {
|
|
43
|
+
cacheName: this.prefix,
|
|
44
|
+
keys: hitKeys,
|
|
45
|
+
count: hitKeys.length,
|
|
46
|
+
});
|
|
47
|
+
if (missKeys.length > 0)
|
|
48
|
+
emitValkeyEvent("cache:miss", {
|
|
49
|
+
cacheName: this.prefix,
|
|
50
|
+
keys: missKeys,
|
|
51
|
+
count: missKeys.length,
|
|
52
|
+
});
|
|
53
|
+
if (bloomMissKeys.length > 0)
|
|
54
|
+
emitValkeyEvent("cache:bloom-miss", {
|
|
55
|
+
cacheName: this.prefix,
|
|
56
|
+
keys: bloomMissKeys,
|
|
57
|
+
count: bloomMissKeys.length,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
deduplicateKeys(keys) {
|
|
61
|
+
const validKeys = [];
|
|
62
|
+
const serializedKeys = [];
|
|
63
|
+
const outputIndices = [];
|
|
64
|
+
const seen = new Map();
|
|
65
|
+
for (let i = 0; i < keys.length; i++) {
|
|
66
|
+
const serialized = this.toSerializedKey(keys[i]);
|
|
67
|
+
if (serialized === null) {
|
|
68
|
+
outputIndices.push(-1);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const existing = seen.get(serialized);
|
|
72
|
+
if (existing !== undefined) {
|
|
73
|
+
outputIndices.push(existing);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const idx = validKeys.length;
|
|
77
|
+
seen.set(serialized, idx);
|
|
78
|
+
validKeys.push(keys[i]);
|
|
79
|
+
serializedKeys.push(serialized);
|
|
80
|
+
outputIndices.push(idx);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { validKeys, serializedKeys, outputIndices };
|
|
84
|
+
}
|
|
85
|
+
toSerializedKey(key) {
|
|
86
|
+
if (key == null)
|
|
87
|
+
return null;
|
|
88
|
+
const serialized = normalizeKey(this.keySerializer(key));
|
|
89
|
+
return serialized === "" ? null : serialized;
|
|
90
|
+
}
|
|
91
|
+
getSerializedCacheKey(serializedKey) {
|
|
92
|
+
return `${CACHE_NAMESPACE}:${this.prefix}:{${serializedKey}}`;
|
|
93
|
+
}
|
|
94
|
+
getSerializedInvalidationKey(serializedKey) {
|
|
95
|
+
return `${CACHE_NAMESPACE}:${this.prefix}:invalidation:{${serializedKey}}`;
|
|
96
|
+
}
|
|
97
|
+
async getValueWithTtl(serializedKey) {
|
|
98
|
+
const cacheKey = this.getSerializedCacheKey(serializedKey);
|
|
99
|
+
const bloomEnabled = this.bloomFilterEnabled?.() ?? true;
|
|
100
|
+
const bloomFilterKey = bloomEnabled ? (this.bloomFilter?.getKey() ?? "") : "";
|
|
101
|
+
const scriptResult = await this.client.invokeScript(getValueWithTtlScript, {
|
|
102
|
+
keys: [cacheKey],
|
|
103
|
+
args: [bloomFilterKey],
|
|
104
|
+
decoder: Decoder.Bytes,
|
|
105
|
+
});
|
|
106
|
+
const valueRaw = Array.isArray(scriptResult) ? scriptResult[0] : null;
|
|
107
|
+
const ttlSecondsRemaining = Array.isArray(scriptResult)
|
|
108
|
+
? normalizeTtlResult(scriptResult[1])
|
|
109
|
+
: null;
|
|
110
|
+
const bloomMiss = Number(Array.isArray(scriptResult) ? (scriptResult[2] ?? 0) : 0);
|
|
111
|
+
if (bloomMiss === 1)
|
|
112
|
+
return { value: null, ttlSecondsRemaining: null, bloomMiss: true };
|
|
113
|
+
const value = await this.decode(serializedKey, valueRaw);
|
|
114
|
+
return { value, ttlSecondsRemaining: ttlSecondsRemaining ?? null, bloomMiss: false };
|
|
115
|
+
}
|
|
116
|
+
async getValuesWithTtl(serializedKeys) {
|
|
117
|
+
if (serializedKeys.length === 0)
|
|
118
|
+
return [];
|
|
119
|
+
const cacheKeys = serializedKeys.map((k) => this.getSerializedCacheKey(k));
|
|
120
|
+
const bloomEnabled = this.bloomFilterEnabled?.() ?? true;
|
|
121
|
+
const bloomFilterKey = bloomEnabled ? (this.bloomFilter?.getKey() ?? "") : "";
|
|
122
|
+
const scriptResult = await this.client.invokeScript(getValuesWithTtlScript, {
|
|
123
|
+
keys: cacheKeys,
|
|
124
|
+
args: [bloomFilterKey],
|
|
125
|
+
decoder: Decoder.Bytes,
|
|
126
|
+
});
|
|
127
|
+
const rawResults = Array.isArray(scriptResult) ? scriptResult : [];
|
|
128
|
+
return Promise.all(serializedKeys.map((key, index) => this.decodeCacheEntry(key, rawResults, index)));
|
|
129
|
+
}
|
|
130
|
+
shouldRefreshTtl(ttlSecondsRemaining) {
|
|
131
|
+
if (typeof ttlSecondsRemaining !== "number")
|
|
132
|
+
return false;
|
|
133
|
+
if (ttlSecondsRemaining < 0)
|
|
134
|
+
return false;
|
|
135
|
+
const refreshWindow = (1 - this.staleTtlAge) * this.ttl;
|
|
136
|
+
return refreshWindow > 0 && ttlSecondsRemaining < refreshWindow;
|
|
137
|
+
}
|
|
138
|
+
async decode(serializedKey, result) {
|
|
139
|
+
try {
|
|
140
|
+
return await decodeValue(result, this.mode);
|
|
141
|
+
}
|
|
142
|
+
catch (cause) {
|
|
143
|
+
if (process.env.NODE_ENV === "test")
|
|
144
|
+
throw cause;
|
|
145
|
+
const isJsonError = cause instanceof SyntaxError;
|
|
146
|
+
const err = new Error(isJsonError
|
|
147
|
+
? "ValkeyCache: failed to parse JSON value"
|
|
148
|
+
: "ValkeyCache: failed to gunzip value", { cause });
|
|
149
|
+
handleValkeyError(err);
|
|
150
|
+
this.deleteBySerializedKey(serializedKey).catch(handleValkeyError);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async serializeValue(value) {
|
|
155
|
+
return await serializeValue(value, this.mode);
|
|
156
|
+
}
|
|
157
|
+
async decodeCacheEntry(key, rawResults, index) {
|
|
158
|
+
const offset = index * 3;
|
|
159
|
+
const bloomMiss = Number(rawResults[offset + 2] ?? 0) === 1;
|
|
160
|
+
if (bloomMiss)
|
|
161
|
+
return { value: null, ttlSecondsRemaining: null, bloomMiss: true };
|
|
162
|
+
const rawValue = rawResults[offset];
|
|
163
|
+
const value = await this.decode(key, rawValue);
|
|
164
|
+
return {
|
|
165
|
+
value,
|
|
166
|
+
ttlSecondsRemaining: normalizeTtlResult(rawResults[offset + 1]),
|
|
167
|
+
bloomMiss: false,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=core.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.mjs","sourceRoot":"","sources":["../../cache/core.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ValkeyCacheMutations } from "./mutations.mts";
|
|
2
|
+
export declare class ValkeyCacheDeletes<K = string> extends ValkeyCacheMutations<K> {
|
|
3
|
+
delete(...keys: K[]): Promise<number>;
|
|
4
|
+
protected deleteBySerializedKey(...serializedKeys: string[]): Promise<number>;
|
|
5
|
+
invalidate(): Promise<void>;
|
|
6
|
+
static invalidate(prefix: string, client?: import("@valkey/valkey-glide").GlideClient): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=deletes.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deletes.d.mts","sourceRoot":"","sources":["../../cache/deletes.mts"],"names":[],"mappings":"AASA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,qBAAa,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,oBAAoB,CAAC,CAAC,CAAC;IACnE,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;cAmB3B,qBAAqB,CAAC,GAAG,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAQnF,UAAU;WAIG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,6CAAoB;CAOnE"}
|