@mark1russell7/service 0.5.11 → 0.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -1
- package/build/{builder-Dn0cby6Q.d.ts → builder-DawJhq4p.d.ts} +2 -2
- package/build/core-C2wnF4mg.d.ts +183 -0
- package/build/http.d.ts +1 -1
- package/build/index.d.ts +6 -3
- package/build/index.js +155 -11
- package/build/index.js.map +1 -1
- package/build/{lifecycle-CGEegMI0.d.ts → lifecycle-fgYfxrHK.d.ts} +17 -2
- package/build/machine-Czfb6fV4.js +372 -0
- package/build/machine-Czfb6fV4.js.map +1 -0
- package/build/pg.d.ts +230 -0
- package/build/pg.js +534 -0
- package/build/pg.js.map +1 -0
- package/build/redis.d.ts +24 -137
- package/build/redis.js +72 -279
- package/build/redis.js.map +1 -1
- package/build/testing.d.ts +2 -2
- package/package.json +9 -1
package/build/redis.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { realClock } from "./clock.js";
|
|
2
2
|
import { heartbeat } from "./heartbeat.js";
|
|
3
3
|
import { i as heartbeatDetection } from "./timing-DGRi3XCI.js";
|
|
4
|
-
import { t as mayReissue } from "./operation-DL5RxFds.js";
|
|
5
4
|
import { backoffDelay } from "./backoff.js";
|
|
6
|
-
import {
|
|
7
|
-
import { connect } from "node:net";
|
|
5
|
+
import { A as keepaliveSilent, C as withinBound, D as immediate, E as firstDetector, M as unwatched, O as keepaliveAnswered, S as timeProblems, T as WHAT, _ as operationTable, b as socketEnds, c as clientProblems, d as finiteMs, f as inFlight, g as notSent, h as ms, i as helloDial, j as kernelGiveUp, k as keepaliveReset, l as clientRecorder, m as message, n as closeTimer, o as attachClient, p as localStateCheck, r as transition, s as clientBounds, t as clientKnob, u as dialCheck, v as roundTripCheck, w as MANIFESTATIONS, x as stepChecks, y as sendWithReissue } from "./machine-Czfb6fV4.js";
|
|
8
6
|
//#region src/redis/connection.ts
|
|
9
7
|
/**
|
|
10
8
|
* This factory makes an ioredis client. It loads ioredis only when a client connects, thus a
|
|
@@ -15,13 +13,6 @@ const ioredis = async (options) => {
|
|
|
15
13
|
};
|
|
16
14
|
//#endregion
|
|
17
15
|
//#region src/redis/meets.ts
|
|
18
|
-
const never = {
|
|
19
|
-
value: "infinite",
|
|
20
|
-
unit: "s"
|
|
21
|
-
};
|
|
22
|
-
const s = (value) => quantity(Math.round(value * 1e6) / 1e6, "s");
|
|
23
|
-
const seconds = (amount) => amount.value === "infinite" ? Number.POSITIVE_INFINITY : valueIn(amount, "s");
|
|
24
|
-
const plus = (amount, more) => amount.value === "infinite" ? never : s(seconds(amount) + more);
|
|
25
16
|
/**
|
|
26
17
|
* This function gives the manifestations that a Redis client can meet, and the detectors of each, from
|
|
27
18
|
* its knobs. A reset or a FIN arrives on the next segment. A black hole and a host that is gone give
|
|
@@ -29,7 +20,6 @@ const plus = (amount, more) => amount.value === "infinite" ? never : s(seconds(a
|
|
|
29
20
|
* segment. Thus TCP keepalive does not fire on them, and only the client's own timers find them.
|
|
30
21
|
*/
|
|
31
22
|
const meetings = (name, knobs, heartbeat) => {
|
|
32
|
-
const keepalive = plus(knobs.keepalive_idle, 10);
|
|
33
23
|
const watchdog = (situation) => ({
|
|
34
24
|
by: "heartbeat_detection_time",
|
|
35
25
|
within: heartbeat,
|
|
@@ -48,27 +38,17 @@ const meetings = (name, knobs, heartbeat) => {
|
|
|
48
38
|
situation: "in flight",
|
|
49
39
|
effect: "the command fails as indeterminate, and the state of the connection does not change"
|
|
50
40
|
};
|
|
51
|
-
const giveUp =
|
|
52
|
-
|
|
53
|
-
within: knobs.retrans_giveup_time,
|
|
54
|
-
situation: "in flight",
|
|
55
|
-
effect: "the kernel gives up the retransmissions and closes the socket"
|
|
56
|
-
};
|
|
57
|
-
const silent = (manifestation, what) => ({
|
|
41
|
+
const giveUp = kernelGiveUp(knobs.retrans_giveup_time);
|
|
42
|
+
const silent = (manifestation) => ({
|
|
58
43
|
manifestation,
|
|
59
|
-
what,
|
|
44
|
+
what: WHAT[manifestation],
|
|
60
45
|
detectors: [
|
|
61
46
|
command,
|
|
62
47
|
writeoff,
|
|
63
48
|
watchdog("in flight"),
|
|
64
49
|
giveUp,
|
|
65
50
|
watchdog("idle"),
|
|
66
|
-
|
|
67
|
-
by: "keepalive_idle",
|
|
68
|
-
within: keepalive,
|
|
69
|
-
situation: "idle",
|
|
70
|
-
effect: "TCP keepalive gives up, and the socket closes"
|
|
71
|
-
}
|
|
51
|
+
keepaliveSilent(knobs.keepalive_idle)
|
|
72
52
|
]
|
|
73
53
|
});
|
|
74
54
|
const acknowledged = (manifestation, what) => ({
|
|
@@ -79,55 +59,30 @@ const meetings = (name, knobs, heartbeat) => {
|
|
|
79
59
|
writeoff,
|
|
80
60
|
watchdog("in flight"),
|
|
81
61
|
watchdog("idle"),
|
|
82
|
-
|
|
83
|
-
by: "keepalive_idle",
|
|
84
|
-
within: never,
|
|
85
|
-
situation: "idle",
|
|
86
|
-
effect: "the kernel of the server answers each probe"
|
|
87
|
-
}
|
|
62
|
+
keepaliveAnswered()
|
|
88
63
|
]
|
|
89
64
|
});
|
|
90
|
-
const immediate = (situation, effect) => ({
|
|
91
|
-
by: "the next segment",
|
|
92
|
-
within: s(0),
|
|
93
|
-
situation,
|
|
94
|
-
effect
|
|
95
|
-
});
|
|
96
65
|
return [
|
|
97
66
|
{
|
|
98
67
|
manifestation: "rst",
|
|
99
|
-
what:
|
|
68
|
+
what: WHAT.rst,
|
|
100
69
|
detectors: [
|
|
101
70
|
immediate("in flight", "the command fails, and the client connects again, and each attempt is refused"),
|
|
102
71
|
watchdog("idle"),
|
|
103
|
-
|
|
104
|
-
by: "keepalive_idle",
|
|
105
|
-
within: keepalive.value === "infinite" ? never : knobs.keepalive_idle,
|
|
106
|
-
situation: "idle",
|
|
107
|
-
effect: "the first probe gets a reset"
|
|
108
|
-
}
|
|
72
|
+
keepaliveReset(knobs.keepalive_idle)
|
|
109
73
|
]
|
|
110
74
|
},
|
|
111
75
|
{
|
|
112
76
|
manifestation: "fin",
|
|
113
|
-
what:
|
|
77
|
+
what: WHAT.fin,
|
|
114
78
|
detectors: [immediate("in flight", "the close arrives, and the command fails"), immediate("idle", "the close arrives")]
|
|
115
79
|
},
|
|
116
|
-
silent("blackhole"
|
|
117
|
-
silent("freeze-host"
|
|
118
|
-
acknowledged("freeze-process", "
|
|
80
|
+
silent("blackhole"),
|
|
81
|
+
silent("freeze-host"),
|
|
82
|
+
acknowledged("freeze-process", WHAT["freeze-process"]),
|
|
119
83
|
acknowledged("app-stall", "the server stops its replies for a time, for example a long command, and its kernel stays up")
|
|
120
84
|
];
|
|
121
85
|
};
|
|
122
|
-
/** This function gives the first detector of a manifestation in a situation, or null when no detector has an end (C3). */
|
|
123
|
-
const firstDetector = (meeting, situation) => {
|
|
124
|
-
return meeting.detectors.filter((detector) => detector.situation === situation && detector.within.value !== "infinite").sort((a, b) => seconds(a.within) - seconds(b.within))[0] ?? null;
|
|
125
|
-
};
|
|
126
|
-
/** This function gives each manifestation and situation that no detector of the client finds: the standing rule, as a query (C3, E9). */
|
|
127
|
-
const unwatched = (meets) => meets.flatMap((meeting) => ["in flight", "idle"].flatMap((situation) => firstDetector(meeting, situation) === null ? [{
|
|
128
|
-
manifestation: meeting.manifestation,
|
|
129
|
-
situation
|
|
130
|
-
}] : []));
|
|
131
86
|
//#endregion
|
|
132
87
|
//#region src/redis/operations.ts
|
|
133
88
|
/**
|
|
@@ -242,12 +197,6 @@ const commandName = (command, args) => {
|
|
|
242
197
|
};
|
|
243
198
|
//#endregion
|
|
244
199
|
//#region src/redis/client.ts
|
|
245
|
-
const ms = (amount) => amount.value === "infinite" ? Number.POSITIVE_INFINITY : valueIn(amount, "ms");
|
|
246
|
-
const finite = (amount) => {
|
|
247
|
-
const value = ms(amount);
|
|
248
|
-
return Number.isFinite(value) ? value : void 0;
|
|
249
|
-
};
|
|
250
|
-
const message = (error) => error instanceof Error ? error.message : String(error);
|
|
251
200
|
/**
|
|
252
201
|
* This function gives the backoff of the tries to connect: from `retry_base`, doubling to
|
|
253
202
|
* `retry_cap`, each with a jitter up to the base. The client rounds each delay to a whole millisecond.
|
|
@@ -267,38 +216,17 @@ const specProblems = (name, spec) => {
|
|
|
267
216
|
const { knobs } = spec;
|
|
268
217
|
if (!Number.isInteger(spec.heartbeatMisses) || spec.heartbeatMisses < 1) problems.push(`${name}'s watchdog closes the connection after ${spec.heartbeatMisses} misses, not a whole number above zero`);
|
|
269
218
|
if (knobs.max_connection_age.value !== "infinite") problems.push(`${name} cannot replace its connection at an age: max_connection_age must be "infinite"`);
|
|
270
|
-
|
|
219
|
+
problems.push(...timeProblems(name, knobs));
|
|
271
220
|
if (ms(knobs.request_timeout) <= 0) problems.push(`${name}'s request_timeout is not above zero: each command must have a bound`);
|
|
272
221
|
if (ms(knobs.heartbeat_timeout) <= 0) problems.push(`${name}'s heartbeat_timeout is not above zero`);
|
|
273
222
|
if (ms(knobs.retry_cap) < ms(knobs.retry_base)) problems.push(`${name}'s retry_cap is below its retry_base`);
|
|
274
223
|
return problems;
|
|
275
224
|
};
|
|
276
225
|
/** This dial function opens a TCP connection, sends an inline PING, and waits for +PONG. */
|
|
277
|
-
const tcpDial = (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
const socket = connect({
|
|
283
|
-
host,
|
|
284
|
-
port
|
|
285
|
-
});
|
|
286
|
-
let reply = "";
|
|
287
|
-
const end = (error) => {
|
|
288
|
-
signal.removeEventListener("abort", onAbort);
|
|
289
|
-
socket.destroy();
|
|
290
|
-
if (error === void 0) resolve();
|
|
291
|
-
else reject(error);
|
|
292
|
-
};
|
|
293
|
-
const onAbort = () => end(/* @__PURE__ */ new Error("the dial was aborted"));
|
|
294
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
295
|
-
socket.once("connect", () => socket.write("PING\r\n"));
|
|
296
|
-
socket.on("data", (chunk) => {
|
|
297
|
-
reply += chunk.toString();
|
|
298
|
-
if (reply.includes("\r\n")) end(reply.startsWith("+PONG") ? void 0 : /* @__PURE__ */ new Error(`the reply was ${reply.trim()}`));
|
|
299
|
-
});
|
|
300
|
-
socket.once("error", (error) => end(error));
|
|
301
|
-
socket.once("close", () => end(/* @__PURE__ */ new Error("the server closed the dial")));
|
|
226
|
+
const tcpDial = helloDial("PING\r\n", (received) => {
|
|
227
|
+
const reply = received.toString();
|
|
228
|
+
if (!reply.includes("\r\n")) return "more";
|
|
229
|
+
return reply.startsWith("+PONG") ? null : /* @__PURE__ */ new Error(`the reply was ${reply.trim()}`);
|
|
302
230
|
});
|
|
303
231
|
/**
|
|
304
232
|
* This function makes a Redis client. It is a dependency of a service, with ioredis under it:
|
|
@@ -309,6 +237,9 @@ const tcpDial = (host, port, signal) => new Promise((resolve, reject) => {
|
|
|
309
237
|
* - It records the server's id of each connection at `ready` (D1).
|
|
310
238
|
* - A watchdog sends a PING in the band of the connection, and it connects again after the misses
|
|
311
239
|
* of the declaration. It also starts a client that stopped (principle 19).
|
|
240
|
+
*
|
|
241
|
+
* The outcomes, the second try, the commands in flight, the bounded close and the checks come
|
|
242
|
+
* from the core of the clients. This entry gives what ioredis does in its own way.
|
|
312
243
|
*/
|
|
313
244
|
const redisClient = (name, spec, options = {}) => {
|
|
314
245
|
const problems = specProblems(name, spec);
|
|
@@ -319,10 +250,7 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
319
250
|
const create = options.create ?? ioredis;
|
|
320
251
|
const dial = options.dial ?? tcpDial;
|
|
321
252
|
const turn = options.turn ?? (() => new Promise((resolve) => setImmediate(resolve)));
|
|
322
|
-
const operations =
|
|
323
|
-
...REDIS_OPERATIONS,
|
|
324
|
-
...spec.operations
|
|
325
|
-
};
|
|
253
|
+
const operations = operationTable(REDIS_OPERATIONS, spec.operations);
|
|
326
254
|
const requestTimeout = ms(knobs.request_timeout);
|
|
327
255
|
const detection = heartbeatDetection(knobs.heartbeat_interval, knobs.heartbeat_timeout, spec.heartbeatMisses);
|
|
328
256
|
const backoff = retryBackoff(knobs);
|
|
@@ -330,18 +258,10 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
330
258
|
let status = "none";
|
|
331
259
|
let closing = false;
|
|
332
260
|
const joins = [];
|
|
333
|
-
const
|
|
261
|
+
const flight = inFlight();
|
|
334
262
|
const readyWaiters = /* @__PURE__ */ new Set();
|
|
335
263
|
const listeners = [];
|
|
336
|
-
const record = (
|
|
337
|
-
at: clock.now(),
|
|
338
|
-
machine: "client",
|
|
339
|
-
instance: name,
|
|
340
|
-
from,
|
|
341
|
-
to,
|
|
342
|
-
on,
|
|
343
|
-
...detail === void 0 ? {} : { detail }
|
|
344
|
-
});
|
|
264
|
+
const record = clientRecorder(options.record, clock, name);
|
|
345
265
|
let retrySince = null;
|
|
346
266
|
const retryStrategy = (times) => {
|
|
347
267
|
const now = clock.now();
|
|
@@ -361,8 +281,8 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
361
281
|
lazyConnect: true,
|
|
362
282
|
connectTimeout: ms(knobs.connect_bound),
|
|
363
283
|
commandTimeout: requestTimeout,
|
|
364
|
-
socketTimeout:
|
|
365
|
-
keepAlive:
|
|
284
|
+
socketTimeout: finiteMs(knobs.driver_writeoff_time),
|
|
285
|
+
keepAlive: finiteMs(knobs.keepalive_idle) ?? false,
|
|
366
286
|
enableOfflineQueue: false,
|
|
367
287
|
autoResendUnfulfilledCommands: false,
|
|
368
288
|
maxRetriesPerRequest: null,
|
|
@@ -378,11 +298,7 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
378
298
|
error: text,
|
|
379
299
|
reply: true
|
|
380
300
|
};
|
|
381
|
-
if (/isn't writeable|enableOfflineQueue/i.test(text)) return
|
|
382
|
-
outcome: "error",
|
|
383
|
-
error: text,
|
|
384
|
-
reply: false
|
|
385
|
-
};
|
|
301
|
+
if (/isn't writeable|enableOfflineQueue/i.test(text)) return notSent(text);
|
|
386
302
|
return {
|
|
387
303
|
outcome: "indeterminate",
|
|
388
304
|
error: text
|
|
@@ -408,10 +324,7 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
408
324
|
generation,
|
|
409
325
|
at,
|
|
410
326
|
clientId,
|
|
411
|
-
|
|
412
|
-
localPort: socket?.localPort ?? null,
|
|
413
|
-
remoteAddress: socket?.remoteAddress ?? null,
|
|
414
|
-
remotePort: socket?.remotePort ?? null
|
|
327
|
+
...socketEnds(socket)
|
|
415
328
|
});
|
|
416
329
|
record("ready", "ready", "join", `generation ${generation}, client id ${clientId ?? "unknown"}`);
|
|
417
330
|
for (const wake of [...readyWaiters]) wake();
|
|
@@ -420,10 +333,7 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
420
333
|
const from = status;
|
|
421
334
|
status = to;
|
|
422
335
|
record(from, to, `status.${to}`);
|
|
423
|
-
if (to === "close" || to === "end")
|
|
424
|
-
outcome: "indeterminate",
|
|
425
|
-
error: `the connection closed with the command in flight (${from})`
|
|
426
|
-
});
|
|
336
|
+
if (to === "close" || to === "end") flight.settleAll(`the connection closed with the command in flight (${from})`);
|
|
427
337
|
if (to === "ready") onReady();
|
|
428
338
|
};
|
|
429
339
|
const watch = (c) => {
|
|
@@ -464,75 +374,25 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
464
374
|
});
|
|
465
375
|
const attempt = (command, args, signal) => {
|
|
466
376
|
const c = connection;
|
|
467
|
-
if (c === null) return Promise.resolve({
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
});
|
|
472
|
-
if (c.status !== "ready") return Promise.resolve({
|
|
473
|
-
outcome: "error",
|
|
474
|
-
error: `${name} is ${c.status}, not ready: the command did not leave the client`,
|
|
475
|
-
reply: false
|
|
476
|
-
});
|
|
477
|
-
if (signal?.aborted) return Promise.resolve({
|
|
478
|
-
outcome: "error",
|
|
479
|
-
error: "the caller aborted the command before it left the client",
|
|
480
|
-
reply: false
|
|
481
|
-
});
|
|
482
|
-
return new Promise((resolve) => {
|
|
483
|
-
let settled = false;
|
|
484
|
-
const settle = (value) => {
|
|
485
|
-
if (settled) return;
|
|
486
|
-
settled = true;
|
|
487
|
-
inFlight.delete(settle);
|
|
488
|
-
signal?.removeEventListener("abort", aborted);
|
|
489
|
-
resolve(value);
|
|
490
|
-
};
|
|
491
|
-
const aborted = () => settle({
|
|
492
|
-
outcome: "indeterminate",
|
|
493
|
-
error: "the caller abandoned the command after it was sent"
|
|
494
|
-
});
|
|
495
|
-
inFlight.add(settle);
|
|
496
|
-
signal?.addEventListener("abort", aborted, { once: true });
|
|
377
|
+
if (c === null) return Promise.resolve(notSent(`${name} has not connected`));
|
|
378
|
+
if (c.status !== "ready") return Promise.resolve(notSent(`${name} is ${c.status}, not ready: the command did not leave the client`));
|
|
379
|
+
if (signal?.aborted) return Promise.resolve(notSent("the caller aborted the command before it left the client"));
|
|
380
|
+
return flight.track(signal, "the caller abandoned the command after it was sent", (settle) => {
|
|
497
381
|
c.call(command, ...args).then((value) => settle({
|
|
498
382
|
outcome: "ok",
|
|
499
383
|
value
|
|
500
384
|
}), (error) => settle(failed(error)));
|
|
501
385
|
});
|
|
502
386
|
};
|
|
503
|
-
const send =
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
};
|
|
513
|
-
const again = !(first.outcome === "error" && first.reply) && operation !== void 0 && signal?.aborted !== true && mayReissue(operation, quantity((clock.now() - started) / 1e3, "s"));
|
|
514
|
-
const left = requestTimeout - (clock.now() - started);
|
|
515
|
-
if (!again || left <= 0 || !await waitReady(left, signal)) return {
|
|
516
|
-
...first,
|
|
517
|
-
attempts: 1
|
|
518
|
-
};
|
|
519
|
-
record(status, status, "command.reissue", commandName(command, args));
|
|
520
|
-
const bound = new AbortController();
|
|
521
|
-
const end = clock.after(Math.max(0, requestTimeout - (clock.now() - started)), () => bound.abort());
|
|
522
|
-
const forward = () => bound.abort();
|
|
523
|
-
signal?.addEventListener("abort", forward, { once: true });
|
|
524
|
-
const second = await attempt(command, args, bound.signal);
|
|
525
|
-
end();
|
|
526
|
-
signal?.removeEventListener("abort", forward);
|
|
527
|
-
return second.outcome === "ok" ? {
|
|
528
|
-
outcome: "ok",
|
|
529
|
-
value: second.value,
|
|
530
|
-
attempts: 2
|
|
531
|
-
} : {
|
|
532
|
-
...second,
|
|
533
|
-
attempts: 2
|
|
534
|
-
};
|
|
535
|
-
};
|
|
387
|
+
const send = (command, args = [], sendOptions = {}) => sendWithReissue({
|
|
388
|
+
clock,
|
|
389
|
+
bound: requestTimeout,
|
|
390
|
+
operation: operations[commandName(command, args)],
|
|
391
|
+
options: sendOptions,
|
|
392
|
+
attempt: (signal) => attempt(command, args, signal),
|
|
393
|
+
beforeAgain: (left, signal) => waitReady(left, signal),
|
|
394
|
+
onReissue: () => record(status, status, "command.reissue", commandName(command, args))
|
|
395
|
+
});
|
|
536
396
|
let watchdog = null;
|
|
537
397
|
const startWatchdog = () => heartbeat({
|
|
538
398
|
interval: ms(knobs.heartbeat_interval),
|
|
@@ -577,15 +437,8 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
577
437
|
watchdog = null;
|
|
578
438
|
const c = connection;
|
|
579
439
|
if (c === null) return;
|
|
580
|
-
const quit = c.quit().then(() => "quit", () => "quit");
|
|
581
|
-
let cancel = () => {};
|
|
582
|
-
const bound = new Promise((resolve) => {
|
|
583
|
-
cancel = clock.after(ms(knobs.close_bound), () => resolve("bound"));
|
|
584
|
-
});
|
|
585
440
|
const startedClose = clock.now();
|
|
586
|
-
|
|
587
|
-
cancel();
|
|
588
|
-
if (first === "bound") record(status, status, "close.bound");
|
|
441
|
+
if (!await withinBound(clock, ms(knobs.close_bound), c.quit())) record(status, status, "close.bound");
|
|
589
442
|
if (c.status !== "end") c.disconnect(false);
|
|
590
443
|
const ended = (within) => c.status === "end" ? Promise.resolve() : new Promise((resolve) => {
|
|
591
444
|
let stop = () => {};
|
|
@@ -605,10 +458,7 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
605
458
|
}
|
|
606
459
|
for (const [event, listener] of listeners) c.off(event, listener);
|
|
607
460
|
listeners.length = 0;
|
|
608
|
-
|
|
609
|
-
outcome: "indeterminate",
|
|
610
|
-
error: "the client closed with the command in flight"
|
|
611
|
-
});
|
|
461
|
+
flight.settleAll("the client closed with the command in flight");
|
|
612
462
|
status = "end";
|
|
613
463
|
}
|
|
614
464
|
};
|
|
@@ -631,32 +481,11 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
631
481
|
},
|
|
632
482
|
operations
|
|
633
483
|
};
|
|
634
|
-
const checks = (checkOptions) => {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
name: `${name}.${step}`,
|
|
640
|
-
dependency: name,
|
|
641
|
-
step,
|
|
642
|
-
ownConnection,
|
|
643
|
-
gates: checkOptions.steps[step] ?? null,
|
|
644
|
-
poll: checkOptions.poll
|
|
645
|
-
}, run]);
|
|
646
|
-
};
|
|
647
|
-
declare("local-state", true, () => connection?.status === "ready" ? "ok" : "error");
|
|
648
|
-
declare("round-trip", true, async (signal) => {
|
|
649
|
-
return (await send("PING", [], {
|
|
650
|
-
signal,
|
|
651
|
-
reissue: false
|
|
652
|
-
})).outcome === "ok" ? "ok" : "error";
|
|
653
|
-
});
|
|
654
|
-
declare("dial", false, async (signal) => {
|
|
655
|
-
await dial(spec.host, spec.port, signal);
|
|
656
|
-
return "ok";
|
|
657
|
-
});
|
|
658
|
-
return made;
|
|
659
|
-
};
|
|
484
|
+
const checks = (checkOptions) => stepChecks(name, checkOptions, {
|
|
485
|
+
"local-state": localStateCheck(() => connection?.status === "ready"),
|
|
486
|
+
"round-trip": roundTripCheck((sendOptions) => send("PING", [], sendOptions)),
|
|
487
|
+
dial: dialCheck(dial, spec.host, spec.port)
|
|
488
|
+
});
|
|
660
489
|
return {
|
|
661
490
|
name,
|
|
662
491
|
declaration,
|
|
@@ -668,23 +497,8 @@ const redisClient = (name, spec, options = {}) => {
|
|
|
668
497
|
joins,
|
|
669
498
|
send,
|
|
670
499
|
checks,
|
|
671
|
-
attach(svc, checkOptions)
|
|
672
|
-
|
|
673
|
-
for (const [check, run] of checks(checkOptions)) svc.check(check, run);
|
|
674
|
-
return svc;
|
|
675
|
-
},
|
|
676
|
-
problems(description) {
|
|
677
|
-
const found = [];
|
|
678
|
-
const teardown = description.shutdown.phases.find((phase) => phase.name === "tear down dependencies");
|
|
679
|
-
if (teardown === void 0) found.push(`${name} needs the phase "tear down dependencies" to close`);
|
|
680
|
-
else if (ms(knobs.close_bound) >= valueIn(teardown.cap, "ms")) found.push(`${name}'s close_bound of ${ms(knobs.close_bound)} ms does not fit in the cap of "tear down dependencies"`);
|
|
681
|
-
for (const check of description.checks) {
|
|
682
|
-
if (check.dependency !== name) continue;
|
|
683
|
-
const slowest = valueIn(check.poll.timeout, "ms");
|
|
684
|
-
if (check.step === "round-trip" && requestTimeout > slowest + valueIn(check.poll.interval, "ms") * (1 + check.poll.jitter)) found.push(`${name}'s request_timeout outlives one jittered poll of ${check.name} (B7, O13)`);
|
|
685
|
-
}
|
|
686
|
-
return found;
|
|
687
|
-
}
|
|
500
|
+
attach: (svc, checkOptions) => attachClient(svc, name, declaration, handle, checks(checkOptions)),
|
|
501
|
+
problems: (description) => clientProblems(name, description, clientBounds(knobs))
|
|
688
502
|
};
|
|
689
503
|
};
|
|
690
504
|
//#endregion
|
|
@@ -842,19 +656,6 @@ const replyOf = (limiter, reply) => {
|
|
|
842
656
|
};
|
|
843
657
|
//#endregion
|
|
844
658
|
//#region src/redis/machine.ts
|
|
845
|
-
const t = (id, from, on, to, extra = {}) => ({
|
|
846
|
-
id,
|
|
847
|
-
from,
|
|
848
|
-
on,
|
|
849
|
-
to,
|
|
850
|
-
...extra
|
|
851
|
-
});
|
|
852
|
-
const knob = (key) => ({
|
|
853
|
-
key,
|
|
854
|
-
scope: "client",
|
|
855
|
-
unit: "s",
|
|
856
|
-
default: null
|
|
857
|
-
});
|
|
858
659
|
/** The states where the connection exists or tries to exist, thus where it can close. */
|
|
859
660
|
const LIVE = [
|
|
860
661
|
"connecting",
|
|
@@ -932,10 +733,10 @@ const REDIS_CLIENT_MACHINE = {
|
|
|
932
733
|
}
|
|
933
734
|
},
|
|
934
735
|
knobs: {
|
|
935
|
-
connect_bound:
|
|
936
|
-
driver_writeoff_time:
|
|
937
|
-
heartbeat_interval:
|
|
938
|
-
close_bound:
|
|
736
|
+
connect_bound: clientKnob("connectTimeout"),
|
|
737
|
+
driver_writeoff_time: clientKnob("socketTimeout"),
|
|
738
|
+
heartbeat_interval: clientKnob("watchdog.interval"),
|
|
739
|
+
close_bound: clientKnob("close.bound")
|
|
939
740
|
},
|
|
940
741
|
timers: {
|
|
941
742
|
connect_timeout: {
|
|
@@ -968,41 +769,33 @@ const REDIS_CLIENT_MACHINE = {
|
|
|
968
769
|
fires: "heartbeat",
|
|
969
770
|
note: "a PING on the connection of the application"
|
|
970
771
|
},
|
|
971
|
-
close:
|
|
972
|
-
duration: {
|
|
973
|
-
knob: "close_bound",
|
|
974
|
-
unit: "s"
|
|
975
|
-
},
|
|
976
|
-
arms: ["close.request"],
|
|
977
|
-
cancels: ["status.end"],
|
|
978
|
-
fires: "close.bound"
|
|
979
|
-
}
|
|
772
|
+
close: closeTimer()
|
|
980
773
|
},
|
|
981
774
|
transitions: [
|
|
982
|
-
|
|
775
|
+
transition("connect", [
|
|
983
776
|
"none",
|
|
984
777
|
"reconnecting",
|
|
985
778
|
"end",
|
|
986
779
|
"close"
|
|
987
780
|
], "status.connecting", "connecting"),
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
...ANY.map((state) =>
|
|
999
|
-
...ANY.map((state) =>
|
|
1000
|
-
...ANY.map((state) =>
|
|
1001
|
-
...ANY.map((state) =>
|
|
1002
|
-
...ANY.map((state) =>
|
|
781
|
+
transition("tcp", "connecting", "status.connect", "connect"),
|
|
782
|
+
transition("ready", "connect", "status.ready", "ready"),
|
|
783
|
+
transition("close", [...LIVE], "status.close", "close"),
|
|
784
|
+
transition("retry", "close", "status.reconnecting", "reconnecting"),
|
|
785
|
+
transition("end", [...LIVE, "close"], "status.end", "end"),
|
|
786
|
+
transition("join", "ready", "join", "ready"),
|
|
787
|
+
transition("miss", "ready", "watchdog.miss", "ready"),
|
|
788
|
+
transition("watchdog-reconnect", "ready", "watchdog.reconnect", "ready"),
|
|
789
|
+
transition("revive", "end", "watchdog.revive", "end"),
|
|
790
|
+
transition("window-ended", "close", "retry.window-ended", "close"),
|
|
791
|
+
...ANY.map((state) => transition(`error-${state}`, state, "error", state)),
|
|
792
|
+
...ANY.map((state) => transition(`connect-failed-${state}`, state, "connect.failed", state)),
|
|
793
|
+
...ANY.map((state) => transition(`reissue-${state}`, state, "command.reissue", state)),
|
|
794
|
+
...ANY.map((state) => transition(`close-bound-${state}`, state, "close.bound", state, { timer: "close" })),
|
|
795
|
+
...ANY.map((state) => transition(`close-destroy-${state}`, state, "close.destroy", state))
|
|
1003
796
|
]
|
|
1004
797
|
};
|
|
1005
798
|
//#endregion
|
|
1006
|
-
export { LIMITER_SCRIPT, REDIS_CLIENT_MACHINE, REDIS_OPERATIONS, commandName, firstDetector, heartbeatDetection, ioredis, meetings, redisClient, redisLimiterStore, retryBackoff, specProblems, tcpDial, unwatched };
|
|
799
|
+
export { LIMITER_SCRIPT, MANIFESTATIONS, REDIS_CLIENT_MACHINE, REDIS_OPERATIONS, commandName, firstDetector, heartbeatDetection, ioredis, meetings, redisClient, redisLimiterStore, retryBackoff, specProblems, tcpDial, unwatched };
|
|
1007
800
|
|
|
1008
801
|
//# sourceMappingURL=redis.js.map
|