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,197 @@
|
|
|
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/redis-protocol.ts
|
|
21
|
+
var redis_protocol_exports = {};
|
|
22
|
+
__export(redis_protocol_exports, {
|
|
23
|
+
INVALIDATE_CACHE_SCRIPT: () => INVALIDATE_CACHE_SCRIPT,
|
|
24
|
+
READ_CACHE_SCRIPT: () => READ_CACHE_SCRIPT,
|
|
25
|
+
READ_TRACKED_CACHE_SCRIPT: () => READ_TRACKED_CACHE_SCRIPT,
|
|
26
|
+
REDIS_ENCODING_BINARY: () => REDIS_ENCODING_BINARY,
|
|
27
|
+
REDIS_ENCODING_UTF8: () => REDIS_ENCODING_UTF8,
|
|
28
|
+
REDIS_FRAME_VERSION: () => REDIS_FRAME_VERSION,
|
|
29
|
+
WRITE_CACHE_SCRIPT: () => WRITE_CACHE_SCRIPT,
|
|
30
|
+
WRITE_TRACKED_CACHE_SCRIPT: () => WRITE_TRACKED_CACHE_SCRIPT
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(redis_protocol_exports);
|
|
33
|
+
|
|
34
|
+
// src/internal/redis-scripts.ts
|
|
35
|
+
var REDIS_FRAME_VERSION = 1;
|
|
36
|
+
var REDIS_ENCODING_UTF8 = 0;
|
|
37
|
+
var REDIS_ENCODING_BINARY = 1;
|
|
38
|
+
var WATERMARK_TTL_MARGIN_MS = 6e4;
|
|
39
|
+
var PARSE_WATERMARK_LUA = String.raw`local function parse_watermark(raw)
|
|
40
|
+
if not string.match(raw, "^%d+$") and not string.match(raw, "^%d+%.%d+$") then
|
|
41
|
+
return nil
|
|
42
|
+
end
|
|
43
|
+
local value = tonumber(raw)
|
|
44
|
+
if not value or value >= math.huge then
|
|
45
|
+
return nil
|
|
46
|
+
end
|
|
47
|
+
return value
|
|
48
|
+
end`;
|
|
49
|
+
var CEIL_FINITE_NUMBER_LUA = String.raw`local function ceil_finite_number(raw)
|
|
50
|
+
local value = tonumber(raw)
|
|
51
|
+
if not value or value ~= value or value >= math.huge or value <= -math.huge then
|
|
52
|
+
return nil
|
|
53
|
+
end
|
|
54
|
+
return math.ceil(value)
|
|
55
|
+
end`;
|
|
56
|
+
var READ_FRAME_LUA = String.raw`local value = redis.call("GET", KEYS[1])
|
|
57
|
+
if not value or string.len(value) < 10 then
|
|
58
|
+
return false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if string.byte(value, 1) ~= ${REDIS_FRAME_VERSION} then
|
|
62
|
+
return false
|
|
63
|
+
end`;
|
|
64
|
+
var RETURN_PAYLOAD_LUA = String.raw`return string.sub(value, 10)`;
|
|
65
|
+
var VALIDATE_WRITE_ARGUMENTS_LUA = String.raw`local cache_ttl_ms = ceil_finite_number(ARGV[1])
|
|
66
|
+
local encoding = tonumber(ARGV[2])
|
|
67
|
+
if not cache_ttl_ms or cache_ttl_ms <= 0 then
|
|
68
|
+
return redis.error_reply("ERR invalid DialCache TTL")
|
|
69
|
+
end
|
|
70
|
+
if not encoding or (encoding ~= ${REDIS_ENCODING_UTF8} and encoding ~= ${REDIS_ENCODING_BINARY}) then
|
|
71
|
+
return redis.error_reply("ERR invalid DialCache payload encoding")
|
|
72
|
+
end`;
|
|
73
|
+
var REDIS_TIME_LUA = String.raw`local redis_time = redis.call("TIME")
|
|
74
|
+
local now_ms = tonumber(redis_time[1]) * 1000 + math.floor(tonumber(redis_time[2]) / 1000)`;
|
|
75
|
+
var WRITE_FRAME_LUA = String.raw`local frame = string.char(${REDIS_FRAME_VERSION})
|
|
76
|
+
.. struct.pack(">I8", now_ms)
|
|
77
|
+
.. string.char(encoding)
|
|
78
|
+
.. ARGV[3]
|
|
79
|
+
redis.call("SET", KEYS[1], frame, "PX", cache_ttl_ms)`;
|
|
80
|
+
var READ_CACHE_SCRIPT = [READ_FRAME_LUA, RETURN_PAYLOAD_LUA].join("\n\n");
|
|
81
|
+
var READ_TRACKED_CACHE_SCRIPT = [
|
|
82
|
+
PARSE_WATERMARK_LUA,
|
|
83
|
+
READ_FRAME_LUA,
|
|
84
|
+
String.raw`local raw_watermark = redis.call("GET", KEYS[2])
|
|
85
|
+
if not raw_watermark then
|
|
86
|
+
return false
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
local watermark = parse_watermark(raw_watermark)
|
|
90
|
+
if not watermark then
|
|
91
|
+
return false
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
local created_at = struct.unpack(">I8", string.sub(value, 2, 9))
|
|
95
|
+
if created_at <= watermark then
|
|
96
|
+
return false
|
|
97
|
+
end`,
|
|
98
|
+
RETURN_PAYLOAD_LUA
|
|
99
|
+
].join("\n\n");
|
|
100
|
+
var WRITE_CACHE_SCRIPT = [
|
|
101
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
102
|
+
VALIDATE_WRITE_ARGUMENTS_LUA,
|
|
103
|
+
REDIS_TIME_LUA,
|
|
104
|
+
WRITE_FRAME_LUA,
|
|
105
|
+
"return 1"
|
|
106
|
+
].join("\n\n");
|
|
107
|
+
var WRITE_TRACKED_CACHE_SCRIPT = [
|
|
108
|
+
PARSE_WATERMARK_LUA,
|
|
109
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
110
|
+
VALIDATE_WRITE_ARGUMENTS_LUA,
|
|
111
|
+
String.raw`local watermark_ttl_floor_ms = ceil_finite_number(ARGV[4])
|
|
112
|
+
if not watermark_ttl_floor_ms or watermark_ttl_floor_ms <= 0 then
|
|
113
|
+
return redis.error_reply("ERR invalid DialCache watermark TTL")
|
|
114
|
+
end`,
|
|
115
|
+
REDIS_TIME_LUA,
|
|
116
|
+
String.raw`local raw_watermark = redis.call("GET", KEYS[2])
|
|
117
|
+
local watermark = 0
|
|
118
|
+
if raw_watermark then
|
|
119
|
+
watermark = parse_watermark(raw_watermark)
|
|
120
|
+
if not watermark then
|
|
121
|
+
return redis.error_reply("ERR invalid DialCache watermark")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if watermark >= now_ms then
|
|
126
|
+
return 0
|
|
127
|
+
end`,
|
|
128
|
+
WRITE_FRAME_LUA,
|
|
129
|
+
String.raw`local desired_ttl_ms = math.max(watermark_ttl_floor_ms, cache_ttl_ms + ${WATERMARK_TTL_MARGIN_MS})
|
|
130
|
+
if not raw_watermark then
|
|
131
|
+
redis.call("SET", KEYS[2], "0", "PX", desired_ttl_ms)
|
|
132
|
+
else
|
|
133
|
+
local current_ttl_ms = redis.call("PTTL", KEYS[2])
|
|
134
|
+
if current_ttl_ms == -2 then
|
|
135
|
+
redis.call("SET", KEYS[2], raw_watermark, "PX", desired_ttl_ms)
|
|
136
|
+
elseif current_ttl_ms ~= -1 and current_ttl_ms < desired_ttl_ms then
|
|
137
|
+
redis.call("PEXPIRE", KEYS[2], desired_ttl_ms)
|
|
138
|
+
end
|
|
139
|
+
end`,
|
|
140
|
+
"return 1"
|
|
141
|
+
].join("\n\n");
|
|
142
|
+
var INVALIDATE_CACHE_SCRIPT = [
|
|
143
|
+
PARSE_WATERMARK_LUA,
|
|
144
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
145
|
+
String.raw`local future_buffer_ms = ceil_finite_number(ARGV[1])
|
|
146
|
+
local watermark_ttl_floor_ms = ceil_finite_number(ARGV[2])
|
|
147
|
+
if not future_buffer_ms or future_buffer_ms < 0 then
|
|
148
|
+
return redis.error_reply("ERR invalid DialCache future buffer")
|
|
149
|
+
end
|
|
150
|
+
if not watermark_ttl_floor_ms or watermark_ttl_floor_ms <= 0 then
|
|
151
|
+
return redis.error_reply("ERR invalid DialCache watermark TTL")
|
|
152
|
+
end`,
|
|
153
|
+
REDIS_TIME_LUA,
|
|
154
|
+
String.raw`local proposed_watermark = now_ms + future_buffer_ms
|
|
155
|
+
local raw_watermark = redis.call("GET", KEYS[1])
|
|
156
|
+
local current_watermark = 0
|
|
157
|
+
|
|
158
|
+
if raw_watermark then
|
|
159
|
+
local parsed_watermark = parse_watermark(raw_watermark)
|
|
160
|
+
if parsed_watermark then
|
|
161
|
+
current_watermark = parsed_watermark
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
local watermark = math.ceil(math.max(current_watermark, proposed_watermark))
|
|
166
|
+
local current_ttl_ms = -2
|
|
167
|
+
if raw_watermark then
|
|
168
|
+
current_ttl_ms = redis.call("PTTL", KEYS[1])
|
|
169
|
+
end
|
|
170
|
+
local desired_ttl_ms = math.max(
|
|
171
|
+
watermark_ttl_floor_ms,
|
|
172
|
+
future_buffer_ms + ${WATERMARK_TTL_MARGIN_MS},
|
|
173
|
+
watermark - now_ms + ${WATERMARK_TTL_MARGIN_MS}
|
|
174
|
+
)
|
|
175
|
+
if current_ttl_ms > desired_ttl_ms then
|
|
176
|
+
desired_ttl_ms = current_ttl_ms
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
local encoded_watermark = string.format("%.0f", watermark)
|
|
180
|
+
if current_ttl_ms == -1 then
|
|
181
|
+
redis.call("SET", KEYS[1], encoded_watermark)
|
|
182
|
+
else
|
|
183
|
+
redis.call("SET", KEYS[1], encoded_watermark, "PX", desired_ttl_ms)
|
|
184
|
+
end`,
|
|
185
|
+
"return 1"
|
|
186
|
+
].join("\n\n");
|
|
187
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
188
|
+
0 && (module.exports = {
|
|
189
|
+
INVALIDATE_CACHE_SCRIPT,
|
|
190
|
+
READ_CACHE_SCRIPT,
|
|
191
|
+
READ_TRACKED_CACHE_SCRIPT,
|
|
192
|
+
REDIS_ENCODING_BINARY,
|
|
193
|
+
REDIS_ENCODING_UTF8,
|
|
194
|
+
REDIS_FRAME_VERSION,
|
|
195
|
+
WRITE_CACHE_SCRIPT,
|
|
196
|
+
WRITE_TRACKED_CACHE_SCRIPT
|
|
197
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const REDIS_FRAME_VERSION = 1;
|
|
2
|
+
declare const REDIS_ENCODING_UTF8 = 0;
|
|
3
|
+
declare const REDIS_ENCODING_BINARY = 1;
|
|
4
|
+
declare const READ_CACHE_SCRIPT: string;
|
|
5
|
+
declare const READ_TRACKED_CACHE_SCRIPT: string;
|
|
6
|
+
declare const WRITE_CACHE_SCRIPT: string;
|
|
7
|
+
declare const WRITE_TRACKED_CACHE_SCRIPT: string;
|
|
8
|
+
declare const INVALIDATE_CACHE_SCRIPT: string;
|
|
9
|
+
|
|
10
|
+
export { INVALIDATE_CACHE_SCRIPT, READ_CACHE_SCRIPT, READ_TRACKED_CACHE_SCRIPT, REDIS_ENCODING_BINARY, REDIS_ENCODING_UTF8, REDIS_FRAME_VERSION, WRITE_CACHE_SCRIPT, WRITE_TRACKED_CACHE_SCRIPT };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const REDIS_FRAME_VERSION = 1;
|
|
2
|
+
declare const REDIS_ENCODING_UTF8 = 0;
|
|
3
|
+
declare const REDIS_ENCODING_BINARY = 1;
|
|
4
|
+
declare const READ_CACHE_SCRIPT: string;
|
|
5
|
+
declare const READ_TRACKED_CACHE_SCRIPT: string;
|
|
6
|
+
declare const WRITE_CACHE_SCRIPT: string;
|
|
7
|
+
declare const WRITE_TRACKED_CACHE_SCRIPT: string;
|
|
8
|
+
declare const INVALIDATE_CACHE_SCRIPT: string;
|
|
9
|
+
|
|
10
|
+
export { INVALIDATE_CACHE_SCRIPT, READ_CACHE_SCRIPT, READ_TRACKED_CACHE_SCRIPT, REDIS_ENCODING_BINARY, REDIS_ENCODING_UTF8, REDIS_FRAME_VERSION, WRITE_CACHE_SCRIPT, WRITE_TRACKED_CACHE_SCRIPT };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INVALIDATE_CACHE_SCRIPT,
|
|
3
|
+
READ_CACHE_SCRIPT,
|
|
4
|
+
READ_TRACKED_CACHE_SCRIPT,
|
|
5
|
+
REDIS_ENCODING_BINARY,
|
|
6
|
+
REDIS_ENCODING_UTF8,
|
|
7
|
+
REDIS_FRAME_VERSION,
|
|
8
|
+
WRITE_CACHE_SCRIPT,
|
|
9
|
+
WRITE_TRACKED_CACHE_SCRIPT
|
|
10
|
+
} from "./chunk-2TPHNNE7.js";
|
|
11
|
+
export {
|
|
12
|
+
INVALIDATE_CACHE_SCRIPT,
|
|
13
|
+
READ_CACHE_SCRIPT,
|
|
14
|
+
READ_TRACKED_CACHE_SCRIPT,
|
|
15
|
+
REDIS_ENCODING_BINARY,
|
|
16
|
+
REDIS_ENCODING_UTF8,
|
|
17
|
+
REDIS_FRAME_VERSION,
|
|
18
|
+
WRITE_CACHE_SCRIPT,
|
|
19
|
+
WRITE_TRACKED_CACHE_SCRIPT
|
|
20
|
+
};
|
|
@@ -0,0 +1,299 @@
|
|
|
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/valkey-glide.ts
|
|
21
|
+
var valkey_glide_exports = {};
|
|
22
|
+
__export(valkey_glide_exports, {
|
|
23
|
+
createValkeyGlideDialCacheClient: () => createValkeyGlideDialCacheClient
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(valkey_glide_exports);
|
|
26
|
+
var import_valkey_glide = require("@valkey/valkey-glide");
|
|
27
|
+
|
|
28
|
+
// src/redis-client.ts
|
|
29
|
+
var DialCacheRedisPayloadError = class extends Error {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "DialCacheRedisPayloadError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var DialCacheRedisPayloadEncodingError = class extends Error {
|
|
36
|
+
constructor(message) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = "DialCacheRedisPayloadEncodingError";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/internal/redis-scripts.ts
|
|
43
|
+
var REDIS_FRAME_VERSION = 1;
|
|
44
|
+
var REDIS_ENCODING_UTF8 = 0;
|
|
45
|
+
var REDIS_ENCODING_BINARY = 1;
|
|
46
|
+
var WATERMARK_TTL_MARGIN_MS = 6e4;
|
|
47
|
+
var PARSE_WATERMARK_LUA = String.raw`local function parse_watermark(raw)
|
|
48
|
+
if not string.match(raw, "^%d+$") and not string.match(raw, "^%d+%.%d+$") then
|
|
49
|
+
return nil
|
|
50
|
+
end
|
|
51
|
+
local value = tonumber(raw)
|
|
52
|
+
if not value or value >= math.huge then
|
|
53
|
+
return nil
|
|
54
|
+
end
|
|
55
|
+
return value
|
|
56
|
+
end`;
|
|
57
|
+
var CEIL_FINITE_NUMBER_LUA = String.raw`local function ceil_finite_number(raw)
|
|
58
|
+
local value = tonumber(raw)
|
|
59
|
+
if not value or value ~= value or value >= math.huge or value <= -math.huge then
|
|
60
|
+
return nil
|
|
61
|
+
end
|
|
62
|
+
return math.ceil(value)
|
|
63
|
+
end`;
|
|
64
|
+
var READ_FRAME_LUA = String.raw`local value = redis.call("GET", KEYS[1])
|
|
65
|
+
if not value or string.len(value) < 10 then
|
|
66
|
+
return false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if string.byte(value, 1) ~= ${REDIS_FRAME_VERSION} then
|
|
70
|
+
return false
|
|
71
|
+
end`;
|
|
72
|
+
var RETURN_PAYLOAD_LUA = String.raw`return string.sub(value, 10)`;
|
|
73
|
+
var VALIDATE_WRITE_ARGUMENTS_LUA = String.raw`local cache_ttl_ms = ceil_finite_number(ARGV[1])
|
|
74
|
+
local encoding = tonumber(ARGV[2])
|
|
75
|
+
if not cache_ttl_ms or cache_ttl_ms <= 0 then
|
|
76
|
+
return redis.error_reply("ERR invalid DialCache TTL")
|
|
77
|
+
end
|
|
78
|
+
if not encoding or (encoding ~= ${REDIS_ENCODING_UTF8} and encoding ~= ${REDIS_ENCODING_BINARY}) then
|
|
79
|
+
return redis.error_reply("ERR invalid DialCache payload encoding")
|
|
80
|
+
end`;
|
|
81
|
+
var REDIS_TIME_LUA = String.raw`local redis_time = redis.call("TIME")
|
|
82
|
+
local now_ms = tonumber(redis_time[1]) * 1000 + math.floor(tonumber(redis_time[2]) / 1000)`;
|
|
83
|
+
var WRITE_FRAME_LUA = String.raw`local frame = string.char(${REDIS_FRAME_VERSION})
|
|
84
|
+
.. struct.pack(">I8", now_ms)
|
|
85
|
+
.. string.char(encoding)
|
|
86
|
+
.. ARGV[3]
|
|
87
|
+
redis.call("SET", KEYS[1], frame, "PX", cache_ttl_ms)`;
|
|
88
|
+
var READ_CACHE_SCRIPT = [READ_FRAME_LUA, RETURN_PAYLOAD_LUA].join("\n\n");
|
|
89
|
+
var READ_TRACKED_CACHE_SCRIPT = [
|
|
90
|
+
PARSE_WATERMARK_LUA,
|
|
91
|
+
READ_FRAME_LUA,
|
|
92
|
+
String.raw`local raw_watermark = redis.call("GET", KEYS[2])
|
|
93
|
+
if not raw_watermark then
|
|
94
|
+
return false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
local watermark = parse_watermark(raw_watermark)
|
|
98
|
+
if not watermark then
|
|
99
|
+
return false
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
local created_at = struct.unpack(">I8", string.sub(value, 2, 9))
|
|
103
|
+
if created_at <= watermark then
|
|
104
|
+
return false
|
|
105
|
+
end`,
|
|
106
|
+
RETURN_PAYLOAD_LUA
|
|
107
|
+
].join("\n\n");
|
|
108
|
+
var WRITE_CACHE_SCRIPT = [
|
|
109
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
110
|
+
VALIDATE_WRITE_ARGUMENTS_LUA,
|
|
111
|
+
REDIS_TIME_LUA,
|
|
112
|
+
WRITE_FRAME_LUA,
|
|
113
|
+
"return 1"
|
|
114
|
+
].join("\n\n");
|
|
115
|
+
var WRITE_TRACKED_CACHE_SCRIPT = [
|
|
116
|
+
PARSE_WATERMARK_LUA,
|
|
117
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
118
|
+
VALIDATE_WRITE_ARGUMENTS_LUA,
|
|
119
|
+
String.raw`local watermark_ttl_floor_ms = ceil_finite_number(ARGV[4])
|
|
120
|
+
if not watermark_ttl_floor_ms or watermark_ttl_floor_ms <= 0 then
|
|
121
|
+
return redis.error_reply("ERR invalid DialCache watermark TTL")
|
|
122
|
+
end`,
|
|
123
|
+
REDIS_TIME_LUA,
|
|
124
|
+
String.raw`local raw_watermark = redis.call("GET", KEYS[2])
|
|
125
|
+
local watermark = 0
|
|
126
|
+
if raw_watermark then
|
|
127
|
+
watermark = parse_watermark(raw_watermark)
|
|
128
|
+
if not watermark then
|
|
129
|
+
return redis.error_reply("ERR invalid DialCache watermark")
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if watermark >= now_ms then
|
|
134
|
+
return 0
|
|
135
|
+
end`,
|
|
136
|
+
WRITE_FRAME_LUA,
|
|
137
|
+
String.raw`local desired_ttl_ms = math.max(watermark_ttl_floor_ms, cache_ttl_ms + ${WATERMARK_TTL_MARGIN_MS})
|
|
138
|
+
if not raw_watermark then
|
|
139
|
+
redis.call("SET", KEYS[2], "0", "PX", desired_ttl_ms)
|
|
140
|
+
else
|
|
141
|
+
local current_ttl_ms = redis.call("PTTL", KEYS[2])
|
|
142
|
+
if current_ttl_ms == -2 then
|
|
143
|
+
redis.call("SET", KEYS[2], raw_watermark, "PX", desired_ttl_ms)
|
|
144
|
+
elseif current_ttl_ms ~= -1 and current_ttl_ms < desired_ttl_ms then
|
|
145
|
+
redis.call("PEXPIRE", KEYS[2], desired_ttl_ms)
|
|
146
|
+
end
|
|
147
|
+
end`,
|
|
148
|
+
"return 1"
|
|
149
|
+
].join("\n\n");
|
|
150
|
+
var INVALIDATE_CACHE_SCRIPT = [
|
|
151
|
+
PARSE_WATERMARK_LUA,
|
|
152
|
+
CEIL_FINITE_NUMBER_LUA,
|
|
153
|
+
String.raw`local future_buffer_ms = ceil_finite_number(ARGV[1])
|
|
154
|
+
local watermark_ttl_floor_ms = ceil_finite_number(ARGV[2])
|
|
155
|
+
if not future_buffer_ms or future_buffer_ms < 0 then
|
|
156
|
+
return redis.error_reply("ERR invalid DialCache future buffer")
|
|
157
|
+
end
|
|
158
|
+
if not watermark_ttl_floor_ms or watermark_ttl_floor_ms <= 0 then
|
|
159
|
+
return redis.error_reply("ERR invalid DialCache watermark TTL")
|
|
160
|
+
end`,
|
|
161
|
+
REDIS_TIME_LUA,
|
|
162
|
+
String.raw`local proposed_watermark = now_ms + future_buffer_ms
|
|
163
|
+
local raw_watermark = redis.call("GET", KEYS[1])
|
|
164
|
+
local current_watermark = 0
|
|
165
|
+
|
|
166
|
+
if raw_watermark then
|
|
167
|
+
local parsed_watermark = parse_watermark(raw_watermark)
|
|
168
|
+
if parsed_watermark then
|
|
169
|
+
current_watermark = parsed_watermark
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
local watermark = math.ceil(math.max(current_watermark, proposed_watermark))
|
|
174
|
+
local current_ttl_ms = -2
|
|
175
|
+
if raw_watermark then
|
|
176
|
+
current_ttl_ms = redis.call("PTTL", KEYS[1])
|
|
177
|
+
end
|
|
178
|
+
local desired_ttl_ms = math.max(
|
|
179
|
+
watermark_ttl_floor_ms,
|
|
180
|
+
future_buffer_ms + ${WATERMARK_TTL_MARGIN_MS},
|
|
181
|
+
watermark - now_ms + ${WATERMARK_TTL_MARGIN_MS}
|
|
182
|
+
)
|
|
183
|
+
if current_ttl_ms > desired_ttl_ms then
|
|
184
|
+
desired_ttl_ms = current_ttl_ms
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
local encoded_watermark = string.format("%.0f", watermark)
|
|
188
|
+
if current_ttl_ms == -1 then
|
|
189
|
+
redis.call("SET", KEYS[1], encoded_watermark)
|
|
190
|
+
else
|
|
191
|
+
redis.call("SET", KEYS[1], encoded_watermark, "PX", desired_ttl_ms)
|
|
192
|
+
end`,
|
|
193
|
+
"return 1"
|
|
194
|
+
].join("\n\n");
|
|
195
|
+
|
|
196
|
+
// src/internal/redis-payload.ts
|
|
197
|
+
function redisPayloadEncoding(value) {
|
|
198
|
+
return Buffer.isBuffer(value) ? REDIS_ENCODING_BINARY : REDIS_ENCODING_UTF8;
|
|
199
|
+
}
|
|
200
|
+
function decodeRedisPayload(raw) {
|
|
201
|
+
if (raw.length === 0) {
|
|
202
|
+
throw new DialCacheRedisPayloadError("Invalid DialCache Redis payload");
|
|
203
|
+
}
|
|
204
|
+
const encoding = raw[0];
|
|
205
|
+
const payload = raw.subarray(1);
|
|
206
|
+
if (encoding === REDIS_ENCODING_UTF8) {
|
|
207
|
+
return payload.toString("utf8");
|
|
208
|
+
}
|
|
209
|
+
if (encoding === REDIS_ENCODING_BINARY) {
|
|
210
|
+
return payload;
|
|
211
|
+
}
|
|
212
|
+
throw new DialCacheRedisPayloadEncodingError("Invalid DialCache Redis payload encoding");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/valkey-glide.ts
|
|
216
|
+
function createValkeyGlideDialCacheClient(client) {
|
|
217
|
+
const scripts = {
|
|
218
|
+
read: new import_valkey_glide.Script(READ_CACHE_SCRIPT),
|
|
219
|
+
readTracked: new import_valkey_glide.Script(READ_TRACKED_CACHE_SCRIPT),
|
|
220
|
+
write: new import_valkey_glide.Script(WRITE_CACHE_SCRIPT),
|
|
221
|
+
writeTracked: new import_valkey_glide.Script(WRITE_TRACKED_CACHE_SCRIPT),
|
|
222
|
+
invalidate: new import_valkey_glide.Script(INVALIDATE_CACHE_SCRIPT)
|
|
223
|
+
};
|
|
224
|
+
let disposed = false;
|
|
225
|
+
let activeInvocations = 0;
|
|
226
|
+
const invoke = async (script, keys, args = []) => {
|
|
227
|
+
if (disposed) {
|
|
228
|
+
throw new Error("Valkey GLIDE DialCache client is disposed");
|
|
229
|
+
}
|
|
230
|
+
activeInvocations += 1;
|
|
231
|
+
try {
|
|
232
|
+
return await client.invokeScript(script, { keys, args, decoder: import_valkey_glide.Decoder.Bytes });
|
|
233
|
+
} finally {
|
|
234
|
+
activeInvocations -= 1;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
async read({ valueKey, watermarkKey }) {
|
|
239
|
+
const raw = watermarkKey === void 0 ? await invoke(scripts.read, [valueKey]) : await invoke(scripts.readTracked, [valueKey, watermarkKey]);
|
|
240
|
+
if (raw === null) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
if (!Buffer.isBuffer(raw)) {
|
|
244
|
+
throw new DialCacheRedisPayloadError("Invalid DialCache Redis payload reply");
|
|
245
|
+
}
|
|
246
|
+
return decodeRedisPayload(raw);
|
|
247
|
+
},
|
|
248
|
+
async write(request) {
|
|
249
|
+
const { valueKey, watermarkKey, cacheTtlMs, value } = request;
|
|
250
|
+
const encoding = redisPayloadEncoding(value);
|
|
251
|
+
const raw = watermarkKey === void 0 ? await invoke(scripts.write, [valueKey], [String(cacheTtlMs), String(encoding), value]) : await invoke(
|
|
252
|
+
scripts.writeTracked,
|
|
253
|
+
[valueKey, watermarkKey],
|
|
254
|
+
[String(cacheTtlMs), String(encoding), value, String(request.watermarkTtlFloorMs)]
|
|
255
|
+
);
|
|
256
|
+
return integerReply(raw, "write") === 1;
|
|
257
|
+
},
|
|
258
|
+
async invalidate({ watermarkKey, futureBufferMs, watermarkTtlFloorMs }) {
|
|
259
|
+
const raw = await invoke(
|
|
260
|
+
scripts.invalidate,
|
|
261
|
+
[watermarkKey],
|
|
262
|
+
[String(futureBufferMs), String(watermarkTtlFloorMs)]
|
|
263
|
+
);
|
|
264
|
+
integerReply(raw, "invalidate");
|
|
265
|
+
},
|
|
266
|
+
async flushAll() {
|
|
267
|
+
if (disposed) {
|
|
268
|
+
throw new Error("Valkey GLIDE DialCache client is disposed");
|
|
269
|
+
}
|
|
270
|
+
if (client instanceof import_valkey_glide.GlideClusterClient) {
|
|
271
|
+
await client.flushall({ route: "allPrimaries" });
|
|
272
|
+
} else {
|
|
273
|
+
await client.flushall();
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
dispose() {
|
|
277
|
+
if (disposed) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (activeInvocations > 0) {
|
|
281
|
+
throw new Error("Cannot dispose Valkey GLIDE DialCache client while operations are in flight");
|
|
282
|
+
}
|
|
283
|
+
disposed = true;
|
|
284
|
+
for (const script of Object.values(scripts)) {
|
|
285
|
+
script.release();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function integerReply(reply, operation) {
|
|
291
|
+
if (typeof reply !== "number") {
|
|
292
|
+
throw new Error(`Invalid DialCache Redis ${operation} reply`);
|
|
293
|
+
}
|
|
294
|
+
return reply;
|
|
295
|
+
}
|
|
296
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
297
|
+
0 && (module.exports = {
|
|
298
|
+
createValkeyGlideDialCacheClient
|
|
299
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GlideClient, GlideClusterClient } from '@valkey/valkey-glide';
|
|
2
|
+
import { k as DialCacheRedisClient } from './config-B7bGp-Xj.cjs';
|
|
3
|
+
import 'prom-client';
|
|
4
|
+
|
|
5
|
+
type SupportedValkeyGlideClient = GlideClient | GlideClusterClient;
|
|
6
|
+
interface ValkeyGlideDialCacheClient extends DialCacheRedisClient {
|
|
7
|
+
/** Release the adapter-owned GLIDE Script handles. Does not close the wrapped GLIDE client. */
|
|
8
|
+
dispose(): void;
|
|
9
|
+
}
|
|
10
|
+
declare function createValkeyGlideDialCacheClient(client: SupportedValkeyGlideClient): ValkeyGlideDialCacheClient;
|
|
11
|
+
|
|
12
|
+
export { type ValkeyGlideDialCacheClient, createValkeyGlideDialCacheClient };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GlideClient, GlideClusterClient } from '@valkey/valkey-glide';
|
|
2
|
+
import { k as DialCacheRedisClient } from './config-B7bGp-Xj.js';
|
|
3
|
+
import 'prom-client';
|
|
4
|
+
|
|
5
|
+
type SupportedValkeyGlideClient = GlideClient | GlideClusterClient;
|
|
6
|
+
interface ValkeyGlideDialCacheClient extends DialCacheRedisClient {
|
|
7
|
+
/** Release the adapter-owned GLIDE Script handles. Does not close the wrapped GLIDE client. */
|
|
8
|
+
dispose(): void;
|
|
9
|
+
}
|
|
10
|
+
declare function createValkeyGlideDialCacheClient(client: SupportedValkeyGlideClient): ValkeyGlideDialCacheClient;
|
|
11
|
+
|
|
12
|
+
export { type ValkeyGlideDialCacheClient, createValkeyGlideDialCacheClient };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decodeRedisPayload,
|
|
3
|
+
redisPayloadEncoding
|
|
4
|
+
} from "./chunk-BF6XTPTS.js";
|
|
5
|
+
import {
|
|
6
|
+
DialCacheRedisPayloadError
|
|
7
|
+
} from "./chunk-S5VCTJID.js";
|
|
8
|
+
import {
|
|
9
|
+
INVALIDATE_CACHE_SCRIPT,
|
|
10
|
+
READ_CACHE_SCRIPT,
|
|
11
|
+
READ_TRACKED_CACHE_SCRIPT,
|
|
12
|
+
WRITE_CACHE_SCRIPT,
|
|
13
|
+
WRITE_TRACKED_CACHE_SCRIPT
|
|
14
|
+
} from "./chunk-2TPHNNE7.js";
|
|
15
|
+
|
|
16
|
+
// src/valkey-glide.ts
|
|
17
|
+
import {
|
|
18
|
+
Decoder,
|
|
19
|
+
GlideClusterClient,
|
|
20
|
+
Script
|
|
21
|
+
} from "@valkey/valkey-glide";
|
|
22
|
+
function createValkeyGlideDialCacheClient(client) {
|
|
23
|
+
const scripts = {
|
|
24
|
+
read: new Script(READ_CACHE_SCRIPT),
|
|
25
|
+
readTracked: new Script(READ_TRACKED_CACHE_SCRIPT),
|
|
26
|
+
write: new Script(WRITE_CACHE_SCRIPT),
|
|
27
|
+
writeTracked: new Script(WRITE_TRACKED_CACHE_SCRIPT),
|
|
28
|
+
invalidate: new Script(INVALIDATE_CACHE_SCRIPT)
|
|
29
|
+
};
|
|
30
|
+
let disposed = false;
|
|
31
|
+
let activeInvocations = 0;
|
|
32
|
+
const invoke = async (script, keys, args = []) => {
|
|
33
|
+
if (disposed) {
|
|
34
|
+
throw new Error("Valkey GLIDE DialCache client is disposed");
|
|
35
|
+
}
|
|
36
|
+
activeInvocations += 1;
|
|
37
|
+
try {
|
|
38
|
+
return await client.invokeScript(script, { keys, args, decoder: Decoder.Bytes });
|
|
39
|
+
} finally {
|
|
40
|
+
activeInvocations -= 1;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
async read({ valueKey, watermarkKey }) {
|
|
45
|
+
const raw = watermarkKey === void 0 ? await invoke(scripts.read, [valueKey]) : await invoke(scripts.readTracked, [valueKey, watermarkKey]);
|
|
46
|
+
if (raw === null) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (!Buffer.isBuffer(raw)) {
|
|
50
|
+
throw new DialCacheRedisPayloadError("Invalid DialCache Redis payload reply");
|
|
51
|
+
}
|
|
52
|
+
return decodeRedisPayload(raw);
|
|
53
|
+
},
|
|
54
|
+
async write(request) {
|
|
55
|
+
const { valueKey, watermarkKey, cacheTtlMs, value } = request;
|
|
56
|
+
const encoding = redisPayloadEncoding(value);
|
|
57
|
+
const raw = watermarkKey === void 0 ? await invoke(scripts.write, [valueKey], [String(cacheTtlMs), String(encoding), value]) : await invoke(
|
|
58
|
+
scripts.writeTracked,
|
|
59
|
+
[valueKey, watermarkKey],
|
|
60
|
+
[String(cacheTtlMs), String(encoding), value, String(request.watermarkTtlFloorMs)]
|
|
61
|
+
);
|
|
62
|
+
return integerReply(raw, "write") === 1;
|
|
63
|
+
},
|
|
64
|
+
async invalidate({ watermarkKey, futureBufferMs, watermarkTtlFloorMs }) {
|
|
65
|
+
const raw = await invoke(
|
|
66
|
+
scripts.invalidate,
|
|
67
|
+
[watermarkKey],
|
|
68
|
+
[String(futureBufferMs), String(watermarkTtlFloorMs)]
|
|
69
|
+
);
|
|
70
|
+
integerReply(raw, "invalidate");
|
|
71
|
+
},
|
|
72
|
+
async flushAll() {
|
|
73
|
+
if (disposed) {
|
|
74
|
+
throw new Error("Valkey GLIDE DialCache client is disposed");
|
|
75
|
+
}
|
|
76
|
+
if (client instanceof GlideClusterClient) {
|
|
77
|
+
await client.flushall({ route: "allPrimaries" });
|
|
78
|
+
} else {
|
|
79
|
+
await client.flushall();
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
dispose() {
|
|
83
|
+
if (disposed) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (activeInvocations > 0) {
|
|
87
|
+
throw new Error("Cannot dispose Valkey GLIDE DialCache client while operations are in flight");
|
|
88
|
+
}
|
|
89
|
+
disposed = true;
|
|
90
|
+
for (const script of Object.values(scripts)) {
|
|
91
|
+
script.release();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function integerReply(reply, operation) {
|
|
97
|
+
if (typeof reply !== "number") {
|
|
98
|
+
throw new Error(`Invalid DialCache Redis ${operation} reply`);
|
|
99
|
+
}
|
|
100
|
+
return reply;
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
createValkeyGlideDialCacheClient
|
|
104
|
+
};
|