slonik-interceptor-query-cache 48.12.3 → 48.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +16 -20
- package/dist/Logger.js +2 -2
- package/dist/factories/createQueryCacheInterceptor.d.ts +1 -1
- package/dist/factories/createQueryCacheInterceptor.d.ts.map +1 -1
- package/dist/factories/createQueryCacheInterceptor.js +9 -11
- package/dist/factories/createQueryCacheInterceptor.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/utilities/extractCacheAttributes.d.ts +1 -1
- package/dist/utilities/extractCacheAttributes.d.ts.map +1 -1
- package/dist/utilities/extractCacheAttributes.js +23 -23
- package/dist/utilities/extractCacheAttributes.js.map +1 -1
- package/dist/utilities/extractCacheAttributes.test.js +41 -41
- package/dist/utilities/extractCacheAttributes.test.js.map +1 -1
- package/dist/utilities/normalizeCacheAttributes.d.ts +2 -2
- package/dist/utilities/normalizeCacheAttributes.js +2 -2
- package/dist/utilities/normalizeCacheAttributes.test.js +7 -7
- package/package.json +32 -35
- package/src/Logger.ts +2 -2
- package/src/factories/createQueryCacheInterceptor.ts +13 -25
- package/src/index.ts +1 -1
- package/src/utilities/extractCacheAttributes.test.ts +47 -59
- package/src/utilities/extractCacheAttributes.ts +25 -27
- package/src/utilities/normalizeCacheAttributes.test.ts +7 -7
- package/src/utilities/normalizeCacheAttributes.ts +4 -4
- package/tsconfig.json +3 -7
- package/eslint.config.js +0 -4
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -14,23 +14,23 @@ Which queries are cached is controlled using cache attributes. Cache attributes
|
|
|
14
14
|
|
|
15
15
|
## Behavior
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
- Does not cache queries inside of a transaction.
|
|
18
18
|
|
|
19
19
|
## Cache attributes
|
|
20
20
|
|
|
21
21
|
#### `@cache-ttl`
|
|
22
22
|
|
|
23
|
-
|Required|Format|Default|
|
|
24
|
-
|
|
25
|
-
|Yes
|
|
23
|
+
| Required | Format | Default |
|
|
24
|
+
| -------- | -------- | ------- |
|
|
25
|
+
| Yes | `/^d+$/` | N/A |
|
|
26
26
|
|
|
27
27
|
Number (in seconds) to cache the query for.
|
|
28
28
|
|
|
29
29
|
#### `@cache-key`
|
|
30
30
|
|
|
31
|
-
|Required|Format|Default|
|
|
32
|
-
|
|
33
|
-
|No
|
|
31
|
+
| Required | Format | Default |
|
|
32
|
+
| -------- | ----------------------- | ---------------------- |
|
|
33
|
+
| No | `/^[$A-Za-z0-9\-_:]+$/` | `$bodyHash:$valueHash` |
|
|
34
34
|
|
|
35
35
|
Cache key that uniquely identifies the query.
|
|
36
36
|
|
|
@@ -40,9 +40,9 @@ If present, `$valueHash` is substituted with the hash of the parameter values.
|
|
|
40
40
|
|
|
41
41
|
#### `@cache-discard-empty`
|
|
42
42
|
|
|
43
|
-
|Required|Format|Default|
|
|
44
|
-
|
|
45
|
-
|No
|
|
43
|
+
| Required | Format | Default |
|
|
44
|
+
| -------- | ------------------- | ------- |
|
|
45
|
+
| No | `/^(false\|true)$/` | `false` |
|
|
46
46
|
|
|
47
47
|
If set to `true`, then `storage.set` is not invoked when query produces no results.
|
|
48
48
|
|
|
@@ -51,13 +51,9 @@ If set to `true`, then `storage.set` is not invoked when query produces no resul
|
|
|
51
51
|
This example shows how to create a compatible storage service using [`node-cache`](https://www.npmjs.com/package/node-cache).
|
|
52
52
|
|
|
53
53
|
```js
|
|
54
|
-
import NodeCache from
|
|
55
|
-
import {
|
|
56
|
-
|
|
57
|
-
} from 'slonik';
|
|
58
|
-
import {
|
|
59
|
-
createQueryCacheInterceptor
|
|
60
|
-
} from 'slonik-interceptor-query-cache';
|
|
54
|
+
import NodeCache from "node-cache";
|
|
55
|
+
import { createPool } from "slonik";
|
|
56
|
+
import { createQueryCacheInterceptor } from "slonik-interceptor-query-cache";
|
|
61
57
|
|
|
62
58
|
const nodeCache = new NodeCache({
|
|
63
59
|
checkperiod: 60,
|
|
@@ -65,7 +61,7 @@ const nodeCache = new NodeCache({
|
|
|
65
61
|
useClones: false,
|
|
66
62
|
});
|
|
67
63
|
|
|
68
|
-
const pool = await createPool(
|
|
64
|
+
const pool = await createPool("postgres://", {
|
|
69
65
|
interceptors: [
|
|
70
66
|
createQueryCacheInterceptor({
|
|
71
67
|
storage: {
|
|
@@ -78,7 +74,7 @@ const pool = await createPool('postgres://', {
|
|
|
78
74
|
},
|
|
79
75
|
},
|
|
80
76
|
}),
|
|
81
|
-
]
|
|
77
|
+
],
|
|
82
78
|
});
|
|
83
79
|
```
|
|
84
80
|
|
|
@@ -131,4 +127,4 @@ await connection.any(sql`
|
|
|
131
127
|
WHERE
|
|
132
128
|
code_alpha_2 = ${countryCode}
|
|
133
129
|
`);
|
|
134
|
-
```
|
|
130
|
+
```
|
package/dist/Logger.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createQueryCacheInterceptor.d.ts","sourceRoot":"","sources":["../../src/factories/createQueryCacheInterceptor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAM9E,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAMF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAQF,KAAK,OAAO,GAAG;IACb,GAAG,EAAE,CACH,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,KAC7B,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;IACjD,GAAG,EAAE,CACH,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,WAAW,CAAC,cAAc,CAAC,KACrC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,oBAAoB,kBAAkB,KACrC,
|
|
1
|
+
{"version":3,"file":"createQueryCacheInterceptor.d.ts","sourceRoot":"","sources":["../../src/factories/createQueryCacheInterceptor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAM9E,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAMF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAQF,KAAK,OAAO,GAAG;IACb,GAAG,EAAE,CACH,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,KAC7B,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;IACjD,GAAG,EAAE,CACH,KAAK,EAAE,KAAK,EACZ,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,WAAW,CAAC,cAAc,CAAC,KACrC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,oBAAoB,kBAAkB,KACrC,WAsEF,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Logger } from
|
|
2
|
-
import { extractCacheAttributes } from
|
|
3
|
-
import { normalizeCacheAttributes } from
|
|
1
|
+
import { Logger } from "../Logger.js";
|
|
2
|
+
import { extractCacheAttributes } from "../utilities/extractCacheAttributes.js";
|
|
3
|
+
import { normalizeCacheAttributes } from "../utilities/normalizeCacheAttributes.js";
|
|
4
4
|
const log = Logger.child({
|
|
5
|
-
namespace:
|
|
5
|
+
namespace: "createQueryCacheInterceptor",
|
|
6
6
|
});
|
|
7
7
|
export const createQueryCacheInterceptor = (configurationInput) => {
|
|
8
8
|
const configuration = {
|
|
@@ -13,8 +13,7 @@ export const createQueryCacheInterceptor = (configurationInput) => {
|
|
|
13
13
|
if (context.transactionId) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
|
-
const cacheAttributes = context.sandbox.cache
|
|
17
|
-
?.cacheAttributes;
|
|
16
|
+
const cacheAttributes = context.sandbox.cache?.cacheAttributes;
|
|
18
17
|
if (!cacheAttributes) {
|
|
19
18
|
return null;
|
|
20
19
|
}
|
|
@@ -22,7 +21,7 @@ export const createQueryCacheInterceptor = (configurationInput) => {
|
|
|
22
21
|
if (maybeResult) {
|
|
23
22
|
log.debug({
|
|
24
23
|
queryId: context.queryId,
|
|
25
|
-
},
|
|
24
|
+
}, "query is served from cache");
|
|
26
25
|
return maybeResult;
|
|
27
26
|
}
|
|
28
27
|
return null;
|
|
@@ -31,11 +30,10 @@ export const createQueryCacheInterceptor = (configurationInput) => {
|
|
|
31
30
|
if (context.transactionId) {
|
|
32
31
|
return null;
|
|
33
32
|
}
|
|
34
|
-
const cacheAttributes = context.sandbox.cache
|
|
35
|
-
?.cacheAttributes;
|
|
33
|
+
const cacheAttributes = context.sandbox.cache?.cacheAttributes;
|
|
36
34
|
if (cacheAttributes) {
|
|
37
35
|
if (cacheAttributes.discardEmpty && result.rowCount === 0) {
|
|
38
|
-
log.debug(
|
|
36
|
+
log.debug("@cache-discard-empty is set and the query result is empty; not caching");
|
|
39
37
|
}
|
|
40
38
|
else {
|
|
41
39
|
await configuration.storage.set(query, cacheAttributes, result);
|
|
@@ -57,7 +55,7 @@ export const createQueryCacheInterceptor = (configurationInput) => {
|
|
|
57
55
|
};
|
|
58
56
|
return null;
|
|
59
57
|
},
|
|
60
|
-
name:
|
|
58
|
+
name: "slonik-interceptor-query-cache",
|
|
61
59
|
};
|
|
62
60
|
};
|
|
63
61
|
//# sourceMappingURL=createQueryCacheInterceptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createQueryCacheInterceptor.js","sourceRoot":"","sources":["../../src/factories/createQueryCacheInterceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAGpF,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC;IACvB,SAAS,EAAE,6BAA6B;CACzC,CAAC,CAAC;AAkCH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,kBAAsC,EACzB,EAAE;IACf,MAAM,aAAa,GAAkB;QACnC,GAAG,kBAAkB;KACtB,CAAC;IAEF,OAAO;QACL,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC7C,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC1B,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,eAAe,GAAI,OAAO,CAAC,OAAmB,CAAC,KAAK
|
|
1
|
+
{"version":3,"file":"createQueryCacheInterceptor.js","sourceRoot":"","sources":["../../src/factories/createQueryCacheInterceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAGpF,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC;IACvB,SAAS,EAAE,6BAA6B;CACzC,CAAC,CAAC;AAkCH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,kBAAsC,EACzB,EAAE;IACf,MAAM,aAAa,GAAkB;QACnC,GAAG,kBAAkB;KACtB,CAAC;IAEF,OAAO;QACL,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC7C,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC1B,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,eAAe,GAAI,OAAO,CAAC,OAAmB,CAAC,KAAK,EAAE,eAAe,CAAC;YAE5E,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;YAE5E,IAAI,WAAW,EAAE,CAAC;gBAChB,GAAG,CAAC,KAAK,CACP;oBACE,OAAO,EAAE,OAAO,CAAC,OAAO;iBACzB,EACD,4BAA4B,CAC7B,CAAC;gBAEF,OAAO,WAAW,CAAC;YACrB,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QACD,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAClD,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC1B,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,eAAe,GAAI,OAAO,CAAC,OAAmB,CAAC,KAAK,EAAE,eAAe,CAAC;YAE5E,IAAI,eAAe,EAAE,CAAC;gBACpB,IAAI,eAAe,CAAC,YAAY,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;oBAC1D,GAAG,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAC;gBACtF,CAAC;qBAAM,CAAC;oBACN,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QACD,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAC7C,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC1B,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,wBAAwB,GAAG,sBAAsB,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAEjF,IAAI,CAAC,wBAAwB,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,eAAe,GAAG,wBAAwB,CAAC,wBAAwB,CAAC,CAAC;YAE3E,OAAO,CAAC,OAAO,CAAC,KAAK,GAAG;gBACtB,eAAe;aAChB,CAAC;YAEF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,EAAE,gCAAgC;KACvC,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createQueryCacheInterceptor } from
|
|
1
|
+
export { createQueryCacheInterceptor } from "./factories/createQueryCacheInterceptor.js";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createQueryCacheInterceptor } from
|
|
1
|
+
export { createQueryCacheInterceptor } from "./factories/createQueryCacheInterceptor.js";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractCacheAttributes.d.ts","sourceRoot":"","sources":["../../src/utilities/extractCacheAttributes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"extractCacheAttributes.d.ts","sourceRoot":"","sources":["../../src/utilities/extractCacheAttributes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,QAAQ,CAAC;AAwEvD,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAQF,eAAO,MAAM,sBAAsB,GACjC,SAAS,MAAM,EACf,QAAQ,SAAS,wBAAwB,EAAE,KAC1C,wBAAwB,GAAG,IAwC7B,CAAC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { xxh64 } from
|
|
2
|
-
import { lru } from
|
|
1
|
+
import { xxh64 } from "@node-rs/xxhash";
|
|
2
|
+
import { lru } from "tiny-lru";
|
|
3
3
|
/**
|
|
4
4
|
* LRU cache for body hashes.
|
|
5
5
|
* Avoids recomputing the same body hash for repeated queries.
|
|
6
6
|
*/
|
|
7
7
|
const bodyHashCache = lru(1_000);
|
|
8
8
|
const hash = (subject) => {
|
|
9
|
-
return xxh64(subject).toString(16).padStart(16,
|
|
9
|
+
return xxh64(subject).toString(16).padStart(16, "0");
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
12
|
* Get or compute body hash with LRU caching.
|
|
@@ -27,30 +27,30 @@ const getBodyHash = (sql, strippedSql) => {
|
|
|
27
27
|
*/
|
|
28
28
|
const serializeValues = (values) => {
|
|
29
29
|
if (values.length === 0) {
|
|
30
|
-
return
|
|
30
|
+
return "[]";
|
|
31
31
|
}
|
|
32
32
|
// For small arrays, direct join is faster
|
|
33
33
|
if (values.length <= 16) {
|
|
34
|
-
let result =
|
|
34
|
+
let result = "";
|
|
35
35
|
for (const value of values) {
|
|
36
36
|
if (result) {
|
|
37
|
-
result +=
|
|
37
|
+
result += "\u0000";
|
|
38
38
|
}
|
|
39
39
|
if (value === null) {
|
|
40
40
|
// null marker
|
|
41
|
-
result +=
|
|
41
|
+
result += "\u0001";
|
|
42
42
|
}
|
|
43
|
-
else if (typeof value ===
|
|
44
|
-
result +=
|
|
43
|
+
else if (typeof value === "string") {
|
|
44
|
+
result += "s" + value;
|
|
45
45
|
}
|
|
46
|
-
else if (typeof value ===
|
|
47
|
-
result +=
|
|
46
|
+
else if (typeof value === "number") {
|
|
47
|
+
result += "n" + value;
|
|
48
48
|
}
|
|
49
|
-
else if (typeof value ===
|
|
50
|
-
result += value ?
|
|
49
|
+
else if (typeof value === "boolean") {
|
|
50
|
+
result += value ? "T" : "F";
|
|
51
51
|
}
|
|
52
|
-
else if (typeof value ===
|
|
53
|
-
result +=
|
|
52
|
+
else if (typeof value === "bigint") {
|
|
53
|
+
result += "b" + value;
|
|
54
54
|
}
|
|
55
55
|
else {
|
|
56
56
|
// Fallback for Buffer or other types
|
|
@@ -68,7 +68,7 @@ const CommentRegex = /^\s*--.*$/gmu;
|
|
|
68
68
|
// TODO throw an error if an unknown attribute is used
|
|
69
69
|
export const extractCacheAttributes = (subject, values) => {
|
|
70
70
|
// Fast early bail-out: skip all work for non-cached queries
|
|
71
|
-
if (!subject.includes(
|
|
71
|
+
if (!subject.includes("@cache-ttl")) {
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
74
|
const ttl = subject.match(TtlRegex)?.[1];
|
|
@@ -76,16 +76,16 @@ export const extractCacheAttributes = (subject, values) => {
|
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
78
|
// Extract key template first to determine which hashes we need
|
|
79
|
-
const key = subject.match(KeyRegex)?.[1] ??
|
|
80
|
-
const discardEmpty = subject.match(DiscardEmptyRegex)?.[1] ===
|
|
81
|
-
const needsBodyHash = key.includes(
|
|
82
|
-
const needsValueHash = key.includes(
|
|
79
|
+
const key = subject.match(KeyRegex)?.[1] ?? "query:$bodyHash:$valueHash";
|
|
80
|
+
const discardEmpty = subject.match(DiscardEmptyRegex)?.[1] === "true";
|
|
81
|
+
const needsBodyHash = key.includes("$bodyHash");
|
|
82
|
+
const needsValueHash = key.includes("$valueHash");
|
|
83
83
|
// Lazy computation: only compute hashes if they're used in the key
|
|
84
|
-
let bodyHash =
|
|
85
|
-
let valueHash =
|
|
84
|
+
let bodyHash = "";
|
|
85
|
+
let valueHash = "";
|
|
86
86
|
if (needsBodyHash) {
|
|
87
87
|
// Strip comments and compute hash (with memoization)
|
|
88
|
-
const strippedSql = subject.replaceAll(CommentRegex,
|
|
88
|
+
const strippedSql = subject.replaceAll(CommentRegex, "");
|
|
89
89
|
bodyHash = getBodyHash(subject, strippedSql);
|
|
90
90
|
}
|
|
91
91
|
if (needsValueHash) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractCacheAttributes.js","sourceRoot":"","sources":["../../src/utilities/extractCacheAttributes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAExC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B;;;GAGG;AACH,MAAM,aAAa,GAAG,GAAG,CAAS,KAAK,CAAC,CAAC;AAEzC,MAAM,IAAI,GAAG,CAAC,OAAe,EAAU,EAAE;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,GAAG,CAAC,GAAW,EAAE,WAAmB,EAAU,EAAE;IAC/D,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAEtC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;IAEnC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAEjC,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,eAAe,GAAG,
|
|
1
|
+
{"version":3,"file":"extractCacheAttributes.js","sourceRoot":"","sources":["../../src/utilities/extractCacheAttributes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
import { extractCacheAttributes } from
|
|
2
|
-
import test from
|
|
3
|
-
test(
|
|
4
|
-
t.is(extractCacheAttributes(
|
|
1
|
+
import { extractCacheAttributes } from "./extractCacheAttributes.js";
|
|
2
|
+
import test from "ava";
|
|
3
|
+
test("returns null when query does not contain cache attributes", (t) => {
|
|
4
|
+
t.is(extractCacheAttributes("", []), null);
|
|
5
5
|
});
|
|
6
|
-
test(
|
|
7
|
-
t.deepEqual(extractCacheAttributes(
|
|
8
|
-
bodyHash:
|
|
6
|
+
test("extracts @cache-ttl", (t) => {
|
|
7
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60", []), {
|
|
8
|
+
bodyHash: "ef46db3751d8e999",
|
|
9
9
|
discardEmpty: false,
|
|
10
|
-
key:
|
|
10
|
+
key: "query:$bodyHash:$valueHash",
|
|
11
11
|
ttl: 60,
|
|
12
|
-
valueHash:
|
|
12
|
+
valueHash: "ccab0b28617f1f56",
|
|
13
13
|
});
|
|
14
14
|
});
|
|
15
|
-
test(
|
|
16
|
-
t.deepEqual(extractCacheAttributes(
|
|
17
|
-
bodyHash:
|
|
15
|
+
test("extracts @cache-discard-empty", (t) => {
|
|
16
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\n-- @cache-discard-empty false", []), {
|
|
17
|
+
bodyHash: "cafc7706cee4572b",
|
|
18
18
|
discardEmpty: false,
|
|
19
|
-
key:
|
|
19
|
+
key: "query:$bodyHash:$valueHash",
|
|
20
20
|
ttl: 60,
|
|
21
|
-
valueHash:
|
|
21
|
+
valueHash: "ccab0b28617f1f56",
|
|
22
22
|
});
|
|
23
|
-
t.deepEqual(extractCacheAttributes(
|
|
24
|
-
bodyHash:
|
|
23
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\n-- @cache-discard-empty true", []), {
|
|
24
|
+
bodyHash: "cafc7706cee4572b",
|
|
25
25
|
discardEmpty: true,
|
|
26
|
-
key:
|
|
26
|
+
key: "query:$bodyHash:$valueHash",
|
|
27
27
|
ttl: 60,
|
|
28
|
-
valueHash:
|
|
28
|
+
valueHash: "ccab0b28617f1f56",
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
|
-
test(
|
|
32
|
-
t.deepEqual(extractCacheAttributes(
|
|
33
|
-
bodyHash:
|
|
31
|
+
test("computes the parameter value hash", (t) => {
|
|
32
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60", [1]), {
|
|
33
|
+
bodyHash: "ef46db3751d8e999",
|
|
34
34
|
discardEmpty: false,
|
|
35
|
-
key:
|
|
35
|
+
key: "query:$bodyHash:$valueHash",
|
|
36
36
|
ttl: 60,
|
|
37
|
-
valueHash:
|
|
37
|
+
valueHash: "51ce9f3ef4b004a7",
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
|
-
test(
|
|
41
|
-
t.deepEqual(extractCacheAttributes(
|
|
42
|
-
bodyHash:
|
|
40
|
+
test("computes the body hash; white spaces do not affect the body hash", (t) => {
|
|
41
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\nSELECT 1", []), {
|
|
42
|
+
bodyHash: "6d1c807e68464b3b",
|
|
43
43
|
discardEmpty: false,
|
|
44
|
-
key:
|
|
44
|
+
key: "query:$bodyHash:$valueHash",
|
|
45
45
|
ttl: 60,
|
|
46
|
-
valueHash:
|
|
46
|
+
valueHash: "ccab0b28617f1f56",
|
|
47
47
|
});
|
|
48
|
-
t.deepEqual(extractCacheAttributes(
|
|
49
|
-
bodyHash:
|
|
48
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\n\nSELECT 1", []), {
|
|
49
|
+
bodyHash: "fa849a274ca52058",
|
|
50
50
|
discardEmpty: false,
|
|
51
|
-
key:
|
|
51
|
+
key: "query:$bodyHash:$valueHash",
|
|
52
52
|
ttl: 60,
|
|
53
|
-
valueHash:
|
|
53
|
+
valueHash: "ccab0b28617f1f56",
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
|
-
test(
|
|
57
|
-
t.deepEqual(extractCacheAttributes(
|
|
58
|
-
bodyHash:
|
|
56
|
+
test("computes the body hash; comments do not affect the body hash", (t) => {
|
|
57
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\nSELECT 1", []), {
|
|
58
|
+
bodyHash: "6d1c807e68464b3b",
|
|
59
59
|
discardEmpty: false,
|
|
60
|
-
key:
|
|
60
|
+
key: "query:$bodyHash:$valueHash",
|
|
61
61
|
ttl: 60,
|
|
62
|
-
valueHash:
|
|
62
|
+
valueHash: "ccab0b28617f1f56",
|
|
63
63
|
});
|
|
64
|
-
t.deepEqual(extractCacheAttributes(
|
|
65
|
-
bodyHash:
|
|
64
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 120\nSELECT 1", []), {
|
|
65
|
+
bodyHash: "6d1c807e68464b3b",
|
|
66
66
|
discardEmpty: false,
|
|
67
|
-
key:
|
|
67
|
+
key: "query:$bodyHash:$valueHash",
|
|
68
68
|
ttl: 120,
|
|
69
|
-
valueHash:
|
|
69
|
+
valueHash: "ccab0b28617f1f56",
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
//# sourceMappingURL=extractCacheAttributes.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractCacheAttributes.test.js","sourceRoot":"","sources":["../../src/utilities/extractCacheAttributes.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,IAAI,MAAM,KAAK,CAAC;AAEvB,IAAI,CAAC,2DAA2D,EAAE,CAAC,CAAC,EAAE,EAAE;IACtE,CAAC,CAAC,EAAE,CAAC,sBAAsB,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC,EAAE,EAAE;IAChC,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,kBAAkB,EAAE,EAAE,CAAC,EAAE;QAC1D,QAAQ,EAAE,kBAAkB;QAC5B,YAAY,EAAE,KAAK;QACnB,GAAG,EAAE,4BAA4B;QACjC,GAAG,EAAE,EAAE;QACP,SAAS,EAAE,kBAAkB;KAC9B,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+BAA+B,EAAE,CAAC,CAAC,EAAE,EAAE;IAC1C,CAAC,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"extractCacheAttributes.test.js","sourceRoot":"","sources":["../../src/utilities/extractCacheAttributes.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CacheAttributes } from
|
|
2
|
-
import type { ExtractedCacheAttributes } from
|
|
1
|
+
import type { CacheAttributes } from "../factories/createQueryCacheInterceptor.js";
|
|
2
|
+
import type { ExtractedCacheAttributes } from "./extractCacheAttributes.js";
|
|
3
3
|
export declare const normalizeCacheAttributes: (extractedCacheAttributes: ExtractedCacheAttributes) => CacheAttributes;
|
|
4
4
|
//# sourceMappingURL=normalizeCacheAttributes.d.ts.map
|
|
@@ -2,8 +2,8 @@ export const normalizeCacheAttributes = (extractedCacheAttributes) => {
|
|
|
2
2
|
return {
|
|
3
3
|
discardEmpty: extractedCacheAttributes.discardEmpty,
|
|
4
4
|
key: extractedCacheAttributes.key
|
|
5
|
-
.replaceAll(
|
|
6
|
-
.replaceAll(
|
|
5
|
+
.replaceAll("$bodyHash", extractedCacheAttributes.bodyHash)
|
|
6
|
+
.replaceAll("$valueHash", extractedCacheAttributes.valueHash),
|
|
7
7
|
ttl: extractedCacheAttributes.ttl,
|
|
8
8
|
};
|
|
9
9
|
};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { normalizeCacheAttributes } from
|
|
2
|
-
import test from
|
|
3
|
-
test(
|
|
1
|
+
import { normalizeCacheAttributes } from "./normalizeCacheAttributes.js";
|
|
2
|
+
import test from "ava";
|
|
3
|
+
test("replaces $bodyHash and $valueHash", (t) => {
|
|
4
4
|
t.deepEqual(normalizeCacheAttributes({
|
|
5
|
-
bodyHash:
|
|
5
|
+
bodyHash: "foo",
|
|
6
6
|
discardEmpty: false,
|
|
7
|
-
key:
|
|
7
|
+
key: "$bodyHash:$valueHash",
|
|
8
8
|
ttl: 60,
|
|
9
|
-
valueHash:
|
|
9
|
+
valueHash: "bar",
|
|
10
10
|
}), {
|
|
11
11
|
discardEmpty: false,
|
|
12
|
-
key:
|
|
12
|
+
key: "foo:bar",
|
|
13
13
|
ttl: 60,
|
|
14
14
|
});
|
|
15
15
|
});
|
package/package.json
CHANGED
|
@@ -1,59 +1,56 @@
|
|
|
1
1
|
{
|
|
2
|
+
"name": "slonik-interceptor-query-cache",
|
|
3
|
+
"version": "48.13.1",
|
|
4
|
+
"description": "Logs Slonik queries.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"interceptor",
|
|
7
|
+
"logging",
|
|
8
|
+
"postgresql"
|
|
9
|
+
],
|
|
10
|
+
"license": "BSD-3-Clause",
|
|
2
11
|
"author": {
|
|
3
|
-
"email": "gajus@gajus.com",
|
|
4
12
|
"name": "Gajus Kuizinas",
|
|
13
|
+
"email": "gajus@gajus.com",
|
|
5
14
|
"url": "http://gajus.com"
|
|
6
15
|
},
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
],
|
|
11
|
-
"files": [
|
|
12
|
-
"src/**/*.test.ts"
|
|
13
|
-
],
|
|
14
|
-
"nodeArguments": [
|
|
15
|
-
"--import=tsimp"
|
|
16
|
-
]
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/gajus/slonik"
|
|
17
19
|
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
18
23
|
"dependencies": {
|
|
19
24
|
"@node-rs/xxhash": "^1.7.6",
|
|
20
25
|
"roarr": "^7.21.4",
|
|
21
26
|
"tiny-lru": "^11.2.11"
|
|
22
27
|
},
|
|
23
|
-
"description": "Logs Slonik queries.",
|
|
24
28
|
"devDependencies": {
|
|
25
29
|
"ava": "^6.4.1",
|
|
26
|
-
"
|
|
27
|
-
"slonik": "^48.12.3",
|
|
30
|
+
"slonik": "^48.13.1",
|
|
28
31
|
"tsimp": "^2.0.12",
|
|
29
|
-
"typescript": "^5.9.3"
|
|
30
|
-
"typescript-eslint": "^8.55.0",
|
|
31
|
-
"@slonik/eslint-config": "^48.12.3"
|
|
32
|
-
},
|
|
33
|
-
"engines": {
|
|
34
|
-
"node": ">=24"
|
|
32
|
+
"typescript": "^5.9.3"
|
|
35
33
|
},
|
|
36
|
-
"keywords": [
|
|
37
|
-
"postgresql",
|
|
38
|
-
"interceptor",
|
|
39
|
-
"logging"
|
|
40
|
-
],
|
|
41
|
-
"license": "BSD-3-Clause",
|
|
42
|
-
"main": "./dist/index.js",
|
|
43
|
-
"name": "slonik-interceptor-query-cache",
|
|
44
34
|
"peerDependencies": {
|
|
45
35
|
"slonik": ">=45.0.0"
|
|
46
36
|
},
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
37
|
+
"ava": {
|
|
38
|
+
"extensions": [
|
|
39
|
+
"ts"
|
|
40
|
+
],
|
|
41
|
+
"files": [
|
|
42
|
+
"src/**/*.test.ts"
|
|
43
|
+
],
|
|
44
|
+
"nodeArguments": [
|
|
45
|
+
"--import=tsimp"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=24"
|
|
50
50
|
},
|
|
51
|
-
"type": "module",
|
|
52
|
-
"types": "./dist/index.d.ts",
|
|
53
|
-
"version": "48.12.3",
|
|
54
51
|
"scripts": {
|
|
55
52
|
"build": "rm -fr ./dist && tsc",
|
|
56
|
-
"lint": "
|
|
53
|
+
"lint": "tsc --noEmit",
|
|
57
54
|
"test": "ava --verbose --serial"
|
|
58
55
|
}
|
|
59
56
|
}
|
package/src/Logger.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Logger } from
|
|
2
|
-
import { extractCacheAttributes } from
|
|
3
|
-
import { normalizeCacheAttributes } from
|
|
4
|
-
import type { Interceptor, Query, QueryResult, QueryResultRow } from
|
|
1
|
+
import { Logger } from "../Logger.js";
|
|
2
|
+
import { extractCacheAttributes } from "../utilities/extractCacheAttributes.js";
|
|
3
|
+
import { normalizeCacheAttributes } from "../utilities/normalizeCacheAttributes.js";
|
|
4
|
+
import type { Interceptor, Query, QueryResult, QueryResultRow } from "slonik";
|
|
5
5
|
|
|
6
6
|
const log = Logger.child({
|
|
7
|
-
namespace:
|
|
7
|
+
namespace: "createQueryCacheInterceptor",
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
export type CacheAttributes = {
|
|
@@ -52,24 +52,20 @@ export const createQueryCacheInterceptor = (
|
|
|
52
52
|
return null;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
const cacheAttributes = (context.sandbox as Sandbox).cache
|
|
56
|
-
?.cacheAttributes;
|
|
55
|
+
const cacheAttributes = (context.sandbox as Sandbox).cache?.cacheAttributes;
|
|
57
56
|
|
|
58
57
|
if (!cacheAttributes) {
|
|
59
58
|
return null;
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
const maybeResult = await configuration.storage.get(
|
|
63
|
-
query,
|
|
64
|
-
cacheAttributes,
|
|
65
|
-
);
|
|
61
|
+
const maybeResult = await configuration.storage.get(query, cacheAttributes);
|
|
66
62
|
|
|
67
63
|
if (maybeResult) {
|
|
68
64
|
log.debug(
|
|
69
65
|
{
|
|
70
66
|
queryId: context.queryId,
|
|
71
67
|
},
|
|
72
|
-
|
|
68
|
+
"query is served from cache",
|
|
73
69
|
);
|
|
74
70
|
|
|
75
71
|
return maybeResult;
|
|
@@ -82,14 +78,11 @@ export const createQueryCacheInterceptor = (
|
|
|
82
78
|
return null;
|
|
83
79
|
}
|
|
84
80
|
|
|
85
|
-
const cacheAttributes = (context.sandbox as Sandbox).cache
|
|
86
|
-
?.cacheAttributes;
|
|
81
|
+
const cacheAttributes = (context.sandbox as Sandbox).cache?.cacheAttributes;
|
|
87
82
|
|
|
88
83
|
if (cacheAttributes) {
|
|
89
84
|
if (cacheAttributes.discardEmpty && result.rowCount === 0) {
|
|
90
|
-
log.debug(
|
|
91
|
-
'@cache-discard-empty is set and the query result is empty; not caching',
|
|
92
|
-
);
|
|
85
|
+
log.debug("@cache-discard-empty is set and the query result is empty; not caching");
|
|
93
86
|
} else {
|
|
94
87
|
await configuration.storage.set(query, cacheAttributes, result);
|
|
95
88
|
}
|
|
@@ -102,18 +95,13 @@ export const createQueryCacheInterceptor = (
|
|
|
102
95
|
return null;
|
|
103
96
|
}
|
|
104
97
|
|
|
105
|
-
const extractedCacheAttributes = extractCacheAttributes(
|
|
106
|
-
query.sql,
|
|
107
|
-
query.values,
|
|
108
|
-
);
|
|
98
|
+
const extractedCacheAttributes = extractCacheAttributes(query.sql, query.values);
|
|
109
99
|
|
|
110
100
|
if (!extractedCacheAttributes) {
|
|
111
101
|
return null;
|
|
112
102
|
}
|
|
113
103
|
|
|
114
|
-
const cacheAttributes = normalizeCacheAttributes(
|
|
115
|
-
extractedCacheAttributes,
|
|
116
|
-
);
|
|
104
|
+
const cacheAttributes = normalizeCacheAttributes(extractedCacheAttributes);
|
|
117
105
|
|
|
118
106
|
context.sandbox.cache = {
|
|
119
107
|
cacheAttributes,
|
|
@@ -121,6 +109,6 @@ export const createQueryCacheInterceptor = (
|
|
|
121
109
|
|
|
122
110
|
return null;
|
|
123
111
|
},
|
|
124
|
-
name:
|
|
112
|
+
name: "slonik-interceptor-query-cache",
|
|
125
113
|
};
|
|
126
114
|
};
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { createQueryCacheInterceptor } from
|
|
1
|
+
export { createQueryCacheInterceptor } from "./factories/createQueryCacheInterceptor.js";
|
|
@@ -1,92 +1,80 @@
|
|
|
1
|
-
import { extractCacheAttributes } from
|
|
2
|
-
import test from
|
|
1
|
+
import { extractCacheAttributes } from "./extractCacheAttributes.js";
|
|
2
|
+
import test from "ava";
|
|
3
3
|
|
|
4
|
-
test(
|
|
5
|
-
t.is(extractCacheAttributes(
|
|
4
|
+
test("returns null when query does not contain cache attributes", (t) => {
|
|
5
|
+
t.is(extractCacheAttributes("", []), null);
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
test(
|
|
9
|
-
t.deepEqual(extractCacheAttributes(
|
|
10
|
-
bodyHash:
|
|
8
|
+
test("extracts @cache-ttl", (t) => {
|
|
9
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60", []), {
|
|
10
|
+
bodyHash: "ef46db3751d8e999",
|
|
11
11
|
discardEmpty: false,
|
|
12
|
-
key:
|
|
12
|
+
key: "query:$bodyHash:$valueHash",
|
|
13
13
|
ttl: 60,
|
|
14
|
-
valueHash:
|
|
14
|
+
valueHash: "ccab0b28617f1f56",
|
|
15
15
|
});
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
test(
|
|
19
|
-
t.deepEqual(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
discardEmpty: false,
|
|
27
|
-
key: 'query:$bodyHash:$valueHash',
|
|
28
|
-
ttl: 60,
|
|
29
|
-
valueHash: 'ccab0b28617f1f56',
|
|
30
|
-
},
|
|
31
|
-
);
|
|
18
|
+
test("extracts @cache-discard-empty", (t) => {
|
|
19
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\n-- @cache-discard-empty false", []), {
|
|
20
|
+
bodyHash: "cafc7706cee4572b",
|
|
21
|
+
discardEmpty: false,
|
|
22
|
+
key: "query:$bodyHash:$valueHash",
|
|
23
|
+
ttl: 60,
|
|
24
|
+
valueHash: "ccab0b28617f1f56",
|
|
25
|
+
});
|
|
32
26
|
|
|
33
|
-
t.deepEqual(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
discardEmpty: true,
|
|
41
|
-
key: 'query:$bodyHash:$valueHash',
|
|
42
|
-
ttl: 60,
|
|
43
|
-
valueHash: 'ccab0b28617f1f56',
|
|
44
|
-
},
|
|
45
|
-
);
|
|
27
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\n-- @cache-discard-empty true", []), {
|
|
28
|
+
bodyHash: "cafc7706cee4572b",
|
|
29
|
+
discardEmpty: true,
|
|
30
|
+
key: "query:$bodyHash:$valueHash",
|
|
31
|
+
ttl: 60,
|
|
32
|
+
valueHash: "ccab0b28617f1f56",
|
|
33
|
+
});
|
|
46
34
|
});
|
|
47
35
|
|
|
48
|
-
test(
|
|
49
|
-
t.deepEqual(extractCacheAttributes(
|
|
50
|
-
bodyHash:
|
|
36
|
+
test("computes the parameter value hash", (t) => {
|
|
37
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60", [1]), {
|
|
38
|
+
bodyHash: "ef46db3751d8e999",
|
|
51
39
|
discardEmpty: false,
|
|
52
|
-
key:
|
|
40
|
+
key: "query:$bodyHash:$valueHash",
|
|
53
41
|
ttl: 60,
|
|
54
|
-
valueHash:
|
|
42
|
+
valueHash: "51ce9f3ef4b004a7",
|
|
55
43
|
});
|
|
56
44
|
});
|
|
57
45
|
|
|
58
|
-
test(
|
|
59
|
-
t.deepEqual(extractCacheAttributes(
|
|
60
|
-
bodyHash:
|
|
46
|
+
test("computes the body hash; white spaces do not affect the body hash", (t) => {
|
|
47
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\nSELECT 1", []), {
|
|
48
|
+
bodyHash: "6d1c807e68464b3b",
|
|
61
49
|
discardEmpty: false,
|
|
62
|
-
key:
|
|
50
|
+
key: "query:$bodyHash:$valueHash",
|
|
63
51
|
ttl: 60,
|
|
64
|
-
valueHash:
|
|
52
|
+
valueHash: "ccab0b28617f1f56",
|
|
65
53
|
});
|
|
66
54
|
|
|
67
|
-
t.deepEqual(extractCacheAttributes(
|
|
68
|
-
bodyHash:
|
|
55
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\n\nSELECT 1", []), {
|
|
56
|
+
bodyHash: "fa849a274ca52058",
|
|
69
57
|
discardEmpty: false,
|
|
70
|
-
key:
|
|
58
|
+
key: "query:$bodyHash:$valueHash",
|
|
71
59
|
ttl: 60,
|
|
72
|
-
valueHash:
|
|
60
|
+
valueHash: "ccab0b28617f1f56",
|
|
73
61
|
});
|
|
74
62
|
});
|
|
75
63
|
|
|
76
|
-
test(
|
|
77
|
-
t.deepEqual(extractCacheAttributes(
|
|
78
|
-
bodyHash:
|
|
64
|
+
test("computes the body hash; comments do not affect the body hash", (t) => {
|
|
65
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 60\nSELECT 1", []), {
|
|
66
|
+
bodyHash: "6d1c807e68464b3b",
|
|
79
67
|
discardEmpty: false,
|
|
80
|
-
key:
|
|
68
|
+
key: "query:$bodyHash:$valueHash",
|
|
81
69
|
ttl: 60,
|
|
82
|
-
valueHash:
|
|
70
|
+
valueHash: "ccab0b28617f1f56",
|
|
83
71
|
});
|
|
84
72
|
|
|
85
|
-
t.deepEqual(extractCacheAttributes(
|
|
86
|
-
bodyHash:
|
|
73
|
+
t.deepEqual(extractCacheAttributes("-- @cache-ttl 120\nSELECT 1", []), {
|
|
74
|
+
bodyHash: "6d1c807e68464b3b",
|
|
87
75
|
discardEmpty: false,
|
|
88
|
-
key:
|
|
76
|
+
key: "query:$bodyHash:$valueHash",
|
|
89
77
|
ttl: 120,
|
|
90
|
-
valueHash:
|
|
78
|
+
valueHash: "ccab0b28617f1f56",
|
|
91
79
|
});
|
|
92
80
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { xxh64 } from
|
|
2
|
-
import type { PrimitiveValueExpression } from
|
|
3
|
-
import { lru } from
|
|
1
|
+
import { xxh64 } from "@node-rs/xxhash";
|
|
2
|
+
import type { PrimitiveValueExpression } from "slonik";
|
|
3
|
+
import { lru } from "tiny-lru";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* LRU cache for body hashes.
|
|
@@ -9,7 +9,7 @@ import { lru } from 'tiny-lru';
|
|
|
9
9
|
const bodyHashCache = lru<string>(1_000);
|
|
10
10
|
|
|
11
11
|
const hash = (subject: string): string => {
|
|
12
|
-
return xxh64(subject).toString(16).padStart(16,
|
|
12
|
+
return xxh64(subject).toString(16).padStart(16, "0");
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -34,33 +34,31 @@ const getBodyHash = (sql: string, strippedSql: string): string => {
|
|
|
34
34
|
* Fast serialization for primitive values.
|
|
35
35
|
* For small arrays, avoids JSON.stringify overhead.
|
|
36
36
|
*/
|
|
37
|
-
const serializeValues = (
|
|
38
|
-
values: readonly PrimitiveValueExpression[],
|
|
39
|
-
): string => {
|
|
37
|
+
const serializeValues = (values: readonly PrimitiveValueExpression[]): string => {
|
|
40
38
|
if (values.length === 0) {
|
|
41
|
-
return
|
|
39
|
+
return "[]";
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
// For small arrays, direct join is faster
|
|
45
43
|
if (values.length <= 16) {
|
|
46
|
-
let result =
|
|
44
|
+
let result = "";
|
|
47
45
|
|
|
48
46
|
for (const value of values) {
|
|
49
47
|
if (result) {
|
|
50
|
-
result +=
|
|
48
|
+
result += "\u0000";
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
if (value === null) {
|
|
54
52
|
// null marker
|
|
55
|
-
result +=
|
|
56
|
-
} else if (typeof value ===
|
|
57
|
-
result +=
|
|
58
|
-
} else if (typeof value ===
|
|
59
|
-
result +=
|
|
60
|
-
} else if (typeof value ===
|
|
61
|
-
result += value ?
|
|
62
|
-
} else if (typeof value ===
|
|
63
|
-
result +=
|
|
53
|
+
result += "\u0001";
|
|
54
|
+
} else if (typeof value === "string") {
|
|
55
|
+
result += "s" + value;
|
|
56
|
+
} else if (typeof value === "number") {
|
|
57
|
+
result += "n" + value;
|
|
58
|
+
} else if (typeof value === "boolean") {
|
|
59
|
+
result += value ? "T" : "F";
|
|
60
|
+
} else if (typeof value === "bigint") {
|
|
61
|
+
result += "b" + value;
|
|
64
62
|
} else {
|
|
65
63
|
// Fallback for Buffer or other types
|
|
66
64
|
return JSON.stringify(values);
|
|
@@ -92,7 +90,7 @@ export const extractCacheAttributes = (
|
|
|
92
90
|
values: readonly PrimitiveValueExpression[],
|
|
93
91
|
): ExtractedCacheAttributes | null => {
|
|
94
92
|
// Fast early bail-out: skip all work for non-cached queries
|
|
95
|
-
if (!subject.includes(
|
|
93
|
+
if (!subject.includes("@cache-ttl")) {
|
|
96
94
|
return null;
|
|
97
95
|
}
|
|
98
96
|
|
|
@@ -103,19 +101,19 @@ export const extractCacheAttributes = (
|
|
|
103
101
|
}
|
|
104
102
|
|
|
105
103
|
// Extract key template first to determine which hashes we need
|
|
106
|
-
const key = subject.match(KeyRegex)?.[1] ??
|
|
107
|
-
const discardEmpty = subject.match(DiscardEmptyRegex)?.[1] ===
|
|
104
|
+
const key = subject.match(KeyRegex)?.[1] ?? "query:$bodyHash:$valueHash";
|
|
105
|
+
const discardEmpty = subject.match(DiscardEmptyRegex)?.[1] === "true";
|
|
108
106
|
|
|
109
|
-
const needsBodyHash = key.includes(
|
|
110
|
-
const needsValueHash = key.includes(
|
|
107
|
+
const needsBodyHash = key.includes("$bodyHash");
|
|
108
|
+
const needsValueHash = key.includes("$valueHash");
|
|
111
109
|
|
|
112
110
|
// Lazy computation: only compute hashes if they're used in the key
|
|
113
|
-
let bodyHash =
|
|
114
|
-
let valueHash =
|
|
111
|
+
let bodyHash = "";
|
|
112
|
+
let valueHash = "";
|
|
115
113
|
|
|
116
114
|
if (needsBodyHash) {
|
|
117
115
|
// Strip comments and compute hash (with memoization)
|
|
118
|
-
const strippedSql = subject.replaceAll(CommentRegex,
|
|
116
|
+
const strippedSql = subject.replaceAll(CommentRegex, "");
|
|
119
117
|
bodyHash = getBodyHash(subject, strippedSql);
|
|
120
118
|
}
|
|
121
119
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { normalizeCacheAttributes } from
|
|
2
|
-
import test from
|
|
1
|
+
import { normalizeCacheAttributes } from "./normalizeCacheAttributes.js";
|
|
2
|
+
import test from "ava";
|
|
3
3
|
|
|
4
|
-
test(
|
|
4
|
+
test("replaces $bodyHash and $valueHash", (t) => {
|
|
5
5
|
t.deepEqual(
|
|
6
6
|
normalizeCacheAttributes({
|
|
7
|
-
bodyHash:
|
|
7
|
+
bodyHash: "foo",
|
|
8
8
|
discardEmpty: false,
|
|
9
|
-
key:
|
|
9
|
+
key: "$bodyHash:$valueHash",
|
|
10
10
|
ttl: 60,
|
|
11
|
-
valueHash:
|
|
11
|
+
valueHash: "bar",
|
|
12
12
|
}),
|
|
13
13
|
{
|
|
14
14
|
discardEmpty: false,
|
|
15
|
-
key:
|
|
15
|
+
key: "foo:bar",
|
|
16
16
|
ttl: 60,
|
|
17
17
|
},
|
|
18
18
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CacheAttributes } from
|
|
2
|
-
import type { ExtractedCacheAttributes } from
|
|
1
|
+
import type { CacheAttributes } from "../factories/createQueryCacheInterceptor.js";
|
|
2
|
+
import type { ExtractedCacheAttributes } from "./extractCacheAttributes.js";
|
|
3
3
|
|
|
4
4
|
export const normalizeCacheAttributes = (
|
|
5
5
|
extractedCacheAttributes: ExtractedCacheAttributes,
|
|
@@ -7,8 +7,8 @@ export const normalizeCacheAttributes = (
|
|
|
7
7
|
return {
|
|
8
8
|
discardEmpty: extractedCacheAttributes.discardEmpty,
|
|
9
9
|
key: extractedCacheAttributes.key
|
|
10
|
-
.replaceAll(
|
|
11
|
-
.replaceAll(
|
|
10
|
+
.replaceAll("$bodyHash", extractedCacheAttributes.bodyHash)
|
|
11
|
+
.replaceAll("$valueHash", extractedCacheAttributes.valueHash),
|
|
12
12
|
ttl: extractedCacheAttributes.ttl,
|
|
13
13
|
};
|
|
14
14
|
};
|
package/tsconfig.json
CHANGED
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
"declaration": true,
|
|
5
5
|
"declarationMap": true,
|
|
6
6
|
"esModuleInterop": true,
|
|
7
|
-
"lib": [
|
|
8
|
-
"es2024"
|
|
9
|
-
],
|
|
7
|
+
"lib": ["es2024"],
|
|
10
8
|
"module": "ES2022",
|
|
11
9
|
"moduleResolution": "node",
|
|
12
10
|
"noImplicitAny": false,
|
|
@@ -18,7 +16,5 @@
|
|
|
18
16
|
"target": "es2024",
|
|
19
17
|
"useUnknownInCatchVariables": false
|
|
20
18
|
},
|
|
21
|
-
"include": [
|
|
22
|
-
|
|
23
|
-
]
|
|
24
|
-
}
|
|
19
|
+
"include": ["src"]
|
|
20
|
+
}
|
package/eslint.config.js
DELETED