@mastra/redis 1.4.3 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache.d.ts +35 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +65 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +65 -3
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/cache.d.ts
CHANGED
|
@@ -9,16 +9,40 @@ export interface RedisClient {
|
|
|
9
9
|
expire(key: string, seconds: number): Promise<number | boolean>;
|
|
10
10
|
scan(cursor: string | number, ...args: unknown[]): Promise<[string | number, string[]]>;
|
|
11
11
|
incr(key: string): Promise<number>;
|
|
12
|
+
/**
|
|
13
|
+
* Server-side Lua scripting. Optional: clients without it fall back to the
|
|
14
|
+
* two-round-trip `increment` + `listPush` path for `listPushIndexed`.
|
|
15
|
+
*/
|
|
16
|
+
eval?(...args: unknown[]): Promise<unknown>;
|
|
12
17
|
}
|
|
13
18
|
export interface RedisServerCacheOptions {
|
|
14
19
|
keyPrefix?: string;
|
|
15
20
|
ttlSeconds?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Run a Lua script with the given KEYS and ARGV. Defaults to the ioredis
|
|
23
|
+
* calling convention (`eval(script, numKeys, ...keys, ...args)`); use
|
|
24
|
+
* `nodeRedisPreset` / `upstashPreset` for those clients.
|
|
25
|
+
*/
|
|
26
|
+
evalScript?: (client: RedisClient, script: string, keys: string[], args: string[]) => Promise<unknown>;
|
|
16
27
|
setWithExpiry?: (client: RedisClient, key: string, value: unknown, seconds: number) => Promise<unknown>;
|
|
17
28
|
scanKeys?: (client: RedisClient, cursor: string | number, pattern: string, count: number) => Promise<[string | number, string[]]>;
|
|
18
29
|
getListLength?: (client: RedisClient, key: string) => Promise<number>;
|
|
19
30
|
pushToList?: (client: RedisClient, key: string, value: unknown) => Promise<number>;
|
|
20
31
|
getListRange?: (client: RedisClient, key: string, start: number, stop: number) => Promise<unknown[]>;
|
|
21
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Atomic "allocate index + append + refresh TTLs" used by `listPushIndexed`.
|
|
35
|
+
*
|
|
36
|
+
* KEYS[1] = list key, KEYS[2] = counter key
|
|
37
|
+
* ARGV[1] = JSON-serialized object WITHOUT an `index` property
|
|
38
|
+
* ARGV[2] = TTL in seconds (0 = no expiry)
|
|
39
|
+
*
|
|
40
|
+
* The index is spliced into the JSON text as the first property rather than
|
|
41
|
+
* decoded/re-encoded with cjson, which would silently alter large integers
|
|
42
|
+
* and sparse arrays. `JSON.stringify` of an object always starts with `{`,
|
|
43
|
+
* so `{...}` → `{"index":N,...}` and `{}` → `{"index":N}`.
|
|
44
|
+
*/
|
|
45
|
+
export declare const LIST_PUSH_INDEXED_SCRIPT: string;
|
|
22
46
|
export declare class RedisServerCache extends MastraServerCache {
|
|
23
47
|
private client;
|
|
24
48
|
private keyPrefix;
|
|
@@ -28,6 +52,9 @@ export declare class RedisServerCache extends MastraServerCache {
|
|
|
28
52
|
private getListLength;
|
|
29
53
|
private pushToList;
|
|
30
54
|
private getListRange;
|
|
55
|
+
private evalScript;
|
|
56
|
+
/** Set once scripting is known to be unusable (no `eval`, or Cluster CROSSSLOT). */
|
|
57
|
+
private scriptingDisabled;
|
|
31
58
|
constructor(config: {
|
|
32
59
|
client: RedisClient;
|
|
33
60
|
}, options?: RedisServerCacheOptions);
|
|
@@ -42,7 +69,13 @@ export declare class RedisServerCache extends MastraServerCache {
|
|
|
42
69
|
delete(key: string): Promise<void>;
|
|
43
70
|
clear(): Promise<void>;
|
|
44
71
|
increment(key: string): Promise<number>;
|
|
72
|
+
/**
|
|
73
|
+
* Single round-trip index allocation + list append + TTL refresh via Lua.
|
|
74
|
+
* This is the durable stream per-chunk hot path (issue #22477): the composed
|
|
75
|
+
* default costs four awaited commands (INCR, EXPIRE, RPUSH, EXPIRE).
|
|
76
|
+
*/
|
|
77
|
+
listPushIndexed(listKey: string, counterKey: string, value: Record<string, unknown>): Promise<number>;
|
|
45
78
|
}
|
|
46
|
-
export declare const upstashPreset: Pick<RedisServerCacheOptions, 'setWithExpiry' | 'scanKeys'>;
|
|
47
|
-
export declare const nodeRedisPreset: Pick<RedisServerCacheOptions, 'setWithExpiry' | 'scanKeys' | 'getListLength' | 'pushToList' | 'getListRange'>;
|
|
79
|
+
export declare const upstashPreset: Pick<RedisServerCacheOptions, 'setWithExpiry' | 'scanKeys' | 'evalScript'>;
|
|
80
|
+
export declare const nodeRedisPreset: Pick<RedisServerCacheOptions, 'setWithExpiry' | 'scanKeys' | 'getListLength' | 'pushToList' | 'getListRange' | 'evalScript'>;
|
|
48
81
|
//# sourceMappingURL=cache.d.ts.map
|
package/dist/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACrE,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;IAChE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACxF,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/docs/SKILL.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1620,6 +1620,38 @@ const defaultPushToList = (client, key, value) => {
|
|
|
1620
1620
|
const defaultGetListRange = (client, key, start, stop) => {
|
|
1621
1621
|
return client.lrange(key, start, stop);
|
|
1622
1622
|
};
|
|
1623
|
+
const defaultEvalScript = (client, script, keys, args) => {
|
|
1624
|
+
return client.eval(script, keys.length, ...keys, ...args);
|
|
1625
|
+
};
|
|
1626
|
+
/**
|
|
1627
|
+
* Atomic "allocate index + append + refresh TTLs" used by `listPushIndexed`.
|
|
1628
|
+
*
|
|
1629
|
+
* KEYS[1] = list key, KEYS[2] = counter key
|
|
1630
|
+
* ARGV[1] = JSON-serialized object WITHOUT an `index` property
|
|
1631
|
+
* ARGV[2] = TTL in seconds (0 = no expiry)
|
|
1632
|
+
*
|
|
1633
|
+
* The index is spliced into the JSON text as the first property rather than
|
|
1634
|
+
* decoded/re-encoded with cjson, which would silently alter large integers
|
|
1635
|
+
* and sparse arrays. `JSON.stringify` of an object always starts with `{`,
|
|
1636
|
+
* so `{...}` → `{"index":N,...}` and `{}` → `{"index":N}`.
|
|
1637
|
+
*/
|
|
1638
|
+
const LIST_PUSH_INDEXED_SCRIPT = `
|
|
1639
|
+
local i = redis.call('INCR', KEYS[2]) - 1
|
|
1640
|
+
local body = ARGV[1]
|
|
1641
|
+
local doc
|
|
1642
|
+
if #body <= 2 then
|
|
1643
|
+
doc = '{"index":' .. i .. '}'
|
|
1644
|
+
else
|
|
1645
|
+
doc = '{"index":' .. i .. ',' .. string.sub(body, 2)
|
|
1646
|
+
end
|
|
1647
|
+
redis.call('RPUSH', KEYS[1], doc)
|
|
1648
|
+
local ttl = tonumber(ARGV[2])
|
|
1649
|
+
if ttl > 0 then
|
|
1650
|
+
redis.call('EXPIRE', KEYS[1], ttl)
|
|
1651
|
+
redis.call('EXPIRE', KEYS[2], ttl)
|
|
1652
|
+
end
|
|
1653
|
+
return i
|
|
1654
|
+
`.trim();
|
|
1623
1655
|
var RedisServerCache = class extends _mastra_core_cache.MastraServerCache {
|
|
1624
1656
|
client;
|
|
1625
1657
|
keyPrefix;
|
|
@@ -1629,6 +1661,9 @@ var RedisServerCache = class extends _mastra_core_cache.MastraServerCache {
|
|
|
1629
1661
|
getListLength;
|
|
1630
1662
|
pushToList;
|
|
1631
1663
|
getListRange;
|
|
1664
|
+
evalScript;
|
|
1665
|
+
/** Set once scripting is known to be unusable (no `eval`, or Cluster CROSSSLOT). */
|
|
1666
|
+
scriptingDisabled;
|
|
1632
1667
|
constructor(config, options = {}) {
|
|
1633
1668
|
super({ name: "RedisServerCache" });
|
|
1634
1669
|
this.client = config.client;
|
|
@@ -1639,6 +1674,8 @@ var RedisServerCache = class extends _mastra_core_cache.MastraServerCache {
|
|
|
1639
1674
|
this.getListLength = options.getListLength ?? defaultGetListLength;
|
|
1640
1675
|
this.pushToList = options.pushToList ?? defaultPushToList;
|
|
1641
1676
|
this.getListRange = options.getListRange ?? defaultGetListRange;
|
|
1677
|
+
this.evalScript = options.evalScript ?? defaultEvalScript;
|
|
1678
|
+
this.scriptingDisabled = typeof this.client.eval !== "function";
|
|
1642
1679
|
}
|
|
1643
1680
|
getKey(key) {
|
|
1644
1681
|
return `${this.keyPrefix}${key}`;
|
|
@@ -1700,13 +1737,34 @@ var RedisServerCache = class extends _mastra_core_cache.MastraServerCache {
|
|
|
1700
1737
|
if (this.ttlSeconds > 0) await this.client.expire(fullKey, this.ttlSeconds);
|
|
1701
1738
|
return value;
|
|
1702
1739
|
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Single round-trip index allocation + list append + TTL refresh via Lua.
|
|
1742
|
+
* This is the durable stream per-chunk hot path (issue #22477): the composed
|
|
1743
|
+
* default costs four awaited commands (INCR, EXPIRE, RPUSH, EXPIRE).
|
|
1744
|
+
*/
|
|
1745
|
+
async listPushIndexed(listKey, counterKey, value) {
|
|
1746
|
+
if (this.scriptingDisabled) return super.listPushIndexed(listKey, counterKey, value);
|
|
1747
|
+
const { index: _ignored, ...body } = value;
|
|
1748
|
+
try {
|
|
1749
|
+
const result = await this.evalScript(this.client, LIST_PUSH_INDEXED_SCRIPT, [this.getKey(listKey), this.getKey(counterKey)], [this.serialize(body), String(this.ttlSeconds)]);
|
|
1750
|
+
return Number(result);
|
|
1751
|
+
} catch (error) {
|
|
1752
|
+
if (error instanceof Error && error.message.includes("CROSSSLOT")) {
|
|
1753
|
+
this.scriptingDisabled = true;
|
|
1754
|
+
this.logger.warn("[RedisServerCache] listPushIndexed script rejected with CROSSSLOT; falling back to increment + listPush");
|
|
1755
|
+
return super.listPushIndexed(listKey, counterKey, value);
|
|
1756
|
+
}
|
|
1757
|
+
throw error;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1703
1760
|
};
|
|
1704
1761
|
const upstashPreset = {
|
|
1705
1762
|
setWithExpiry: (client, key, value, seconds) => client.set(key, value, { ex: seconds }),
|
|
1706
1763
|
scanKeys: (client, cursor, pattern, count) => client.scan(cursor, {
|
|
1707
1764
|
match: pattern,
|
|
1708
1765
|
count
|
|
1709
|
-
})
|
|
1766
|
+
}),
|
|
1767
|
+
evalScript: (client, script, keys, args) => client.eval(script, keys, args)
|
|
1710
1768
|
};
|
|
1711
1769
|
const nodeRedisPreset = {
|
|
1712
1770
|
setWithExpiry: (client, key, value, seconds) => client.set(key, value, { EX: seconds }),
|
|
@@ -1716,9 +1774,14 @@ const nodeRedisPreset = {
|
|
|
1716
1774
|
}),
|
|
1717
1775
|
getListLength: (client, key) => client.lLen(key),
|
|
1718
1776
|
pushToList: (client, key, value) => client.rPush(key, value),
|
|
1719
|
-
getListRange: (client, key, start, stop) => client.lRange(key, start, stop)
|
|
1777
|
+
getListRange: (client, key, start, stop) => client.lRange(key, start, stop),
|
|
1778
|
+
evalScript: (client, script, keys, args) => client.eval(script, {
|
|
1779
|
+
keys,
|
|
1780
|
+
arguments: args
|
|
1781
|
+
})
|
|
1720
1782
|
};
|
|
1721
1783
|
//#endregion
|
|
1784
|
+
exports.LIST_PUSH_INDEXED_SCRIPT = LIST_PUSH_INDEXED_SCRIPT;
|
|
1722
1785
|
exports.RedisServerCache = RedisServerCache;
|
|
1723
1786
|
exports.RedisStore = RedisStore;
|
|
1724
1787
|
exports.ScoresRedis = ScoresRedis;
|