dialcache 0.1.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/LICENSE +21 -0
- package/README.md +357 -0
- package/dist/chunk-2TPHNNE7.js +164 -0
- package/dist/chunk-BF6XTPTS.js +32 -0
- package/dist/chunk-S5VCTJID.js +18 -0
- package/dist/config-B7bGp-Xj.d.cts +205 -0
- package/dist/config-B7bGp-Xj.d.ts +205 -0
- package/dist/index.cjs +1011 -0
- package/dist/index.d.cts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +956 -0
- package/dist/node-redis.cjs +329 -0
- package/dist/node-redis.d.cts +52 -0
- package/dist/node-redis.d.ts +52 -0
- package/dist/node-redis.js +127 -0
- package/dist/redis-protocol.cjs +197 -0
- package/dist/redis-protocol.d.cts +10 -0
- package/dist/redis-protocol.d.ts +10 -0
- package/dist/redis-protocol.js +20 -0
- package/dist/valkey-glide.cjs +299 -0
- package/dist/valkey-glide.d.cts +12 -0
- package/dist/valkey-glide.d.ts +12 -0
- package/dist/valkey-glide.js +104 -0
- package/package.json +115 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/node-redis.ts
|
|
21
|
+
var node_redis_exports = {};
|
|
22
|
+
__export(node_redis_exports, {
|
|
23
|
+
createNodeRedisDialCacheClient: () => createNodeRedisDialCacheClient,
|
|
24
|
+
dialcacheRedisScripts: () => dialcacheRedisScripts
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(node_redis_exports);
|
|
27
|
+
var import_redis = require("redis");
|
|
28
|
+
|
|
29
|
+
// src/internal/redis-scripts.ts
|
|
30
|
+
var REDIS_FRAME_VERSION = 1;
|
|
31
|
+
var REDIS_ENCODING_UTF8 = 0;
|
|
32
|
+
var REDIS_ENCODING_BINARY = 1;
|
|
33
|
+
var WATERMARK_TTL_MARGIN_MS = 6e4;
|
|
34
|
+
var PARSE_WATERMARK_LUA = String.raw`local function parse_watermark(raw)
|
|
35
|
+
if not string.match(raw, "^%d+$") and not string.match(raw, "^%d+%.%d+$") then
|
|
36
|
+
return nil
|
|
37
|
+
end
|
|
38
|
+
local value = tonumber(raw)
|
|
39
|
+
if not value or value >= math.huge then
|
|
40
|
+
return nil
|
|
41
|
+
end
|
|
42
|
+
return value
|
|
43
|
+
end`;
|
|
44
|
+
var CEIL_FINITE_NUMBER_LUA = String.raw`local function ceil_finite_number(raw)
|
|
45
|
+
local value = tonumber(raw)
|
|
46
|
+
if not value or value ~= value or value >= math.huge or value <= -math.huge then
|
|
47
|
+
return nil
|
|
48
|
+
end
|
|
49
|
+
return math.ceil(value)
|
|
50
|
+
end`;
|
|
51
|
+
var READ_FRAME_LUA = String.raw`local value = redis.call("GET", KEYS[1])
|
|
52
|
+
if not value or string.len(value) < 10 then
|
|
53
|
+
return false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if string.byte(value, 1) ~= ${REDIS_FRAME_VERSION} then
|
|
57
|
+
return false
|
|
58
|
+
end`;
|
|
59
|
+
var RETURN_PAYLOAD_LUA = String.raw`return string.sub(value, 10)`;
|
|
60
|
+
var VALIDATE_WRITE_ARGUMENTS_LUA = String.raw`local cache_ttl_ms = ceil_finite_number(ARGV[1])
|
|
61
|
+
local encoding = tonumber(ARGV[2])
|
|
62
|
+
if not cache_ttl_ms or cache_ttl_ms <= 0 then
|
|
63
|
+
return redis.error_reply("ERR invalid DialCache TTL")
|
|
64
|
+
end
|
|
65
|
+
if not encoding or (encoding ~= ${REDIS_ENCODING_UTF8} and encoding ~= ${REDIS_ENCODING_BINARY}) then
|
|
66
|
+
return redis.error_reply("ERR invalid DialCache payload encoding")
|
|
67
|
+
end`;
|
|
68
|
+
var REDIS_TIME_LUA = String.raw`local redis_time = redis.call("TIME")
|
|
69
|
+
local now_ms = tonumber(redis_time[1]) * 1000 + math.floor(tonumber(redis_time[2]) / 1000)`;
|
|
70
|
+
var WRITE_FRAME_LUA = String.raw`local frame = string.char(${REDIS_FRAME_VERSION})
|
|
71
|
+
.. struct.pack(">I8", now_ms)
|
|
72
|
+
.. string.char(encoding)
|
|
73
|
+
.. ARGV[3]
|
|
74
|
+
redis.call("SET", KEYS[1], frame, "PX", cache_ttl_ms)`;
|
|
75
|
+
var READ_CACHE_SCRIPT = [READ_FRAME_LUA, RETURN_PAYLOAD_LUA].join("\n\n");
|
|
76
|
+
var READ_TRACKED_CACHE_SCRIPT = [
|
|
77
|
+
PARSE_WATERMARK_LUA,
|
|
78
|
+
READ_FRAME_LUA,
|
|
79
|
+
String.raw`local raw_watermark = redis.call("GET", KEYS[2])
|
|
80
|
+
if not raw_watermark then
|
|
81
|
+
return false
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
local watermark = parse_watermark(raw_watermark)
|
|
85
|
+
if not watermark then
|
|
86
|
+
return false
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
local created_at = struct.unpack(">I8", string.sub(value, 2, 9))
|
|
90
|
+
if created_at <= watermark then
|
|
91
|
+
return false
|
|
92
|
+
end`,
|
|
93
|
+
RETURN_PAYLOAD_LUA
|
|
94
|
+
].join("\n\n");
|
|
95
|
+
var WRITE_CACHE_SCRIPT = [
|
|
96
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
97
|
+
VALIDATE_WRITE_ARGUMENTS_LUA,
|
|
98
|
+
REDIS_TIME_LUA,
|
|
99
|
+
WRITE_FRAME_LUA,
|
|
100
|
+
"return 1"
|
|
101
|
+
].join("\n\n");
|
|
102
|
+
var WRITE_TRACKED_CACHE_SCRIPT = [
|
|
103
|
+
PARSE_WATERMARK_LUA,
|
|
104
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
105
|
+
VALIDATE_WRITE_ARGUMENTS_LUA,
|
|
106
|
+
String.raw`local watermark_ttl_floor_ms = ceil_finite_number(ARGV[4])
|
|
107
|
+
if not watermark_ttl_floor_ms or watermark_ttl_floor_ms <= 0 then
|
|
108
|
+
return redis.error_reply("ERR invalid DialCache watermark TTL")
|
|
109
|
+
end`,
|
|
110
|
+
REDIS_TIME_LUA,
|
|
111
|
+
String.raw`local raw_watermark = redis.call("GET", KEYS[2])
|
|
112
|
+
local watermark = 0
|
|
113
|
+
if raw_watermark then
|
|
114
|
+
watermark = parse_watermark(raw_watermark)
|
|
115
|
+
if not watermark then
|
|
116
|
+
return redis.error_reply("ERR invalid DialCache watermark")
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
if watermark >= now_ms then
|
|
121
|
+
return 0
|
|
122
|
+
end`,
|
|
123
|
+
WRITE_FRAME_LUA,
|
|
124
|
+
String.raw`local desired_ttl_ms = math.max(watermark_ttl_floor_ms, cache_ttl_ms + ${WATERMARK_TTL_MARGIN_MS})
|
|
125
|
+
if not raw_watermark then
|
|
126
|
+
redis.call("SET", KEYS[2], "0", "PX", desired_ttl_ms)
|
|
127
|
+
else
|
|
128
|
+
local current_ttl_ms = redis.call("PTTL", KEYS[2])
|
|
129
|
+
if current_ttl_ms == -2 then
|
|
130
|
+
redis.call("SET", KEYS[2], raw_watermark, "PX", desired_ttl_ms)
|
|
131
|
+
elseif current_ttl_ms ~= -1 and current_ttl_ms < desired_ttl_ms then
|
|
132
|
+
redis.call("PEXPIRE", KEYS[2], desired_ttl_ms)
|
|
133
|
+
end
|
|
134
|
+
end`,
|
|
135
|
+
"return 1"
|
|
136
|
+
].join("\n\n");
|
|
137
|
+
var INVALIDATE_CACHE_SCRIPT = [
|
|
138
|
+
PARSE_WATERMARK_LUA,
|
|
139
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
140
|
+
String.raw`local future_buffer_ms = ceil_finite_number(ARGV[1])
|
|
141
|
+
local watermark_ttl_floor_ms = ceil_finite_number(ARGV[2])
|
|
142
|
+
if not future_buffer_ms or future_buffer_ms < 0 then
|
|
143
|
+
return redis.error_reply("ERR invalid DialCache future buffer")
|
|
144
|
+
end
|
|
145
|
+
if not watermark_ttl_floor_ms or watermark_ttl_floor_ms <= 0 then
|
|
146
|
+
return redis.error_reply("ERR invalid DialCache watermark TTL")
|
|
147
|
+
end`,
|
|
148
|
+
REDIS_TIME_LUA,
|
|
149
|
+
String.raw`local proposed_watermark = now_ms + future_buffer_ms
|
|
150
|
+
local raw_watermark = redis.call("GET", KEYS[1])
|
|
151
|
+
local current_watermark = 0
|
|
152
|
+
|
|
153
|
+
if raw_watermark then
|
|
154
|
+
local parsed_watermark = parse_watermark(raw_watermark)
|
|
155
|
+
if parsed_watermark then
|
|
156
|
+
current_watermark = parsed_watermark
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
local watermark = math.ceil(math.max(current_watermark, proposed_watermark))
|
|
161
|
+
local current_ttl_ms = -2
|
|
162
|
+
if raw_watermark then
|
|
163
|
+
current_ttl_ms = redis.call("PTTL", KEYS[1])
|
|
164
|
+
end
|
|
165
|
+
local desired_ttl_ms = math.max(
|
|
166
|
+
watermark_ttl_floor_ms,
|
|
167
|
+
future_buffer_ms + ${WATERMARK_TTL_MARGIN_MS},
|
|
168
|
+
watermark - now_ms + ${WATERMARK_TTL_MARGIN_MS}
|
|
169
|
+
)
|
|
170
|
+
if current_ttl_ms > desired_ttl_ms then
|
|
171
|
+
desired_ttl_ms = current_ttl_ms
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
local encoded_watermark = string.format("%.0f", watermark)
|
|
175
|
+
if current_ttl_ms == -1 then
|
|
176
|
+
redis.call("SET", KEYS[1], encoded_watermark)
|
|
177
|
+
else
|
|
178
|
+
redis.call("SET", KEYS[1], encoded_watermark, "PX", desired_ttl_ms)
|
|
179
|
+
end`,
|
|
180
|
+
"return 1"
|
|
181
|
+
].join("\n\n");
|
|
182
|
+
|
|
183
|
+
// src/redis-client.ts
|
|
184
|
+
var DialCacheRedisPayloadError = class extends Error {
|
|
185
|
+
constructor(message) {
|
|
186
|
+
super(message);
|
|
187
|
+
this.name = "DialCacheRedisPayloadError";
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
var DialCacheRedisPayloadEncodingError = class extends Error {
|
|
191
|
+
constructor(message) {
|
|
192
|
+
super(message);
|
|
193
|
+
this.name = "DialCacheRedisPayloadEncodingError";
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/internal/redis-payload.ts
|
|
198
|
+
function redisPayloadEncoding(value) {
|
|
199
|
+
return Buffer.isBuffer(value) ? REDIS_ENCODING_BINARY : REDIS_ENCODING_UTF8;
|
|
200
|
+
}
|
|
201
|
+
function decodeRedisPayload(raw) {
|
|
202
|
+
if (raw.length === 0) {
|
|
203
|
+
throw new DialCacheRedisPayloadError("Invalid DialCache Redis payload");
|
|
204
|
+
}
|
|
205
|
+
const encoding = raw[0];
|
|
206
|
+
const payload = raw.subarray(1);
|
|
207
|
+
if (encoding === REDIS_ENCODING_UTF8) {
|
|
208
|
+
return payload.toString("utf8");
|
|
209
|
+
}
|
|
210
|
+
if (encoding === REDIS_ENCODING_BINARY) {
|
|
211
|
+
return payload;
|
|
212
|
+
}
|
|
213
|
+
throw new DialCacheRedisPayloadEncodingError("Invalid DialCache Redis payload encoding");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/node-redis.ts
|
|
217
|
+
var bufferReplyOptions = (0, import_redis.commandOptions)({ returnBuffers: true });
|
|
218
|
+
var readReply = (reply) => reply;
|
|
219
|
+
var integerReply = (reply) => reply;
|
|
220
|
+
function defineDialCacheScript(config) {
|
|
221
|
+
return (0, import_redis.defineScript)(config);
|
|
222
|
+
}
|
|
223
|
+
var dialcacheRedisScripts = {
|
|
224
|
+
dialcacheRead: defineDialCacheScript({
|
|
225
|
+
SCRIPT: READ_CACHE_SCRIPT,
|
|
226
|
+
NUMBER_OF_KEYS: 1,
|
|
227
|
+
FIRST_KEY_INDEX: 0,
|
|
228
|
+
IS_READ_ONLY: true,
|
|
229
|
+
transformArguments(valueKey) {
|
|
230
|
+
return [valueKey];
|
|
231
|
+
},
|
|
232
|
+
transformReply: readReply
|
|
233
|
+
}),
|
|
234
|
+
dialcacheReadTracked: defineDialCacheScript({
|
|
235
|
+
SCRIPT: READ_TRACKED_CACHE_SCRIPT,
|
|
236
|
+
NUMBER_OF_KEYS: 2,
|
|
237
|
+
FIRST_KEY_INDEX: 0,
|
|
238
|
+
// Replica lag must not hide a newly-written invalidation watermark.
|
|
239
|
+
IS_READ_ONLY: false,
|
|
240
|
+
transformArguments(valueKey, watermarkKey) {
|
|
241
|
+
return [valueKey, watermarkKey];
|
|
242
|
+
},
|
|
243
|
+
transformReply: readReply
|
|
244
|
+
}),
|
|
245
|
+
dialcacheWrite: defineDialCacheScript({
|
|
246
|
+
SCRIPT: WRITE_CACHE_SCRIPT,
|
|
247
|
+
NUMBER_OF_KEYS: 1,
|
|
248
|
+
FIRST_KEY_INDEX: 0,
|
|
249
|
+
IS_READ_ONLY: false,
|
|
250
|
+
transformArguments(valueKey, cacheTtlMs, encoding, payload) {
|
|
251
|
+
return [valueKey, String(cacheTtlMs), String(encoding), payload];
|
|
252
|
+
},
|
|
253
|
+
transformReply: integerReply
|
|
254
|
+
}),
|
|
255
|
+
dialcacheWriteTracked: defineDialCacheScript({
|
|
256
|
+
SCRIPT: WRITE_TRACKED_CACHE_SCRIPT,
|
|
257
|
+
NUMBER_OF_KEYS: 2,
|
|
258
|
+
FIRST_KEY_INDEX: 0,
|
|
259
|
+
IS_READ_ONLY: false,
|
|
260
|
+
transformArguments(valueKey, watermarkKey, cacheTtlMs, encoding, payload, watermarkTtlFloorMs) {
|
|
261
|
+
return [valueKey, watermarkKey, String(cacheTtlMs), String(encoding), payload, String(watermarkTtlFloorMs)];
|
|
262
|
+
},
|
|
263
|
+
transformReply: integerReply
|
|
264
|
+
}),
|
|
265
|
+
dialcacheInvalidate: defineDialCacheScript({
|
|
266
|
+
SCRIPT: INVALIDATE_CACHE_SCRIPT,
|
|
267
|
+
NUMBER_OF_KEYS: 1,
|
|
268
|
+
FIRST_KEY_INDEX: 0,
|
|
269
|
+
IS_READ_ONLY: false,
|
|
270
|
+
transformArguments(watermarkKey, futureBufferMs, watermarkTtlFloorMs) {
|
|
271
|
+
return [watermarkKey, String(futureBufferMs), String(watermarkTtlFloorMs)];
|
|
272
|
+
},
|
|
273
|
+
transformReply: integerReply
|
|
274
|
+
})
|
|
275
|
+
};
|
|
276
|
+
function createNodeRedisDialCacheClient(client) {
|
|
277
|
+
return {
|
|
278
|
+
async read({ valueKey, watermarkKey }) {
|
|
279
|
+
const raw = watermarkKey === void 0 ? await client.dialcacheRead(bufferReplyOptions, valueKey) : await client.dialcacheReadTracked(bufferReplyOptions, valueKey, watermarkKey);
|
|
280
|
+
return raw === null ? null : decodeRedisPayload(raw);
|
|
281
|
+
},
|
|
282
|
+
async write(request) {
|
|
283
|
+
const { valueKey, watermarkKey, cacheTtlMs, value } = request;
|
|
284
|
+
const encodingByte = redisPayloadEncoding(value);
|
|
285
|
+
const result = watermarkKey === void 0 ? await client.dialcacheWrite(valueKey, cacheTtlMs, encodingByte, value) : await client.dialcacheWriteTracked(
|
|
286
|
+
valueKey,
|
|
287
|
+
watermarkKey,
|
|
288
|
+
cacheTtlMs,
|
|
289
|
+
encodingByte,
|
|
290
|
+
value,
|
|
291
|
+
request.watermarkTtlFloorMs
|
|
292
|
+
);
|
|
293
|
+
return result === 1;
|
|
294
|
+
},
|
|
295
|
+
async invalidate({ watermarkKey, futureBufferMs, watermarkTtlFloorMs }) {
|
|
296
|
+
await client.dialcacheInvalidate(watermarkKey, futureBufferMs, watermarkTtlFloorMs);
|
|
297
|
+
},
|
|
298
|
+
async flushAll() {
|
|
299
|
+
const cluster = clusterCommands(client);
|
|
300
|
+
if (cluster === null) {
|
|
301
|
+
if (client.flushAll === void 0) {
|
|
302
|
+
throw new Error("Node-redis client does not implement flushAll");
|
|
303
|
+
}
|
|
304
|
+
await client.flushAll();
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (cluster.masters.length === 0) {
|
|
308
|
+
throw new Error("Node-redis cluster has no connected masters");
|
|
309
|
+
}
|
|
310
|
+
await Promise.all(
|
|
311
|
+
cluster.masters.map(async (master) => {
|
|
312
|
+
const node = await cluster.nodeClient(master);
|
|
313
|
+
await node.flushAll();
|
|
314
|
+
})
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function clusterCommands(client) {
|
|
320
|
+
if (!("masters" in client) || !("nodeClient" in client) || !Array.isArray(client.masters)) {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
return client;
|
|
324
|
+
}
|
|
325
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
326
|
+
0 && (module.exports = {
|
|
327
|
+
createNodeRedisDialCacheClient,
|
|
328
|
+
dialcacheRedisScripts
|
|
329
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { commandOptions } from 'redis';
|
|
2
|
+
import { k as DialCacheRedisClient } from './config-B7bGp-Xj.cjs';
|
|
3
|
+
import 'prom-client';
|
|
4
|
+
|
|
5
|
+
type BufferReplyOptions = ReturnType<typeof commandOptions<{
|
|
6
|
+
readonly returnBuffers: true;
|
|
7
|
+
}>>;
|
|
8
|
+
type NodeRedisArgument = string | Buffer;
|
|
9
|
+
interface NodeRedisScript<Args extends Array<unknown>, Reply> {
|
|
10
|
+
readonly SCRIPT: string;
|
|
11
|
+
readonly SHA1: string;
|
|
12
|
+
readonly NUMBER_OF_KEYS: number;
|
|
13
|
+
readonly FIRST_KEY_INDEX: number;
|
|
14
|
+
readonly IS_READ_ONLY: boolean;
|
|
15
|
+
transformArguments(...args: Args): Array<NodeRedisArgument>;
|
|
16
|
+
transformReply(reply: Reply): Reply;
|
|
17
|
+
}
|
|
18
|
+
type DialCacheNodeRedisScripts = {
|
|
19
|
+
readonly dialcacheRead: NodeRedisScript<[valueKey: string], string | null>;
|
|
20
|
+
readonly dialcacheReadTracked: NodeRedisScript<[valueKey: string, watermarkKey: string], string | null>;
|
|
21
|
+
readonly dialcacheWrite: NodeRedisScript<[
|
|
22
|
+
valueKey: string,
|
|
23
|
+
cacheTtlMs: number,
|
|
24
|
+
encoding: number,
|
|
25
|
+
payload: string | Buffer
|
|
26
|
+
], number>;
|
|
27
|
+
readonly dialcacheWriteTracked: NodeRedisScript<[
|
|
28
|
+
valueKey: string,
|
|
29
|
+
watermarkKey: string,
|
|
30
|
+
cacheTtlMs: number,
|
|
31
|
+
encoding: number,
|
|
32
|
+
payload: string | Buffer,
|
|
33
|
+
watermarkTtlFloorMs: number
|
|
34
|
+
], number>;
|
|
35
|
+
readonly dialcacheInvalidate: NodeRedisScript<[
|
|
36
|
+
watermarkKey: string,
|
|
37
|
+
futureBufferMs: number,
|
|
38
|
+
watermarkTtlFloorMs: number
|
|
39
|
+
], number>;
|
|
40
|
+
};
|
|
41
|
+
declare const dialcacheRedisScripts: DialCacheNodeRedisScripts;
|
|
42
|
+
interface NodeRedisScriptClient {
|
|
43
|
+
dialcacheRead(options: BufferReplyOptions, valueKey: string): Promise<Buffer | null>;
|
|
44
|
+
dialcacheReadTracked(options: BufferReplyOptions, valueKey: string, watermarkKey: string): Promise<Buffer | null>;
|
|
45
|
+
dialcacheWrite(valueKey: string, cacheTtlMs: number, encoding: number, payload: string | Buffer): Promise<number>;
|
|
46
|
+
dialcacheWriteTracked(valueKey: string, watermarkKey: string, cacheTtlMs: number, encoding: number, payload: string | Buffer, watermarkTtlFloorMs: number): Promise<number>;
|
|
47
|
+
dialcacheInvalidate(watermarkKey: string, futureBufferMs: number, watermarkTtlFloorMs: number): Promise<number>;
|
|
48
|
+
flushAll?(): Promise<unknown>;
|
|
49
|
+
}
|
|
50
|
+
declare function createNodeRedisDialCacheClient(client: NodeRedisScriptClient): DialCacheRedisClient;
|
|
51
|
+
|
|
52
|
+
export { type DialCacheNodeRedisScripts, createNodeRedisDialCacheClient, dialcacheRedisScripts };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { commandOptions } from 'redis';
|
|
2
|
+
import { k as DialCacheRedisClient } from './config-B7bGp-Xj.js';
|
|
3
|
+
import 'prom-client';
|
|
4
|
+
|
|
5
|
+
type BufferReplyOptions = ReturnType<typeof commandOptions<{
|
|
6
|
+
readonly returnBuffers: true;
|
|
7
|
+
}>>;
|
|
8
|
+
type NodeRedisArgument = string | Buffer;
|
|
9
|
+
interface NodeRedisScript<Args extends Array<unknown>, Reply> {
|
|
10
|
+
readonly SCRIPT: string;
|
|
11
|
+
readonly SHA1: string;
|
|
12
|
+
readonly NUMBER_OF_KEYS: number;
|
|
13
|
+
readonly FIRST_KEY_INDEX: number;
|
|
14
|
+
readonly IS_READ_ONLY: boolean;
|
|
15
|
+
transformArguments(...args: Args): Array<NodeRedisArgument>;
|
|
16
|
+
transformReply(reply: Reply): Reply;
|
|
17
|
+
}
|
|
18
|
+
type DialCacheNodeRedisScripts = {
|
|
19
|
+
readonly dialcacheRead: NodeRedisScript<[valueKey: string], string | null>;
|
|
20
|
+
readonly dialcacheReadTracked: NodeRedisScript<[valueKey: string, watermarkKey: string], string | null>;
|
|
21
|
+
readonly dialcacheWrite: NodeRedisScript<[
|
|
22
|
+
valueKey: string,
|
|
23
|
+
cacheTtlMs: number,
|
|
24
|
+
encoding: number,
|
|
25
|
+
payload: string | Buffer
|
|
26
|
+
], number>;
|
|
27
|
+
readonly dialcacheWriteTracked: NodeRedisScript<[
|
|
28
|
+
valueKey: string,
|
|
29
|
+
watermarkKey: string,
|
|
30
|
+
cacheTtlMs: number,
|
|
31
|
+
encoding: number,
|
|
32
|
+
payload: string | Buffer,
|
|
33
|
+
watermarkTtlFloorMs: number
|
|
34
|
+
], number>;
|
|
35
|
+
readonly dialcacheInvalidate: NodeRedisScript<[
|
|
36
|
+
watermarkKey: string,
|
|
37
|
+
futureBufferMs: number,
|
|
38
|
+
watermarkTtlFloorMs: number
|
|
39
|
+
], number>;
|
|
40
|
+
};
|
|
41
|
+
declare const dialcacheRedisScripts: DialCacheNodeRedisScripts;
|
|
42
|
+
interface NodeRedisScriptClient {
|
|
43
|
+
dialcacheRead(options: BufferReplyOptions, valueKey: string): Promise<Buffer | null>;
|
|
44
|
+
dialcacheReadTracked(options: BufferReplyOptions, valueKey: string, watermarkKey: string): Promise<Buffer | null>;
|
|
45
|
+
dialcacheWrite(valueKey: string, cacheTtlMs: number, encoding: number, payload: string | Buffer): Promise<number>;
|
|
46
|
+
dialcacheWriteTracked(valueKey: string, watermarkKey: string, cacheTtlMs: number, encoding: number, payload: string | Buffer, watermarkTtlFloorMs: number): Promise<number>;
|
|
47
|
+
dialcacheInvalidate(watermarkKey: string, futureBufferMs: number, watermarkTtlFloorMs: number): Promise<number>;
|
|
48
|
+
flushAll?(): Promise<unknown>;
|
|
49
|
+
}
|
|
50
|
+
declare function createNodeRedisDialCacheClient(client: NodeRedisScriptClient): DialCacheRedisClient;
|
|
51
|
+
|
|
52
|
+
export { type DialCacheNodeRedisScripts, createNodeRedisDialCacheClient, dialcacheRedisScripts };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeRedisPayload,
|
|
3
|
+
redisPayloadEncoding
|
|
4
|
+
} from "./chunk-BF6XTPTS.js";
|
|
5
|
+
import "./chunk-S5VCTJID.js";
|
|
6
|
+
import {
|
|
7
|
+
INVALIDATE_CACHE_SCRIPT,
|
|
8
|
+
READ_CACHE_SCRIPT,
|
|
9
|
+
READ_TRACKED_CACHE_SCRIPT,
|
|
10
|
+
WRITE_CACHE_SCRIPT,
|
|
11
|
+
WRITE_TRACKED_CACHE_SCRIPT
|
|
12
|
+
} from "./chunk-2TPHNNE7.js";
|
|
13
|
+
|
|
14
|
+
// src/node-redis.ts
|
|
15
|
+
import { commandOptions, defineScript } from "redis";
|
|
16
|
+
var bufferReplyOptions = commandOptions({ returnBuffers: true });
|
|
17
|
+
var readReply = (reply) => reply;
|
|
18
|
+
var integerReply = (reply) => reply;
|
|
19
|
+
function defineDialCacheScript(config) {
|
|
20
|
+
return defineScript(config);
|
|
21
|
+
}
|
|
22
|
+
var dialcacheRedisScripts = {
|
|
23
|
+
dialcacheRead: defineDialCacheScript({
|
|
24
|
+
SCRIPT: READ_CACHE_SCRIPT,
|
|
25
|
+
NUMBER_OF_KEYS: 1,
|
|
26
|
+
FIRST_KEY_INDEX: 0,
|
|
27
|
+
IS_READ_ONLY: true,
|
|
28
|
+
transformArguments(valueKey) {
|
|
29
|
+
return [valueKey];
|
|
30
|
+
},
|
|
31
|
+
transformReply: readReply
|
|
32
|
+
}),
|
|
33
|
+
dialcacheReadTracked: defineDialCacheScript({
|
|
34
|
+
SCRIPT: READ_TRACKED_CACHE_SCRIPT,
|
|
35
|
+
NUMBER_OF_KEYS: 2,
|
|
36
|
+
FIRST_KEY_INDEX: 0,
|
|
37
|
+
// Replica lag must not hide a newly-written invalidation watermark.
|
|
38
|
+
IS_READ_ONLY: false,
|
|
39
|
+
transformArguments(valueKey, watermarkKey) {
|
|
40
|
+
return [valueKey, watermarkKey];
|
|
41
|
+
},
|
|
42
|
+
transformReply: readReply
|
|
43
|
+
}),
|
|
44
|
+
dialcacheWrite: defineDialCacheScript({
|
|
45
|
+
SCRIPT: WRITE_CACHE_SCRIPT,
|
|
46
|
+
NUMBER_OF_KEYS: 1,
|
|
47
|
+
FIRST_KEY_INDEX: 0,
|
|
48
|
+
IS_READ_ONLY: false,
|
|
49
|
+
transformArguments(valueKey, cacheTtlMs, encoding, payload) {
|
|
50
|
+
return [valueKey, String(cacheTtlMs), String(encoding), payload];
|
|
51
|
+
},
|
|
52
|
+
transformReply: integerReply
|
|
53
|
+
}),
|
|
54
|
+
dialcacheWriteTracked: defineDialCacheScript({
|
|
55
|
+
SCRIPT: WRITE_TRACKED_CACHE_SCRIPT,
|
|
56
|
+
NUMBER_OF_KEYS: 2,
|
|
57
|
+
FIRST_KEY_INDEX: 0,
|
|
58
|
+
IS_READ_ONLY: false,
|
|
59
|
+
transformArguments(valueKey, watermarkKey, cacheTtlMs, encoding, payload, watermarkTtlFloorMs) {
|
|
60
|
+
return [valueKey, watermarkKey, String(cacheTtlMs), String(encoding), payload, String(watermarkTtlFloorMs)];
|
|
61
|
+
},
|
|
62
|
+
transformReply: integerReply
|
|
63
|
+
}),
|
|
64
|
+
dialcacheInvalidate: defineDialCacheScript({
|
|
65
|
+
SCRIPT: INVALIDATE_CACHE_SCRIPT,
|
|
66
|
+
NUMBER_OF_KEYS: 1,
|
|
67
|
+
FIRST_KEY_INDEX: 0,
|
|
68
|
+
IS_READ_ONLY: false,
|
|
69
|
+
transformArguments(watermarkKey, futureBufferMs, watermarkTtlFloorMs) {
|
|
70
|
+
return [watermarkKey, String(futureBufferMs), String(watermarkTtlFloorMs)];
|
|
71
|
+
},
|
|
72
|
+
transformReply: integerReply
|
|
73
|
+
})
|
|
74
|
+
};
|
|
75
|
+
function createNodeRedisDialCacheClient(client) {
|
|
76
|
+
return {
|
|
77
|
+
async read({ valueKey, watermarkKey }) {
|
|
78
|
+
const raw = watermarkKey === void 0 ? await client.dialcacheRead(bufferReplyOptions, valueKey) : await client.dialcacheReadTracked(bufferReplyOptions, valueKey, watermarkKey);
|
|
79
|
+
return raw === null ? null : decodeRedisPayload(raw);
|
|
80
|
+
},
|
|
81
|
+
async write(request) {
|
|
82
|
+
const { valueKey, watermarkKey, cacheTtlMs, value } = request;
|
|
83
|
+
const encodingByte = redisPayloadEncoding(value);
|
|
84
|
+
const result = watermarkKey === void 0 ? await client.dialcacheWrite(valueKey, cacheTtlMs, encodingByte, value) : await client.dialcacheWriteTracked(
|
|
85
|
+
valueKey,
|
|
86
|
+
watermarkKey,
|
|
87
|
+
cacheTtlMs,
|
|
88
|
+
encodingByte,
|
|
89
|
+
value,
|
|
90
|
+
request.watermarkTtlFloorMs
|
|
91
|
+
);
|
|
92
|
+
return result === 1;
|
|
93
|
+
},
|
|
94
|
+
async invalidate({ watermarkKey, futureBufferMs, watermarkTtlFloorMs }) {
|
|
95
|
+
await client.dialcacheInvalidate(watermarkKey, futureBufferMs, watermarkTtlFloorMs);
|
|
96
|
+
},
|
|
97
|
+
async flushAll() {
|
|
98
|
+
const cluster = clusterCommands(client);
|
|
99
|
+
if (cluster === null) {
|
|
100
|
+
if (client.flushAll === void 0) {
|
|
101
|
+
throw new Error("Node-redis client does not implement flushAll");
|
|
102
|
+
}
|
|
103
|
+
await client.flushAll();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (cluster.masters.length === 0) {
|
|
107
|
+
throw new Error("Node-redis cluster has no connected masters");
|
|
108
|
+
}
|
|
109
|
+
await Promise.all(
|
|
110
|
+
cluster.masters.map(async (master) => {
|
|
111
|
+
const node = await cluster.nodeClient(master);
|
|
112
|
+
await node.flushAll();
|
|
113
|
+
})
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function clusterCommands(client) {
|
|
119
|
+
if (!("masters" in client) || !("nodeClient" in client) || !Array.isArray(client.masters)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return client;
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
createNodeRedisDialCacheClient,
|
|
126
|
+
dialcacheRedisScripts
|
|
127
|
+
};
|