valkyries 0.9.0 → 0.10.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 +12 -1
- package/dist/delete.d.mts +12 -0
- package/dist/delete.d.mts.map +1 -1
- package/dist/delete.mjs +39 -0
- package/dist/delete.mjs.map +1 -1
- package/dist/diagnostics.d.mts +37 -0
- package/dist/diagnostics.d.mts.map +1 -0
- package/dist/diagnostics.mjs +104 -0
- package/dist/diagnostics.mjs.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/retry.d.mts +6 -2
- package/dist/retry.d.mts.map +1 -1
- package/dist/retry.mjs +7 -2
- package/dist/retry.mjs.map +1 -1
- package/dist/shutdown.d.mts +19 -1
- package/dist/shutdown.d.mts.map +1 -1
- package/dist/shutdown.mjs +28 -12
- package/dist/shutdown.mjs.map +1 -1
- package/docs/api.md +2 -0
- package/docs/clients.md +15 -3
- package/docs/configuration.md +4 -0
- package/docs/diagnostics.md +50 -0
- package/docs/utilities.md +19 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -126,7 +126,7 @@ const client = await GlideClient.createClient(glideConfigFromUrl("redis://localh
|
|
|
126
126
|
const cache = new ValkeyCache({ prefix: "custom", ttlSeconds: 60, client });
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
Call `closeValkeyClients()` to close package-managed clients.
|
|
129
|
+
Call `closeValkeyClients()` to close package-managed clients. Pass `{ throwOnError: true }` to reject with an `AggregateError` if any close fails.
|
|
130
130
|
|
|
131
131
|
## Scan and Unlink Keys
|
|
132
132
|
|
|
@@ -146,6 +146,16 @@ The result reports `scannedKeys`, `matchedKeys`, and keys confirmed removed by `
|
|
|
146
146
|
`unlinkedKeys`. `SCAN` is non-snapshot, so these counts can include duplicates or omit keys while
|
|
147
147
|
the keyspace changes.
|
|
148
148
|
|
|
149
|
+
Use `scanAndUnlinkKeyPrefixes()` to flush several literal prefixes serially. Prefixes are
|
|
150
|
+
glob-escaped, later prefixes still run after a failure, and several failures are combined into an
|
|
151
|
+
`AggregateError` that reports `unlinkedKeys`:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const { unlinkedKeys } = await scanAndUnlinkKeyPrefixes(client, ["cache:users:", "cache:teams:"], {
|
|
155
|
+
signal: abortController.signal,
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
149
159
|
## Expire Keys Without a TTL
|
|
150
160
|
|
|
151
161
|
Use `expireKeysWithNoExpiry()` to apply a TTL only when matching keys still have no expiry:
|
|
@@ -181,6 +191,7 @@ include duplicates or omit keys while the keyspace changes. `scanCount` is a SCA
|
|
|
181
191
|
- [Conditional operations](docs/conditional-operations.md)
|
|
182
192
|
- [Events and metrics](docs/events-and-metrics.md)
|
|
183
193
|
- [Lua scripts](docs/lua-scripts.md)
|
|
194
|
+
- [Diagnostics](docs/diagnostics.md)
|
|
184
195
|
- [Utilities](docs/utilities.md)
|
|
185
196
|
- [Testing and CI](docs/testing-and-ci.md)
|
|
186
197
|
- [Migration guide](docs/migration.md)
|
package/dist/delete.d.mts
CHANGED
|
@@ -8,4 +8,16 @@ export declare function deleteKeysWithLiteralPrefixes(client: ScanAndUnlinkKeysC
|
|
|
8
8
|
* omit keys, or differ from the number of keys that exist when this function returns.
|
|
9
9
|
*/
|
|
10
10
|
export declare function scanAndUnlinkKeys(client: ScanAndUnlinkKeysClient, pattern: string, { signal, matches }?: ScanAndUnlinkKeysOptions): Promise<ScanAndUnlinkKeysResult>;
|
|
11
|
+
export type ScanAndUnlinkKeyPrefixesResult = {
|
|
12
|
+
unlinkedKeys: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Unlinks every key that starts with any of the literal prefixes, scanning one prefix at a time.
|
|
16
|
+
*
|
|
17
|
+
* Each prefix is glob-escaped, so `*`, `?`, `[`, `]` and `\` match literally. A failing prefix does
|
|
18
|
+
* not stop later prefixes. One failure is rethrown as-is; several are combined into an
|
|
19
|
+
* `AggregateError` with an `unlinkedKeys` property. Failures reach the Valkey error handler once,
|
|
20
|
+
* through `scanAndUnlinkKeys`. An aborted `signal` rethrows its exact reason immediately.
|
|
21
|
+
*/
|
|
22
|
+
export declare function scanAndUnlinkKeyPrefixes(client: ScanAndUnlinkKeysClient, prefixes: readonly string[], options?: ScanAndUnlinkKeysOptions): Promise<ScanAndUnlinkKeyPrefixesResult>;
|
|
11
23
|
//# sourceMappingURL=delete.d.mts.map
|
package/dist/delete.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.d.mts","sourceRoot":"","sources":["../delete.mts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,aAAa,CAAC;AAIrB,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,MAAM,EACf,EAAE,MAAM,EAAE,OAAoB,EAAE,GAAE,wBAA6B,GAC9D,OAAO,CAAC,uBAAuB,CAAC,CA4BlC"}
|
|
1
|
+
{"version":3,"file":"delete.d.mts","sourceRoot":"","sources":["../delete.mts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,aAAa,CAAC;AAIrB,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,MAAM,EACf,EAAE,MAAM,EAAE,OAAoB,EAAE,GAAE,wBAA6B,GAC9D,OAAO,CAAC,uBAAuB,CAAC,CA4BlC;AAED,MAAM,MAAM,8BAA8B,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAMtE;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,uBAAuB,EAC/B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,8BAA8B,CAAC,CAiCzC"}
|
package/dist/delete.mjs
CHANGED
|
@@ -46,4 +46,43 @@ export async function scanAndUnlinkKeys(client, pattern, { signal, matches = ()
|
|
|
46
46
|
throw err;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
function escapeGlobLiteral(value) {
|
|
50
|
+
return value.replace(/[\\*?[\]]/g, "\\$&");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Unlinks every key that starts with any of the literal prefixes, scanning one prefix at a time.
|
|
54
|
+
*
|
|
55
|
+
* Each prefix is glob-escaped, so `*`, `?`, `[`, `]` and `\` match literally. A failing prefix does
|
|
56
|
+
* not stop later prefixes. One failure is rethrown as-is; several are combined into an
|
|
57
|
+
* `AggregateError` with an `unlinkedKeys` property. Failures reach the Valkey error handler once,
|
|
58
|
+
* through `scanAndUnlinkKeys`. An aborted `signal` rethrows its exact reason immediately.
|
|
59
|
+
*/
|
|
60
|
+
export async function scanAndUnlinkKeyPrefixes(client, prefixes, options = {}) {
|
|
61
|
+
for (const prefix of prefixes) {
|
|
62
|
+
if (typeof prefix !== "string" || prefix === "") {
|
|
63
|
+
throw new TypeError("scanAndUnlinkKeyPrefixes prefixes must be non-empty strings");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const { signal } = options;
|
|
67
|
+
const failures = [];
|
|
68
|
+
let unlinkedKeys = 0;
|
|
69
|
+
for (const prefix of new Set(prefixes)) {
|
|
70
|
+
throwIfAborted(signal);
|
|
71
|
+
try {
|
|
72
|
+
const result = await scanAndUnlinkKeys(client, `${escapeGlobLiteral(prefix)}*`, options);
|
|
73
|
+
unlinkedKeys += result.unlinkedKeys;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (signal?.aborted && err === signal.reason)
|
|
77
|
+
throw err;
|
|
78
|
+
failures.push(err);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (failures.length === 1)
|
|
82
|
+
throw failures[0];
|
|
83
|
+
if (failures.length > 1) {
|
|
84
|
+
throw Object.assign(new AggregateError(failures, `${failures.length} prefix scans failed after unlinking ${unlinkedKeys} keys`), { unlinkedKeys });
|
|
85
|
+
}
|
|
86
|
+
return { unlinkedKeys };
|
|
87
|
+
}
|
|
49
88
|
//# sourceMappingURL=delete.mjs.map
|
package/dist/delete.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.mjs","sourceRoot":"","sources":["../delete.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAO/E,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAA+B,EAC/B,OAAe;IAEf,MAAM,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,MAA+B,EAC/B,OAAe,EACf,QAA2B;IAE3B,MAAM,cAAc,GAAG,CAAC,GAAG,IAAI,GAAG,CAAU,QAAQ,CAAC,CAAC,CAAC,MAAM,CAC3D,CAAC,MAAM,EAAoB,EAAE,CAAC,OAAO,MAAM,KAAK,QAAQ,CACzD,CAAC;IACF,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAExC,MAAM,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE;QACvC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;KAC/F,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAA+B,EAC/B,OAAe,EACf,EAAE,MAAM,EAAE,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,EAAE,GAA6B,EAAE;IAE/D,IAAI,CAAC;QACH,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,IAAI,KAAK,EAAE,MAAM,OAAO,IAAI,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE;YACxD,KAAK,EAAE,kBAAkB;YACzB,MAAM;SACP,CAAC,EAAE,CAAC;YACH,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;YAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrC,cAAc,CAAC,MAAM,CAAC,CAAC;YACvB,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC;YAE3B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,cAAc,CAAC,MAAM,CAAC,CAAC;gBACvB,YAAY,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC1C,cAAc,CAAC,MAAM,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAED,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,MAAM,EAAE,OAAO,IAAI,GAAG,KAAK,MAAM,CAAC,MAAM;YAAE,MAAM,GAAG,CAAC;QACxD,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvB,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"delete.mjs","sourceRoot":"","sources":["../delete.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type GlideClient } from "@valkey/valkey-glide";
|
|
2
|
+
export type ValkeyMemorySummary = {
|
|
3
|
+
usedMemoryBytes: number;
|
|
4
|
+
usedMemoryRssBytes: number;
|
|
5
|
+
usedMemoryPeakBytes: number;
|
|
6
|
+
maxmemoryBytes: number;
|
|
7
|
+
maxmemoryPolicy: string;
|
|
8
|
+
usedMemoryDatasetBytes: number | null;
|
|
9
|
+
lazyfreePendingObjects: number | null;
|
|
10
|
+
memFragmentationRatio: number | null;
|
|
11
|
+
};
|
|
12
|
+
export type DiagnosticsOptions = {
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
};
|
|
15
|
+
export type CountKeysByPrefixOptions = DiagnosticsOptions & {
|
|
16
|
+
/** SCAN COUNT hint per page. Defaults to 500. */
|
|
17
|
+
count?: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Parses `INFO memory` output (CRLF or LF, `#` section headers) into typed numbers.
|
|
21
|
+
* Throws when a required field is missing or any present numeric field is malformed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseValkeyMemoryInfo(info: string): ValkeyMemorySummary;
|
|
24
|
+
/** Runs `INFO memory` and parses it. Checks `signal` before and after the command. */
|
|
25
|
+
export declare function getValkeyMemoryInfo(client: Pick<GlideClient, "info">, { signal }?: DiagnosticsOptions): Promise<ValkeyMemorySummary>;
|
|
26
|
+
/**
|
|
27
|
+
* Rejects empty prefixes, a group named `unclassified`, and prefixes that overlap across
|
|
28
|
+
* groups (identical, or one a prefix of another), so every key belongs to at most one group.
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateKeyPrefixRegistry(prefixes: Record<string, readonly string[]>): void;
|
|
31
|
+
/**
|
|
32
|
+
* Counts keys per prefix group with bounded SCAN pages. Keys matching no prefix are counted as
|
|
33
|
+
* `unclassified`. SCAN is non-snapshot, so counts may include duplicates or miss keys written
|
|
34
|
+
* during the walk. `signal` is checked before and after each page.
|
|
35
|
+
*/
|
|
36
|
+
export declare function countKeysByPrefix<G extends string>(client: Pick<GlideClient, "scan">, prefixes: Record<G, readonly string[]>, { signal, count }?: CountKeysByPrefixOptions): Promise<Record<G | "unclassified", number>>;
|
|
37
|
+
//# sourceMappingURL=diagnostics.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.mts","sourceRoot":"","sources":["../diagnostics.mts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAMrE,MAAM,MAAM,mBAAmB,GAAG;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAE1D,MAAM,MAAM,wBAAwB,GAAG,kBAAkB,GAAG;IAC1D,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4BF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAsBvE;AAED,sFAAsF;AACtF,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACjC,EAAE,MAAM,EAAE,GAAE,kBAAuB,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAK9B;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,GAAG,IAAI,CAmB3F;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EACtD,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACjC,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC,EACtC,EAAE,MAAM,EAAE,KAA0B,EAAE,GAAE,wBAA6B,GACpE,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,cAAc,EAAE,MAAM,CAAC,CAAC,CAc7C"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { InfoOptions } from "@valkey/valkey-glide";
|
|
2
|
+
import { glideStringToString, scanKeyPages, throwIfAborted } from "./scan.mjs";
|
|
3
|
+
const DEFAULT_SCAN_COUNT = 500;
|
|
4
|
+
const UNCLASSIFIED = "unclassified";
|
|
5
|
+
function parseFields(info) {
|
|
6
|
+
const fields = new Map();
|
|
7
|
+
for (const raw of info.split("\n")) {
|
|
8
|
+
const line = raw.trim();
|
|
9
|
+
const separator = line.indexOf(":");
|
|
10
|
+
if (line.startsWith("#") || separator < 0)
|
|
11
|
+
continue;
|
|
12
|
+
fields.set(line.slice(0, separator), line.slice(separator + 1));
|
|
13
|
+
}
|
|
14
|
+
return fields;
|
|
15
|
+
}
|
|
16
|
+
function parseInteger(name, value) {
|
|
17
|
+
const parsed = Number(value);
|
|
18
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(parsed)) {
|
|
19
|
+
throw new Error(`INFO field ${name} is not a non-negative safe integer: ${value}`);
|
|
20
|
+
}
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
function parseRatio(name, value) {
|
|
24
|
+
if (!/^\d+(\.\d+)?$/.test(value) || !Number.isFinite(Number(value))) {
|
|
25
|
+
throw new Error(`INFO field ${name} is not a non-negative number: ${value}`);
|
|
26
|
+
}
|
|
27
|
+
return Number(value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parses `INFO memory` output (CRLF or LF, `#` section headers) into typed numbers.
|
|
31
|
+
* Throws when a required field is missing or any present numeric field is malformed.
|
|
32
|
+
*/
|
|
33
|
+
export function parseValkeyMemoryInfo(info) {
|
|
34
|
+
const fields = parseFields(info);
|
|
35
|
+
const required = (name) => {
|
|
36
|
+
const value = fields.get(name);
|
|
37
|
+
if (!value)
|
|
38
|
+
throw new Error(`INFO field ${name} is missing`);
|
|
39
|
+
return value;
|
|
40
|
+
};
|
|
41
|
+
const optional = (name, parse) => {
|
|
42
|
+
const value = fields.get(name);
|
|
43
|
+
return value === undefined ? null : parse(name, value);
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
usedMemoryBytes: parseInteger("used_memory", required("used_memory")),
|
|
47
|
+
usedMemoryRssBytes: parseInteger("used_memory_rss", required("used_memory_rss")),
|
|
48
|
+
usedMemoryPeakBytes: parseInteger("used_memory_peak", required("used_memory_peak")),
|
|
49
|
+
maxmemoryBytes: parseInteger("maxmemory", required("maxmemory")),
|
|
50
|
+
maxmemoryPolicy: required("maxmemory_policy"),
|
|
51
|
+
usedMemoryDatasetBytes: optional("used_memory_dataset", parseInteger),
|
|
52
|
+
lazyfreePendingObjects: optional("lazyfree_pending_objects", parseInteger),
|
|
53
|
+
memFragmentationRatio: optional("mem_fragmentation_ratio", parseRatio),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Runs `INFO memory` and parses it. Checks `signal` before and after the command. */
|
|
57
|
+
export async function getValkeyMemoryInfo(client, { signal } = {}) {
|
|
58
|
+
throwIfAborted(signal);
|
|
59
|
+
const info = await client.info([InfoOptions.Memory]);
|
|
60
|
+
throwIfAborted(signal);
|
|
61
|
+
return parseValkeyMemoryInfo(glideStringToString(info));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Rejects empty prefixes, a group named `unclassified`, and prefixes that overlap across
|
|
65
|
+
* groups (identical, or one a prefix of another), so every key belongs to at most one group.
|
|
66
|
+
*/
|
|
67
|
+
export function validateKeyPrefixRegistry(prefixes) {
|
|
68
|
+
const seen = [];
|
|
69
|
+
for (const [group, groupPrefixes] of Object.entries(prefixes)) {
|
|
70
|
+
if (group === UNCLASSIFIED)
|
|
71
|
+
throw new Error(`Prefix group name is reserved: ${group}`);
|
|
72
|
+
for (const prefix of groupPrefixes) {
|
|
73
|
+
if (prefix === "")
|
|
74
|
+
throw new Error(`Prefix group ${group} contains an empty prefix`);
|
|
75
|
+
const other = seen.find((entry) => entry.group !== group &&
|
|
76
|
+
(entry.prefix.startsWith(prefix) || prefix.startsWith(entry.prefix)));
|
|
77
|
+
if (other) {
|
|
78
|
+
throw new Error(`Prefix ${JSON.stringify(prefix)} in ${group} overlaps ${JSON.stringify(other.prefix)} in ${other.group}`);
|
|
79
|
+
}
|
|
80
|
+
seen.push({ group, prefix });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Counts keys per prefix group with bounded SCAN pages. Keys matching no prefix are counted as
|
|
86
|
+
* `unclassified`. SCAN is non-snapshot, so counts may include duplicates or miss keys written
|
|
87
|
+
* during the walk. `signal` is checked before and after each page.
|
|
88
|
+
*/
|
|
89
|
+
export async function countKeysByPrefix(client, prefixes, { signal, count = DEFAULT_SCAN_COUNT } = {}) {
|
|
90
|
+
validateKeyPrefixRegistry(prefixes);
|
|
91
|
+
const groups = Object.keys(prefixes);
|
|
92
|
+
const counts = { [UNCLASSIFIED]: 0 };
|
|
93
|
+
for (const group of groups)
|
|
94
|
+
counts[group] = 0;
|
|
95
|
+
for await (const keys of scanKeyPages(client, "*", { count, signal })) {
|
|
96
|
+
for (const key of keys) {
|
|
97
|
+
const name = glideStringToString(key);
|
|
98
|
+
const group = groups.find((g) => prefixes[g].some((prefix) => name.startsWith(prefix)));
|
|
99
|
+
counts[group ?? UNCLASSIFIED] += 1;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return counts;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=diagnostics.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.mjs","sourceRoot":"","sources":["../diagnostics.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAoB,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,MAAM,YAAY,GAAG,cAAc,CAAC;AAoBpC,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC;YAAE,SAAS;QACpD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,KAAa;IAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,wCAAwC,KAAK,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,UAAU,CAAC,IAAY,EAAE,KAAa;IAC7C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,kCAAkC,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE;QACxC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,aAAa,CAAC,CAAC;QAC7D,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,KAAuC,EAAiB,EAAE;QACxF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC;IAEF,OAAO;QACL,eAAe,EAAE,YAAY,CAAC,aAAa,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;QACrE,kBAAkB,EAAE,YAAY,CAAC,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAC;QAChF,mBAAmB,EAAE,YAAY,CAAC,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;QACnF,cAAc,EAAE,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;QAChE,eAAe,EAAE,QAAQ,CAAC,kBAAkB,CAAC;QAC7C,sBAAsB,EAAE,QAAQ,CAAC,qBAAqB,EAAE,YAAY,CAAC;QACrE,sBAAsB,EAAE,QAAQ,CAAC,0BAA0B,EAAE,YAAY,CAAC;QAC1E,qBAAqB,EAAE,QAAQ,CAAC,yBAAyB,EAAE,UAAU,CAAC;KACvE,CAAC;AACJ,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAiC,EACjC,EAAE,MAAM,EAAE,GAAuB,EAAE;IAEnC,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IACrD,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,OAAO,qBAAqB,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAA2C;IACnF,MAAM,IAAI,GAAwC,EAAE,CAAC;IACrD,KAAK,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,IAAI,KAAK,KAAK,YAAY;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,KAAK,EAAE,CAAC,CAAC;QACvF,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;YACnC,IAAI,MAAM,KAAK,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,KAAK,2BAA2B,CAAC,CAAC;YACrF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CACrB,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,KAAK,KAAK,KAAK;gBACrB,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CACvE,CAAC;YACF,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,KAAK,aAAa,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,KAAK,EAAE,CAC1G,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAiC,EACjC,QAAsC,EACtC,EAAE,MAAM,EAAE,KAAK,GAAG,kBAAkB,EAAE,GAA6B,EAAE;IAErE,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAQ,CAAC;IAC5C,MAAM,MAAM,GAA2B,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC;IAC7D,KAAK,MAAM,KAAK,IAAI,MAAM;QAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE9C,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,YAAY,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACtE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACxF,MAAM,CAAC,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,MAA4C,CAAC;AACtD,CAAC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -13,7 +13,9 @@ export * from "./errors.mts";
|
|
|
13
13
|
export * from "./key-normalization.mts";
|
|
14
14
|
export * from "./cache-metrics.mts";
|
|
15
15
|
export * from "./retry.mts";
|
|
16
|
-
export { scanAndUnlinkKeys } from "./delete.mts";
|
|
16
|
+
export { scanAndUnlinkKeyPrefixes, scanAndUnlinkKeys } from "./delete.mts";
|
|
17
17
|
export { expireKeysWithNoExpiry } from "./expiry.mts";
|
|
18
|
+
export { countKeysByPrefix, getValkeyMemoryInfo, parseValkeyMemoryInfo, validateKeyPrefixRegistry, } from "./diagnostics.mts";
|
|
19
|
+
export type { CountKeysByPrefixOptions, DiagnosticsOptions, ValkeyMemorySummary, } from "./diagnostics.mts";
|
|
18
20
|
export type { RateLimiterAddAndCheckWindowsOptions, RateLimiterOptions, RateLimiterWindow, ValkeyBloomFilterOptions, ValkeyCacheOptions, ValkeyCacheMode, ValkeyCacheResponse, DynamicConfigField, DynamicConfigFieldType, DynamicConfigOptions, ScanAndUnlinkKeysClient, ScanAndUnlinkKeysOptions, ScanAndUnlinkKeysResult, ExpireKeysWithNoExpiryOptions, ExpireKeysWithNoExpiryResult, } from "./types.mts";
|
|
19
21
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../index.mts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../index.mts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,oCAAoC,EACpC,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,aAAa,CAAC"}
|
package/dist/index.mjs
CHANGED
|
@@ -13,6 +13,7 @@ export * from "./errors.mjs";
|
|
|
13
13
|
export * from "./key-normalization.mjs";
|
|
14
14
|
export * from "./cache-metrics.mjs";
|
|
15
15
|
export * from "./retry.mjs";
|
|
16
|
-
export { scanAndUnlinkKeys } from "./delete.mjs";
|
|
16
|
+
export { scanAndUnlinkKeyPrefixes, scanAndUnlinkKeys } from "./delete.mjs";
|
|
17
17
|
export { expireKeysWithNoExpiry } from "./expiry.mjs";
|
|
18
|
+
export { countKeysByPrefix, getValkeyMemoryInfo, parseValkeyMemoryInfo, validateKeyPrefixRegistry, } from "./diagnostics.mjs";
|
|
18
19
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../index.mts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../index.mts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC"}
|
package/dist/retry.d.mts
CHANGED
|
@@ -33,10 +33,10 @@ export type RetryValkeyOperationOptions = {
|
|
|
33
33
|
*/
|
|
34
34
|
export declare function retryValkeyOperation<T>(fn: () => Promise<T>, options?: RetryValkeyOperationOptions): Promise<T>;
|
|
35
35
|
export type RetrySaturationErrorOptions = {
|
|
36
|
-
/** Maximum number of attempts (default: 3). */
|
|
36
|
+
/** Maximum number of attempts (default: `VALKEY_INFLIGHT_RETRY_ATTEMPTS`, or 3). */
|
|
37
37
|
attempts?: number;
|
|
38
38
|
/**
|
|
39
|
-
* Minimum milliseconds between attempts (default: 1000).
|
|
39
|
+
* Minimum milliseconds between attempts (default: `VALKEY_INFLIGHT_RETRY_DELAY_MS`, or 1000).
|
|
40
40
|
* Actual delay is jittered in [delayMs, delayMs * 5] to spread concurrent retriers.
|
|
41
41
|
*/
|
|
42
42
|
delayMs?: number;
|
|
@@ -45,6 +45,10 @@ export type RetrySaturationErrorOptions = {
|
|
|
45
45
|
* Retries a Valkey operation only on inflight-saturation rejections
|
|
46
46
|
* (`Reached maximum inflight requests`), with jittered delay in [delayMs, delayMs*5].
|
|
47
47
|
*
|
|
48
|
+
* When `attempts` / `delayMs` are omitted they default to `config.inflight_retry_attempts` /
|
|
49
|
+
* `config.inflight_retry_delay_ms` (`VALKEY_INFLIGHT_RETRY_ATTEMPTS` /
|
|
50
|
+
* `VALKEY_INFLIGHT_RETRY_DELAY_MS`); explicit options take precedence.
|
|
51
|
+
*
|
|
48
52
|
* All other errors are rethrown immediately. Use this at internal command boundaries
|
|
49
53
|
* so callers don't need to wrap each call site.
|
|
50
54
|
*
|
package/dist/retry.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.d.mts","sourceRoot":"","sources":["../retry.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retry.d.mts","sourceRoot":"","sources":["../retry.mts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAI9D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGzD;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IAC1C;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,CAAC,CAAC,CA8BZ;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,CAAC,CAAC,CAOZ"}
|
package/dist/retry.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomInt } from "node:crypto";
|
|
2
|
+
import { config } from "./config.mjs";
|
|
2
3
|
/**
|
|
3
4
|
* Substring patterns that identify transient Valkey / Glide errors which are safe to retry.
|
|
4
5
|
* These originate from the @valkey/valkey-glide native Rust core.
|
|
@@ -72,6 +73,10 @@ export async function retryValkeyOperation(fn, options) {
|
|
|
72
73
|
* Retries a Valkey operation only on inflight-saturation rejections
|
|
73
74
|
* (`Reached maximum inflight requests`), with jittered delay in [delayMs, delayMs*5].
|
|
74
75
|
*
|
|
76
|
+
* When `attempts` / `delayMs` are omitted they default to `config.inflight_retry_attempts` /
|
|
77
|
+
* `config.inflight_retry_delay_ms` (`VALKEY_INFLIGHT_RETRY_ATTEMPTS` /
|
|
78
|
+
* `VALKEY_INFLIGHT_RETRY_DELAY_MS`); explicit options take precedence.
|
|
79
|
+
*
|
|
75
80
|
* All other errors are rethrown immediately. Use this at internal command boundaries
|
|
76
81
|
* so callers don't need to wrap each call site.
|
|
77
82
|
*
|
|
@@ -80,8 +85,8 @@ export async function retryValkeyOperation(fn, options) {
|
|
|
80
85
|
*/
|
|
81
86
|
export function retrySaturationError(fn, options) {
|
|
82
87
|
return retryValkeyOperation(fn, {
|
|
83
|
-
attempts: options?.attempts,
|
|
84
|
-
delayMs: options?.delayMs,
|
|
88
|
+
attempts: options?.attempts ?? config.inflight_retry_attempts,
|
|
89
|
+
delayMs: options?.delayMs ?? config.inflight_retry_delay_ms,
|
|
85
90
|
shouldRetry: isSaturationError,
|
|
86
91
|
jitter: true,
|
|
87
92
|
});
|
package/dist/retry.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.mjs","sourceRoot":"","sources":["../retry.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"retry.mjs","sourceRoot":"","sources":["../retry.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;GAGG;AACH,MAAM,kBAAkB,GAAG;IACzB,mCAAmC;IACnC,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,4BAA4B;CAC7B,CAAC;AAEF,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AAE/D;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1B,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AACpD,CAAC;AAgBD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAoB,EACpB,OAAqC;IAErC,MAAM,EACJ,QAAQ,GAAG,CAAC,EACZ,OAAO,GAAG,IAAI,EACd,WAAW,GAAG,sBAAsB,EACpC,MAAM,GAAG,KAAK,GACf,GAAG,OAAO,IAAI,EAAE,CAAC;IAClB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC1C,IAAI,SAAkB,CAAC;IACvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YACrC,SAAS,GAAG,KAAK,CAAC;YAClB,IAAI,OAAO,GAAG,WAAW,GAAG,CAAC,EAAE,CAAC;gBAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;gBAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,SAAS,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;gBACtF,oFAAoF;gBACpF,qFAAqF;gBACrF,uFAAuF;gBACvF,yFAAyF;gBACzF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBAClC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBAC1C,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;gBACnB,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAC;AAClB,CAAC;AAYD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAClC,EAAoB,EACpB,OAAqC;IAErC,OAAO,oBAAoB,CAAC,EAAE,EAAE;QAC9B,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,MAAM,CAAC,uBAAuB;QAC7D,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,MAAM,CAAC,uBAAuB;QAC3D,WAAW,EAAE,iBAAiB;QAC9B,MAAM,EAAE,IAAI;KACb,CAAC,CAAC;AACL,CAAC"}
|
package/dist/shutdown.d.mts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
export interface CloseValkeyClientsOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Reject with an `AggregateError` after every close has settled if any close failed.
|
|
4
|
+
* Failures are still reported to `handleValkeyError`. Defaults to `false`.
|
|
5
|
+
*/
|
|
6
|
+
throwOnError?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Closes every live `DynamicConfig`, then every package-managed client and the
|
|
10
|
+
* dynamic-config subscription client. Closing `dynamicConfigs` beforehand is unnecessary.
|
|
11
|
+
*
|
|
12
|
+
* Shutdown runs once per module instance. Later calls are no-ops that resolve immediately,
|
|
13
|
+
* even if they pass `throwOnError` and an earlier call did not (or the first run failed).
|
|
14
|
+
*/
|
|
15
|
+
export declare const closeValkeyClients: (options?: CloseValkeyClientsOptions) => Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Same as `closeValkeyClients()` but takes no arguments, so it is safe to pass directly as a
|
|
18
|
+
* signal or lifecycle callback that supplies its own arguments. Never rejects on close failures.
|
|
19
|
+
*/
|
|
1
20
|
export declare const onGracefulShutdown: () => Promise<void>;
|
|
2
|
-
export declare const closeValkeyClients: typeof onGracefulShutdown;
|
|
3
21
|
//# sourceMappingURL=shutdown.d.mts.map
|
package/dist/shutdown.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.d.mts","sourceRoot":"","sources":["../shutdown.mts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"shutdown.d.mts","sourceRoot":"","sources":["../shutdown.mts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,aAAoB,yBAAyB,KAAG,OAAO,CAAC,IAAI,CA0B1F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,OAAO,CAAC,IAAI,CAAyB,CAAC"}
|
package/dist/shutdown.mjs
CHANGED
|
@@ -15,25 +15,41 @@ const safeClose = (obj) => {
|
|
|
15
15
|
return Promise.reject(error);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Closes every live `DynamicConfig`, then every package-managed client and the
|
|
20
|
+
* dynamic-config subscription client. Closing `dynamicConfigs` beforehand is unnecessary.
|
|
21
|
+
*
|
|
22
|
+
* Shutdown runs once per module instance. Later calls are no-ops that resolve immediately,
|
|
23
|
+
* even if they pass `throwOnError` and an earlier call did not (or the first run failed).
|
|
24
|
+
*/
|
|
25
|
+
export const closeValkeyClients = async (options) => {
|
|
19
26
|
if (shutdown)
|
|
20
27
|
return;
|
|
21
28
|
shutdown = true;
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
const errors = [];
|
|
30
|
+
const report = (results) => {
|
|
31
|
+
for (const result of results) {
|
|
32
|
+
if (result.status === "rejected") {
|
|
33
|
+
const error = toError(result.reason);
|
|
34
|
+
errors.push(error);
|
|
35
|
+
handleValkeyError(error);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
report(await Promise.allSettled([...dynamicConfigs].map(safeClose)));
|
|
40
|
+
report(await Promise.allSettled([
|
|
28
41
|
...Array.from(urlsToClients.values(), safeClose),
|
|
29
42
|
closeDynamicConfigValkeySubscriptionClient(),
|
|
30
|
-
]);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
handleValkeyError(toError(result.reason));
|
|
43
|
+
]));
|
|
44
|
+
if (options?.throwOnError && errors.length > 0) {
|
|
45
|
+
throw new AggregateError(errors, "Valkey shutdown failed");
|
|
34
46
|
}
|
|
35
47
|
};
|
|
36
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Same as `closeValkeyClients()` but takes no arguments, so it is safe to pass directly as a
|
|
50
|
+
* signal or lifecycle callback that supplies its own arguments. Never rejects on close failures.
|
|
51
|
+
*/
|
|
52
|
+
export const onGracefulShutdown = () => closeValkeyClients();
|
|
37
53
|
function toError(error) {
|
|
38
54
|
return error instanceof Error ? error : new Error(String(error));
|
|
39
55
|
}
|
package/dist/shutdown.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.mjs","sourceRoot":"","sources":["../shutdown.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,0CAA0C,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,IAAI,QAAQ,GAAG,KAAK,CAAC;AAErB;;;;GAIG;AACH,MAAM,SAAS,GAAG,CAAC,GAA6B,EAAE,EAAE;IAClD,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"shutdown.mjs","sourceRoot":"","sources":["../shutdown.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,0CAA0C,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,IAAI,QAAQ,GAAG,KAAK,CAAC;AAErB;;;;GAIG;AACH,MAAM,SAAS,GAAG,CAAC,GAA6B,EAAE,EAAE;IAClD,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC,CAAC;AAUF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,OAAmC,EAAiB,EAAE;IAC7F,IAAI,QAAQ;QAAE,OAAO;IACrB,QAAQ,GAAG,IAAI,CAAC;IAEhB,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,CAAC,OAAwC,EAAE,EAAE;QAC1D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBACjC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACrC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACnB,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,CAAC,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,CACJ,MAAM,OAAO,CAAC,UAAU,CAAC;QACvB,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,SAAS,CAAC;QAChD,0CAA0C,EAAE;KAC7C,CAAC,CACH,CAAC;IAEF,IAAI,OAAO,EAAE,YAAY,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,cAAc,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAkB,EAAE,CAAC,kBAAkB,EAAE,CAAC;AAE5E,SAAS,OAAO,CAAC,KAAc;IAC7B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
package/docs/api.md
CHANGED
|
@@ -17,6 +17,7 @@ This is the entry point for the public `valkyries` API. Detailed API documentati
|
|
|
17
17
|
- [Events and metrics](events-and-metrics.md) - `valkeyEvents`, cache metrics, event payloads, and error handling hooks.
|
|
18
18
|
- [Lua scripts](lua-scripts.md) - packaged Lua scripts and script-loading helpers.
|
|
19
19
|
- [Configuration](configuration.md) - environment variables and connection behavior.
|
|
20
|
+
- [Diagnostics](diagnostics.md) - `INFO memory` parsing and cancellable prefix key counting.
|
|
20
21
|
- [Utilities](utilities.md) - key and Valkey result normalization helpers.
|
|
21
22
|
|
|
22
23
|
## Package Entry Points
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
reserveIdempotencyKey,
|
|
32
33
|
closeValkeyClients,
|
|
33
34
|
scanAndUnlinkKeys,
|
|
35
|
+
scanAndUnlinkKeyPrefixes,
|
|
34
36
|
expireKeysWithNoExpiry,
|
|
35
37
|
} from "valkyries";
|
|
36
38
|
```
|
package/docs/clients.md
CHANGED
|
@@ -10,7 +10,7 @@ Connections are deduplicated by URL, read preference, and lazy-connect setting t
|
|
|
10
10
|
|
|
11
11
|
For libraries, tests, and multi-tenant services, pass a `GlideClient` directly to `ValkeyCache`, `ValkeyBloomFilter`, `DynamicConfig`, or `RateLimiter`.
|
|
12
12
|
|
|
13
|
-
Use `closeValkeyClients()` or `onGracefulShutdown()` to close package-managed clients and the dynamic-config pub/sub connection.
|
|
13
|
+
Use `closeValkeyClients()` (or the argument-free `onGracefulShutdown()`) to close package-managed clients and the dynamic-config pub/sub connection.
|
|
14
14
|
|
|
15
15
|
## Default Clients
|
|
16
16
|
|
|
@@ -102,8 +102,20 @@ Registers or removes handlers that receive dynamic-config pub/sub messages.
|
|
|
102
102
|
## Shutdown
|
|
103
103
|
|
|
104
104
|
```ts
|
|
105
|
-
closeValkeyClients(): Promise<void>
|
|
105
|
+
closeValkeyClients(options?: { throwOnError?: boolean }): Promise<void>
|
|
106
106
|
onGracefulShutdown(): Promise<void>
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
`closeValkeyClients()` closes all live dynamic configs first, then package-managed clients and the dynamic-config subscription client. Closing `dynamicConfigs` yourself before calling it is unnecessary. `onGracefulShutdown()` runs the same sequence but takes no arguments, so it is safe to pass directly as a signal or lifecycle callback.
|
|
110
|
+
|
|
111
|
+
By default, close failures are reported to `handleValkeyError` and the promise always resolves. With `throwOnError: true`, failures are still reported to `handleValkeyError`; after every close has settled, the promise rejects with `new AggregateError(errors, "Valkey shutdown failed")`.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
try {
|
|
115
|
+
await closeValkeyClients({ throwOnError: true });
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error instanceof AggregateError) process.exitCode = 1;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Shutdown runs once per process. Repeated calls are no-ops that resolve immediately, including a later call that passes `throwOnError: true` after an earlier call without it: it neither re-closes anything nor reports the earlier failures.
|
package/docs/configuration.md
CHANGED
|
@@ -14,3 +14,7 @@ Redis-style URLs are supported:
|
|
|
14
14
|
- `rediss://user:password@example.com:6380`
|
|
15
15
|
|
|
16
16
|
The cache client uses `readFrom: "preferReplica"`. Rate limiter reads use `primary` to avoid replica lag. Dynamic config commands use `preferReplica`; pub/sub uses a dedicated eager connection.
|
|
17
|
+
|
|
18
|
+
## Saturation retries
|
|
19
|
+
|
|
20
|
+
`VALKEY_INFLIGHT_RETRY_ATTEMPTS` (default `3`) and `VALKEY_INFLIGHT_RETRY_DELAY_MS` (default `1000`) set how internal command boundaries retry commands the client rejects locally with `Reached maximum inflight requests`. The exported `retrySaturationError(fn, options?)` uses the same values when `options.attempts` / `options.delayMs` are omitted; explicit options take precedence. Delays are jittered in `[delayMs, delayMs * 5]`.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Diagnostics
|
|
2
|
+
|
|
3
|
+
Read-only helpers for inspecting memory and keyspace usage. Choosing a client, checking deployment
|
|
4
|
+
topology, and naming groups are left to the caller.
|
|
5
|
+
|
|
6
|
+
## `parseValkeyMemoryInfo(info)`
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
parseValkeyMemoryInfo(info: string): ValkeyMemorySummary
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Parses `INFO memory` text (`key:value` lines, `#` section headers, LF or CRLF). Throws when
|
|
13
|
+
`used_memory`, `used_memory_rss`, `used_memory_peak`, `maxmemory`, or `maxmemory_policy` is missing,
|
|
14
|
+
or when an integer field is not a non-negative safe integer. `usedMemoryDatasetBytes`,
|
|
15
|
+
`lazyfreePendingObjects`, and `memFragmentationRatio` are `null` when absent, but still throw when
|
|
16
|
+
present and malformed.
|
|
17
|
+
|
|
18
|
+
## `getValkeyMemoryInfo(client, options?)`
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
getValkeyMemoryInfo(
|
|
22
|
+
client: Pick<GlideClient, "info">,
|
|
23
|
+
options?: { signal?: AbortSignal },
|
|
24
|
+
): Promise<ValkeyMemorySummary>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Runs `INFO memory` and parses it. The signal is checked before and after the command.
|
|
28
|
+
|
|
29
|
+
## `countKeysByPrefix(client, prefixes, options?)`
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { countKeysByPrefix } from "valkyries";
|
|
33
|
+
|
|
34
|
+
const counts = await countKeysByPrefix(
|
|
35
|
+
client,
|
|
36
|
+
{ cache: ["cache:"], sessions: ["session:", "sess:"] },
|
|
37
|
+
{ signal, count: 500 },
|
|
38
|
+
);
|
|
39
|
+
// { cache: 12, sessions: 3, unclassified: 40 }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Walks the keyspace with bounded `SCAN` pages (`count` defaults to 500) and checks `signal` before and
|
|
43
|
+
after each page, including the last. Keys matching no prefix are counted as `unclassified`. SCAN is
|
|
44
|
+
non-snapshot, so counts can include duplicates or miss keys written during the walk. The registry is
|
|
45
|
+
validated first.
|
|
46
|
+
|
|
47
|
+
## `validateKeyPrefixRegistry(prefixes)`
|
|
48
|
+
|
|
49
|
+
Throws for empty prefixes, a group named `unclassified`, and prefixes that overlap across groups
|
|
50
|
+
(identical, or one a prefix of another), so every key belongs to at most one group.
|
package/docs/utilities.md
CHANGED
|
@@ -24,6 +24,25 @@ from the keys that exist when the call returns. The optional `signal` is checked
|
|
|
24
24
|
every SCAN and UNLINK boundary; its exact abort reason is rethrown without reporting it to the
|
|
25
25
|
configured Valkey error handler.
|
|
26
26
|
|
|
27
|
+
## `scanAndUnlinkKeyPrefixes(client, prefixes, options)`
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
scanAndUnlinkKeyPrefixes(
|
|
31
|
+
client: ScanAndUnlinkKeysClient,
|
|
32
|
+
prefixes: readonly string[],
|
|
33
|
+
options?: ScanAndUnlinkKeysOptions, // { signal?, matches? }
|
|
34
|
+
): Promise<{ unlinkedKeys: number }>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Runs `scanAndUnlinkKeys` serially for each distinct prefix, glob-escaping `*`, `?`, `[`, `]`, and
|
|
38
|
+
`\` and appending `*`, so a prefix matches only keys that literally start with it. An empty or
|
|
39
|
+
non-string prefix throws a `TypeError` before any scan starts.
|
|
40
|
+
|
|
41
|
+
A failing prefix does not stop later prefixes. One failure is rethrown as-is; two or more are
|
|
42
|
+
combined into an `AggregateError` whose message and `unlinkedKeys` property report the keys already
|
|
43
|
+
removed. Failures reach the configured Valkey error handler once, from `scanAndUnlinkKeys`. The
|
|
44
|
+
`signal` is checked before each prefix, and its exact abort reason is rethrown immediately.
|
|
45
|
+
|
|
27
46
|
## `expireKeysWithNoExpiry(client, options)`
|
|
28
47
|
|
|
29
48
|
```ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "valkyries",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Valkey utilities for caches, bloom filters, dynamic configuration, and rate limiting.",
|
|
5
5
|
"homepage": "https://github.com/jonathanong/valkyries#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@types/node": "^26.1.0",
|
|
71
71
|
"@vitest/coverage-v8": "^5.0.0",
|
|
72
72
|
"github-actionlint": "^1.7.12",
|
|
73
|
-
"oxfmt": "^0.
|
|
73
|
+
"oxfmt": "^0.71.0",
|
|
74
74
|
"oxlint": "^1.65.0",
|
|
75
75
|
"oxlint-tsgolint": "^7.0.2001",
|
|
76
76
|
"tsx": "^4.22.1",
|