@mark1russell7/kind 0.1.8 → 0.2.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/README.md +26 -2
- package/build/index.d.ts +26 -6
- package/build/index.js +159 -45
- package/build/index.js.map +1 -1
- package/build/{judge-a7yCzwZc.d.ts → judge-Dwa6MqP4.d.ts} +76 -14
- package/build/{judge-BbTAxRSd.js → judge-reWPMVr4.js} +100 -40
- package/build/judge-reWPMVr4.js.map +1 -0
- package/build/judge.d.ts +2 -2
- package/build/judge.js +1 -1
- package/image/Dockerfile +6 -6
- package/image/client-grpc.mjs +97 -0
- package/image/npm-lock.json +326 -0
- package/image/package.json +1 -0
- package/package.json +4 -3
- package/build/judge-BbTAxRSd.js.map +0 -1
|
@@ -79,6 +79,15 @@ declare const cidrOverlap: (a: string, b: string) => boolean;
|
|
|
79
79
|
//#region src/cell.d.ts
|
|
80
80
|
/** A template is the topology of a cell: T07, the endpoint lifecycle of a Service, or T18, a health-gated fleet. */
|
|
81
81
|
type Template = "T07" | "T18";
|
|
82
|
+
/**
|
|
83
|
+
* A stack is the server and the clients of a cell:
|
|
84
|
+
* - `service`: the service of this repository with its declaration, and the HTTP client, which opens a
|
|
85
|
+
* new connection for each request. Thus kube-proxy picks a backend for each request.
|
|
86
|
+
* - `grpc`: the server of grpc-go v1.84.0 of the harness, and a client of grpc-js 1.14.6 with one channel
|
|
87
|
+
* at its defaults. The channel holds one connection. It connects again only when that connection
|
|
88
|
+
* ends: at a GOAWAY of the server, or at the end of its socket.
|
|
89
|
+
*/
|
|
90
|
+
type Stack = "service" | "grpc";
|
|
82
91
|
/**
|
|
83
92
|
* A trigger is the event or the fault of a cell, by the name of the plan:
|
|
84
93
|
* - `pod-deletion`, `rolling-update`, `node-drain` and `scale-in` stop a pod with its grace.
|
|
@@ -157,11 +166,17 @@ interface Declaration {
|
|
|
157
166
|
readonly threshold: Quantity;
|
|
158
167
|
} | null;
|
|
159
168
|
}
|
|
160
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* A workload is the clients: one pod on each node, each with requests to the ClusterIP. The HTTP client
|
|
171
|
+
* opens a new connection for each request. The gRPC client sends each call on its one channel.
|
|
172
|
+
*/
|
|
161
173
|
interface Workload {
|
|
162
174
|
/** This time is the interval between two requests of one client. The clients do not wait for an answer. */
|
|
163
175
|
readonly every: Quantity;
|
|
164
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* This time is how long a client waits for an answer before the request is a timeout. A call of
|
|
178
|
+
* gRPC has no deadline at the defaults of grpc-js, thus the client cancels it at this time.
|
|
179
|
+
*/
|
|
165
180
|
readonly timeout: Quantity;
|
|
166
181
|
}
|
|
167
182
|
/**
|
|
@@ -197,6 +212,8 @@ interface ResetReuse<A = Quantity> {
|
|
|
197
212
|
* - `window`: the requests that fail start in a window. The window starts at `start` and ends in
|
|
198
213
|
* `end`. With `certain`, at least one request fails. With `afterReset`, a request outside the
|
|
199
214
|
* window can also fail, on a port that a reset closed up to `life` before it (`ResetReuse`).
|
|
215
|
+
* With `each`, each replica of the run that gets a deletion has a window of its own, from its own
|
|
216
|
+
* references. A request is late only when it is outside each of these windows.
|
|
200
217
|
* - `removal`: the Service has no ready endpoint, and the rules of each node have no backend, in the band.
|
|
201
218
|
* - `leaves`: the target replica leaves the EndpointSlice and the rules of each node in the band.
|
|
202
219
|
* - `restarts`: each replica receives SIGTERM in the band, or no replica does.
|
|
@@ -222,6 +239,12 @@ type Prediction<A = Quantity> = {
|
|
|
222
239
|
} | null;
|
|
223
240
|
readonly certain: boolean;
|
|
224
241
|
readonly afterReset?: ResetReuse<A> | undefined;
|
|
242
|
+
/**
|
|
243
|
+
* This flag gives a window to each replica of the run (`replicas`) that gets a deletion after the
|
|
244
|
+
* start of the run. The references `deletion` and `intake` are then those of that replica. A
|
|
245
|
+
* rolling update deletes each old replica in turn. Without the flag, the window is that of the target.
|
|
246
|
+
*/
|
|
247
|
+
readonly each?: boolean | undefined;
|
|
225
248
|
readonly why: string;
|
|
226
249
|
} | {
|
|
227
250
|
readonly kind: "removal";
|
|
@@ -295,13 +318,16 @@ interface KindCell<A = Quantity> {
|
|
|
295
318
|
readonly id: string;
|
|
296
319
|
readonly name: string;
|
|
297
320
|
readonly template: Template;
|
|
321
|
+
/** This stack is the server and the clients of the cell. Without it, the cell has the service (`service`). */
|
|
322
|
+
readonly stack?: Stack | undefined;
|
|
298
323
|
/** This text names a system of the plan that the cell replaces with the service of this repository, or null. */
|
|
299
324
|
readonly substitutes: string | null;
|
|
300
325
|
readonly mode: KubeProxyMode;
|
|
301
326
|
readonly workers: number;
|
|
302
327
|
readonly trigger: Trigger<A>;
|
|
303
328
|
readonly pod: PodSettings;
|
|
304
|
-
|
|
329
|
+
/** This declaration is the settings of the service, or null for a server that is not the service (the stack `grpc`). */
|
|
330
|
+
readonly declaration: Declaration | null;
|
|
305
331
|
readonly workload: Workload;
|
|
306
332
|
readonly prediction: Prediction<A>;
|
|
307
333
|
/** This flag marks a calibration: its runs measure the propagation of the endpoints, and nothing fails. */
|
|
@@ -342,7 +368,7 @@ declare const REDIS = "redis";
|
|
|
342
368
|
*/
|
|
343
369
|
/**
|
|
344
370
|
* A line is one JSON line of a source of a run, with its stamp `t` in milliseconds on
|
|
345
|
-
* CLOCK_MONOTONIC. It is a request of a client, a record of the service or a probe answer. It is
|
|
371
|
+
* CLOCK_MONOTONIC. It is a request of a client, a record of the service, a line of grpc-go or a probe answer. It is
|
|
346
372
|
* also a pod, an EndpointSlice or an event of the API. Or it is the backends of the rules of a
|
|
347
373
|
* node, a kernel record, or an act of the lab. A record keeps each line as its source wrote it.
|
|
348
374
|
*/
|
|
@@ -430,15 +456,32 @@ declare const recordsOf: (lines: readonly Line[]) => RecordLine[];
|
|
|
430
456
|
declare const failuresOf: (lines: readonly Line[]) => Request[];
|
|
431
457
|
/** This function counts the outcomes of the requests, by outcome. */
|
|
432
458
|
declare const outcomesOf: (requests: readonly Request[]) => Record<string, number>;
|
|
459
|
+
/**
|
|
460
|
+
* This function orders the replicas by how many clients each one answered last, the most first,
|
|
461
|
+
* then by name. `answers` gives the backend of the last answer of each client, by client. A client
|
|
462
|
+
* of gRPC holds its connection to one replica, thus the first replica is the one whose stop the
|
|
463
|
+
* most clients see.
|
|
464
|
+
*/
|
|
465
|
+
declare const byClients: <T extends {
|
|
466
|
+
readonly pod: string;
|
|
467
|
+
}>(replicas: readonly T[], answers: ReadonlyMap<string, string>) => T[];
|
|
433
468
|
/** This function gives the requests of each node, by the name of the node. */
|
|
434
469
|
declare const byNode: (requests: readonly Request[]) => Map<string, Request[]>;
|
|
435
470
|
/** This function gives the time that the watcher first received a deletion timestamp of one of the pods, after a time. */
|
|
436
471
|
declare const deletionOf: (lines: readonly Line[], pods: readonly string[], after: number) => number | null;
|
|
437
472
|
/** This function gives the stamp of a transition of the service in a pod: the first matching record of a machine, after a time. */
|
|
438
473
|
declare const transitionOf: (lines: readonly Line[], pod: string, machine: string, match: (record: RecordLine) => boolean, after: number) => number | null;
|
|
439
|
-
/**
|
|
474
|
+
/**
|
|
475
|
+
* This function gives the line `stopping` of grpc-go in a pod, after a time. The server writes it at
|
|
476
|
+
* SIGTERM. Then GracefulStop closes its listener and sends GOAWAY on each connection.
|
|
477
|
+
*/
|
|
478
|
+
declare const stoppingOf: (lines: readonly Line[], pod: string, after: number) => number | null;
|
|
479
|
+
/**
|
|
480
|
+
* This function gives when the server of a pod stopped its intake. That is the start of the phase
|
|
481
|
+
* `stop intake` of the service, or the line `stopping` of grpc-go.
|
|
482
|
+
*/
|
|
440
483
|
declare const intakeOf: (lines: readonly Line[], pod: string, after: number) => number | null;
|
|
441
|
-
/** This function gives when the
|
|
484
|
+
/** This function gives when the server of a pod received its exit signal: a record of the service, or the line `stopping` of grpc-go. */
|
|
442
485
|
declare const signalOf: (lines: readonly Line[], pod: string, after: number) => number | null;
|
|
443
486
|
/**
|
|
444
487
|
* This function gives when the rules of a node stopped the selection of a backend: the first poll
|
|
@@ -506,6 +549,17 @@ interface RunFacts<A = Quantity> {
|
|
|
506
549
|
readonly left?: A | undefined;
|
|
507
550
|
/** These times are the SIGTERM of each replica, after the fault (`restarts`). */
|
|
508
551
|
readonly signals?: ReadonlyArray<A | null> | undefined;
|
|
552
|
+
/**
|
|
553
|
+
* These are the replicas of a window for each deleted replica (`each`). Each one is a replica of the
|
|
554
|
+
* run with a deletion after its start, with the stamps of its intake and of its deletion.
|
|
555
|
+
*/
|
|
556
|
+
readonly deletions?: readonly Deleted[] | undefined;
|
|
557
|
+
}
|
|
558
|
+
/** A deleted replica is a replica of a run that got a deletion. It gives its pod, and the stamps of its intake (or null) and of its deletion. */
|
|
559
|
+
interface Deleted {
|
|
560
|
+
readonly pod: string;
|
|
561
|
+
readonly intake: number | null;
|
|
562
|
+
readonly deletion: number;
|
|
509
563
|
}
|
|
510
564
|
/**
|
|
511
565
|
* A run check is the judgement of one run: if it agrees with the prediction, why, its main value,
|
|
@@ -552,14 +606,22 @@ declare const unitOf: (prediction: {
|
|
|
552
606
|
* `outcome`.
|
|
553
607
|
*/
|
|
554
608
|
declare const reusesReset: (request: Request, requests: readonly Request[], reuse: ResetReuse) => boolean;
|
|
555
|
-
/** This function gives a reference event of a run, as a stamp, or null when the run does not have it. */
|
|
556
|
-
declare const referenceOf: (run: KindRun, from: Reference) => number | null;
|
|
557
609
|
/**
|
|
558
|
-
* This function gives
|
|
559
|
-
*
|
|
560
|
-
|
|
610
|
+
* This function gives a reference event of a run, as a stamp, or null when the run does not have it.
|
|
611
|
+
* The references `deletion` and `intake` are those of the target, or of the replica `pod`.
|
|
612
|
+
*/
|
|
613
|
+
declare const referenceOf: (run: KindRun, from: Reference, pod?: string | undefined) => number | null;
|
|
614
|
+
/**
|
|
615
|
+
* This function gives the propagation of each node for the target of a run, or for the replica of
|
|
616
|
+
* the address `ip`. It is the time from its deletion (or its crash) to the first poll of the rules
|
|
617
|
+
* of the node without it. The change came between the poll before (`after`) and that poll (`by`).
|
|
618
|
+
*/
|
|
619
|
+
declare const propagationOf: (run: KindRun, from: number | null, ip?: string | undefined) => Record<string, Propagated | null>;
|
|
620
|
+
/**
|
|
621
|
+
* This function gives each replica of a run that got a deletion after the start of the run, in the
|
|
622
|
+
* sequence of the deletions. A rolling update deletes each old replica in turn.
|
|
561
623
|
*/
|
|
562
|
-
declare const
|
|
624
|
+
declare const deletionsOf: (run: KindRun) => Deleted[];
|
|
563
625
|
/** This function gives the facts of a run that each table reads: its failures by outcome and node, and the times of its target. */
|
|
564
626
|
declare const factsOf: (cell: KindCell, run: KindRun) => RunFacts;
|
|
565
627
|
/** This function judges one run against the prediction of its cell. */
|
|
@@ -722,5 +784,5 @@ declare const quantileOf: (sorted: readonly number[], p: number) => number;
|
|
|
722
784
|
*/
|
|
723
785
|
declare const medianInterval: (sorted: readonly number[]) => [number, number];
|
|
724
786
|
//#endregion
|
|
725
|
-
export {
|
|
726
|
-
//# sourceMappingURL=judge-
|
|
787
|
+
export { RecordLine as $, KindConfig as $t, TriggerV1 as A, REDIS as At, RunCheck as B, KubeletProbe as Bt, KindCellResult as C, slicesOf as Ct, PredictionV1 as D, HEALTH_PORT as Dt, LoadSample as E, CLIENT as Et, KIND_NOISE as F, showBand as Ft, judgeKind as G, Replica as Gt, checkRun as H, Prediction as Ht, KindDecision as I, Band as It, reusesReset as J, Template as Jt, propagationOf as K, ResetReuse as Kt, KindVerdict as L, Declaration as Lt, upgrade as M, bandOf as Mt, upgradeCell as N, seconds as Nt, RunCheckV1 as O, LOG_LABEL as Ot, Deleted as P, secondsOf as Pt, PodLine as Q, KUBE_PROXY_MODES as Qt, LAST_SLACK as R, KindCell as Rt, KindCellRecordV1 as S, sliceEmptyOf as St, KindVerdictV1 as T, transitionOf as Tt, deletionsOf as U, RedisDeclaration as Ut, RunFacts as V, PodSettings as Vt, factsOf as W, Reference as Wt, Endpoint as X, TriggerKind as Xt, unitOf as Y, Trigger as Yt, Line as Z, Workload as Zt, ClusterManifest as _, restartsAt as _t, STAGES as a, cidrRange as an, byNode as at, FORMAT_V1 as b, signalsOf as bt, readRecord as c, kubeProxyPatch as cn, emptyOf as ct, calibrationOf as d, scenarioProblems as dn, kernelCounts as dt, KindScenario as en, Request as et, cellsTable as f, outcomesOf as ft, BandV1 as g, requestsOf as gt, timeSpread as h, recordsOf as ht, quantileOf as i, cidrOverlap as in, byClients as it, judge as j, SERVER as jt, RunFactsV1 as k, PORT as kt, Judged as l, nodeNames as ln, failuresOf as lt, predictionText as m, podsOf as mt, medianInterval as n, NODE_IMAGES as nn, SliceLine as nt, kindCellRecordSchema as o, goDuration as on, deletionOf as ot, medianText as p, podsAt as pt, referenceOf as q, Stack as qt, middle as r, NodeImage as rn, backendsAt as rt, kindCellRecordV1Schema as s, kindConfig as sn, dropOf as st, RELATIVE as t, KubeProxyMode as tn, RulesLine as tt, TimeSpread as u, scenario as un, intakeOf as ut, EnvironmentManifest as v, rulesOf as vt, KindCellV1 as w, stoppingOf as wt, KindCellRecord as x, sliceDropOf as xt, FORMAT as y, signalOf as yt, Propagated as z, KindRun as zt };
|
|
788
|
+
//# sourceMappingURL=judge-Dwa6MqP4.d.ts.map
|
|
@@ -50,6 +50,16 @@ const outcomesOf = (requests) => {
|
|
|
50
50
|
for (const request of requests) out[request.o] = (out[request.o] ?? 0) + 1;
|
|
51
51
|
return out;
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* This function orders the replicas by how many clients each one answered last, the most first,
|
|
55
|
+
* then by name. `answers` gives the backend of the last answer of each client, by client. A client
|
|
56
|
+
* of gRPC holds its connection to one replica, thus the first replica is the one whose stop the
|
|
57
|
+
* most clients see.
|
|
58
|
+
*/
|
|
59
|
+
const byClients = (replicas, answers) => {
|
|
60
|
+
const count = (pod) => [...answers.values()].filter((backend) => backend === pod).length;
|
|
61
|
+
return [...replicas].sort((a, b) => count(b.pod) - count(a.pod) || a.pod.localeCompare(b.pod));
|
|
62
|
+
};
|
|
53
63
|
/** This function gives the requests of each node, by the name of the node. */
|
|
54
64
|
const byNode = (requests) => {
|
|
55
65
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -60,10 +70,18 @@ const byNode = (requests) => {
|
|
|
60
70
|
const deletionOf = (lines, pods, after) => podsOf(lines).find((pod) => pods.includes(pod.name) && pod.del && pod.t >= after)?.t ?? null;
|
|
61
71
|
/** This function gives the stamp of a transition of the service in a pod: the first matching record of a machine, after a time. */
|
|
62
72
|
const transitionOf = (lines, pod, machine, match, after) => recordsOf(lines).find((record) => record.p === pod && record.m === machine && record.at >= after && match(record))?.at ?? null;
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
/**
|
|
74
|
+
* This function gives the line `stopping` of grpc-go in a pod, after a time. The server writes it at
|
|
75
|
+
* SIGTERM. Then GracefulStop closes its listener and sends GOAWAY on each connection.
|
|
76
|
+
*/
|
|
77
|
+
const stoppingOf = (lines, pod, after) => lines.find((line) => line.ev === "stopping" && line["p"] === pod && line.t >= after)?.t ?? null;
|
|
78
|
+
/**
|
|
79
|
+
* This function gives when the server of a pod stopped its intake. That is the start of the phase
|
|
80
|
+
* `stop intake` of the service, or the line `stopping` of grpc-go.
|
|
81
|
+
*/
|
|
82
|
+
const intakeOf = (lines, pod, after) => transitionOf(lines, pod, "drain", (record) => record.to === "stop intake", after) ?? stoppingOf(lines, pod, after);
|
|
83
|
+
/** This function gives when the server of a pod received its exit signal: a record of the service, or the line `stopping` of grpc-go. */
|
|
84
|
+
const signalOf = (lines, pod, after) => transitionOf(lines, pod, "exit", (record) => record.on === "exit.request", after) ?? stoppingOf(lines, pod, after);
|
|
67
85
|
/**
|
|
68
86
|
* This function gives when the rules of a node stopped the selection of a backend: the first poll
|
|
69
87
|
* after a time without it. The change came after `prev`, the poll before it, and at the latest at `t`.
|
|
@@ -199,27 +217,29 @@ const reusesReset = (request, requests, reuse) => {
|
|
|
199
217
|
const life = valueIn(reuse.life, "ms") + NOISE;
|
|
200
218
|
return requests.some((earlier) => earlier.o === "reset" && earlier.p === request.p && earlier.lp === request.lp && earlier.t + earlier.d <= request.t && request.t - (earlier.t + earlier.d) <= life);
|
|
201
219
|
};
|
|
202
|
-
/**
|
|
203
|
-
|
|
220
|
+
/**
|
|
221
|
+
* This function gives a reference event of a run, as a stamp, or null when the run does not have it.
|
|
222
|
+
* The references `deletion` and `intake` are those of the target, or of the replica `pod`.
|
|
223
|
+
*/
|
|
224
|
+
const referenceOf = (run, from, pod = run.target?.pod) => {
|
|
204
225
|
const lines = run.lines;
|
|
205
226
|
if (from === "fault") return run.acts.fault;
|
|
206
227
|
if (from === "resume") return run.acts.resume;
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return intakeOf(lines, run.target.pod, run.start);
|
|
228
|
+
if (pod === void 0) return null;
|
|
229
|
+
if (from === "deletion") return deletionOf(lines, [pod], run.start);
|
|
230
|
+
return intakeOf(lines, pod, run.start);
|
|
211
231
|
};
|
|
212
232
|
/**
|
|
213
|
-
* This function gives the propagation of each node for the target of a run
|
|
214
|
-
* its deletion (or its crash) to the first poll of the rules
|
|
215
|
-
* came between the poll before (`after`) and that poll (`by`).
|
|
233
|
+
* This function gives the propagation of each node for the target of a run, or for the replica of
|
|
234
|
+
* the address `ip`. It is the time from its deletion (or its crash) to the first poll of the rules
|
|
235
|
+
* of the node without it. The change came between the poll before (`after`) and that poll (`by`).
|
|
216
236
|
*/
|
|
217
|
-
const propagationOf = (run, from) => {
|
|
237
|
+
const propagationOf = (run, from, ip = run.target?.ip) => {
|
|
218
238
|
const out = {};
|
|
219
|
-
if (
|
|
239
|
+
if (ip === void 0 || from === null) return out;
|
|
220
240
|
const nodes = [...new Set(run.lines.filter((line) => line.ev === "rules").map((line) => String(line["node"])))].sort();
|
|
221
241
|
for (const node of nodes) {
|
|
222
|
-
const drop = dropOf(run.lines, node, `${
|
|
242
|
+
const drop = dropOf(run.lines, node, `${ip}:${PORT}`, from);
|
|
223
243
|
out[node] = drop === null ? null : {
|
|
224
244
|
after: drop.prev === null ? null : seconds(sec(Math.max(0, drop.prev - from))),
|
|
225
245
|
by: seconds(sec(drop.t - from))
|
|
@@ -227,6 +247,20 @@ const propagationOf = (run, from) => {
|
|
|
227
247
|
}
|
|
228
248
|
return out;
|
|
229
249
|
};
|
|
250
|
+
/**
|
|
251
|
+
* This function gives each replica of a run that got a deletion after the start of the run, in the
|
|
252
|
+
* sequence of the deletions. A rolling update deletes each old replica in turn.
|
|
253
|
+
*/
|
|
254
|
+
const deletionsOf = (run) => run.replicas.flatMap((replica) => {
|
|
255
|
+
const deletion = referenceOf(run, "deletion", replica.pod);
|
|
256
|
+
return deletion === null ? [] : [{
|
|
257
|
+
pod: replica.pod,
|
|
258
|
+
intake: referenceOf(run, "intake", replica.pod),
|
|
259
|
+
deletion
|
|
260
|
+
}];
|
|
261
|
+
}).sort((a, b) => a.deletion - b.deletion);
|
|
262
|
+
/** This function tells if a prediction is a window for each deleted replica. */
|
|
263
|
+
const eachOf = (prediction) => prediction.kind === "window" && prediction.each === true;
|
|
230
264
|
/** This function gives the facts of a run that each table reads: its failures by outcome and node, and the times of its target. */
|
|
231
265
|
const factsOf = (cell, run) => {
|
|
232
266
|
const lines = run.lines;
|
|
@@ -247,7 +281,8 @@ const factsOf = (cell, run) => {
|
|
|
247
281
|
const at = sliceDropOf(lines, "lab", run.target.ip, from);
|
|
248
282
|
return at === null ? null : sec(at - from);
|
|
249
283
|
})()),
|
|
250
|
-
propagation: propagationOf(run, from)
|
|
284
|
+
propagation: propagationOf(run, from),
|
|
285
|
+
...eachOf(cell.prediction) ? { deletions: deletionsOf(run) } : {}
|
|
251
286
|
};
|
|
252
287
|
};
|
|
253
288
|
/** This function judges one run against the prediction of its cell. */
|
|
@@ -277,46 +312,64 @@ const checkRun = (cell, run) => {
|
|
|
277
312
|
return done(counted.length === 0, counted.length === 0 ? `no ${excluded === void 0 ? "request failed" : `request ended ${excluded.join(", ")}`}` : `${counted.length} ${what}: ${JSON.stringify(facts.outcomes)}`, counted.length);
|
|
278
313
|
}
|
|
279
314
|
case "window": {
|
|
280
|
-
const
|
|
281
|
-
const end = prediction.end === null ? null : referenceOf(run, prediction.end.from);
|
|
282
|
-
if (start === null || prediction.end !== null && end === null) return done(false, `the run has no ${start === null ? prediction.start.from : prediction.end.from} reference`, null);
|
|
315
|
+
const each = prediction.each === true;
|
|
283
316
|
const opens = secondsOf(prediction.start.band);
|
|
284
317
|
const closes = prediction.end === null ? null : secondsOf(prediction.end.band);
|
|
285
|
-
const
|
|
286
|
-
|
|
318
|
+
const droppedAfter = (origin, propagation) => (node) => {
|
|
319
|
+
const propagated = propagation[node];
|
|
320
|
+
return origin === null || propagated === void 0 || propagated === null ? null : origin + valueIn(propagated.by, "ms");
|
|
321
|
+
};
|
|
322
|
+
const pods = each ? (facts.deletions ?? []).map((deleted) => deleted.pod) : [run.target?.pod];
|
|
323
|
+
if (pods.length === 0) return done(false, "no replica of the run got a deletion", null);
|
|
324
|
+
const windows = [];
|
|
325
|
+
for (const pod of pods) {
|
|
326
|
+
const start = referenceOf(run, prediction.start.from, pod);
|
|
327
|
+
const end = prediction.end === null ? null : referenceOf(run, prediction.end.from, pod);
|
|
328
|
+
if (start === null || prediction.end !== null && end === null) return done(false, `the run has no ${start === null ? prediction.start.from : prediction.end.from} reference${each ? ` for ${pod}` : ""}`, null);
|
|
329
|
+
const origin = each ? referenceOf(run, "deletion", pod) : facts.deletion ?? (cell.trigger.kind === "crash" ? run.acts.fault : null);
|
|
330
|
+
const ip = run.replicas.find((replica) => replica.pod === pod)?.ip;
|
|
331
|
+
windows.push({
|
|
332
|
+
pod,
|
|
333
|
+
start,
|
|
334
|
+
end,
|
|
335
|
+
lo: start + opens.lo * 1e3 - NOISE,
|
|
336
|
+
hi: end === null ? Number.POSITIVE_INFINITY : end + closes.hi * 1e3 + NOISE,
|
|
337
|
+
dropped: droppedAfter(origin, each ? propagationOf(run, origin, ip) : facts.propagation)
|
|
338
|
+
});
|
|
339
|
+
}
|
|
287
340
|
const relevant = prediction.nodes === "paused" ? failures.filter((request) => request.n === run.paused) : failures;
|
|
288
341
|
const others = prediction.nodes === "paused" ? failures.filter((request) => request.n !== run.paused) : [];
|
|
289
342
|
const reuse = prediction.afterReset;
|
|
290
|
-
const late = relevant.filter((request) => request.t < lo || request.t > hi);
|
|
343
|
+
const late = relevant.filter((request) => windows.every((window) => request.t < window.lo || request.t > window.hi));
|
|
291
344
|
const reused = reuse === void 0 ? [] : late.filter((request) => reusesReset(request, failures, reuse));
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
const propagated = facts.propagation[node];
|
|
295
|
-
return origin === null || propagated === void 0 || propagated === null ? null : origin + valueIn(propagated.by, "ms");
|
|
296
|
-
};
|
|
297
|
-
const tail = late.filter((request) => {
|
|
298
|
-
const drop = request.t > hi && !reused.includes(request) ? dropped(request.n) : null;
|
|
345
|
+
const tailOf = (request) => reused.includes(request) ? void 0 : windows.find((window) => {
|
|
346
|
+
const drop = request.t > window.hi ? window.dropped(request.n) : null;
|
|
299
347
|
return drop !== null && request.t <= drop + NOISE;
|
|
300
348
|
});
|
|
349
|
+
const tail = late.filter((request) => tailOf(request) !== void 0);
|
|
301
350
|
const outside = late.filter((request) => !reused.includes(request) && !tail.includes(request));
|
|
351
|
+
const bounds = (window) => `[${sec(window.lo - window.start)}, ${sec(window.hi - window.start)}] s`;
|
|
302
352
|
const problems = [];
|
|
303
|
-
if (outside.length > 0) problems.push(`${outside.length} failed requests outside
|
|
353
|
+
if (outside.length > 0) problems.push(each ? `${outside.length} failed requests outside the window of each deleted replica: ${windows.map((window) => `${window.pod} ${bounds(window)}`).join(", ")} after its ${prediction.start.from}` : `${outside.length} failed requests outside ${bounds(windows[0])} after ${prediction.start.from}`);
|
|
304
354
|
if (others.length > 0) problems.push(`${others.length} failed requests on nodes whose kube-proxy did not pause`);
|
|
305
355
|
if (prediction.certain && relevant.length === 0) problems.push("no request failed in a window that the law opens");
|
|
306
|
-
|
|
356
|
+
const final = windows.reduce((a, b) => (b.end ?? Number.NEGATIVE_INFINITY) > (a.end ?? Number.NEGATIVE_INFINITY) ? b : a);
|
|
357
|
+
if (prediction.certain && final.end !== null && relevant.length > 0) {
|
|
307
358
|
const last = relevant.at(-1).t;
|
|
308
|
-
const earliest = end + closes.lo * 1e3 - NOISE - SLACK;
|
|
359
|
+
const earliest = final.end + closes.lo * 1e3 - NOISE - SLACK;
|
|
309
360
|
if (last < earliest) problems.push(`the last failed request started ${sec(earliest - last)} s before the window can end`);
|
|
310
361
|
}
|
|
311
362
|
const inside = relevant.filter((request) => !late.includes(request));
|
|
312
363
|
const span = inside.length === 0 ? 0 : sec(inside.at(-1).t - inside[0].t);
|
|
313
|
-
const
|
|
364
|
+
const where = each ? `the windows of ${windows.length} deleted replicas` : "the window";
|
|
365
|
+
const agrees = reused.length === 0 ? `${relevant.length} failed requests, all in ${where} (span ${span} s)` : `${inside.length} failed requests in ${where} (span ${span} s), and ${reused.length} on a port that a reset closed`;
|
|
314
366
|
const checked = done(problems.length === 0, problems.length === 0 ? agrees : problems.join("; "), relevant.length);
|
|
315
367
|
if (problems.length > 0 || tail.length === 0) return checked;
|
|
316
368
|
const last = tail.at(-1);
|
|
369
|
+
const after = tailOf(last);
|
|
317
370
|
return {
|
|
318
371
|
...checked,
|
|
319
|
-
tail: `${tail.length} failed requests after the window, the last ${sec(last.t - hi + NOISE)} s after it on ${last.n}, before the rules of ${last.n} dropped the target`
|
|
372
|
+
tail: each ? `${tail.length} failed requests after a window, the last ${sec(last.t - after.hi + NOISE)} s after the window of ${after.pod} on ${last.n}, before the rules of ${last.n} dropped ${after.pod}` : `${tail.length} failed requests after the window, the last ${sec(last.t - after.hi + NOISE)} s after it on ${last.n}, before the rules of ${last.n} dropped the target`
|
|
320
373
|
};
|
|
321
374
|
}
|
|
322
375
|
case "removal": {
|
|
@@ -673,7 +726,7 @@ const atBand = (from, band) => {
|
|
|
673
726
|
const predictionText = (prediction) => {
|
|
674
727
|
switch (prediction.kind) {
|
|
675
728
|
case "none": return prediction.outcomes === void 0 ? "no failure" : `no ${prediction.outcomes.join(", ")}`;
|
|
676
|
-
case "window": return `failures only from ${atBand(prediction.start.from, prediction.start.band)}${prediction.end === null ? "" : ` to ${atBand(prediction.end.from, prediction.end.band)}`}${prediction.nodes === "paused" ? ", on the paused node" : ""}${prediction.certain ? ", at least one" : ""}${prediction.afterReset === void 0 ? "" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}`;
|
|
729
|
+
case "window": return `failures only from ${atBand(prediction.start.from, prediction.start.band)}${prediction.end === null ? "" : ` to ${atBand(prediction.end.from, prediction.end.band)}`}${prediction.each === true ? ", of each deleted replica" : ""}${prediction.nodes === "paused" ? ", on the paused node" : ""}${prediction.certain ? ", at least one" : ""}${prediction.afterReset === void 0 ? "" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}`;
|
|
677
730
|
case "removal": return `Service empty in ${showBand(prediction.band)}`;
|
|
678
731
|
case "leaves": return `the replica leaves in ${showBand(prediction.band)}`;
|
|
679
732
|
case "restarts": return prediction.band === null ? "no restart" : `each replica SIGTERM in ${showBand(prediction.band)}`;
|
|
@@ -930,6 +983,7 @@ const recordOf = (a, main) => {
|
|
|
930
983
|
life: a,
|
|
931
984
|
outcome: z.string()
|
|
932
985
|
}).optional(),
|
|
986
|
+
each: z.boolean().optional(),
|
|
933
987
|
why: z.string()
|
|
934
988
|
}),
|
|
935
989
|
z.object({
|
|
@@ -964,6 +1018,7 @@ const recordOf = (a, main) => {
|
|
|
964
1018
|
id: z.string(),
|
|
965
1019
|
name: z.string(),
|
|
966
1020
|
template: z.enum(["T07", "T18"]),
|
|
1021
|
+
stack: z.enum(["service", "grpc"]).optional(),
|
|
967
1022
|
substitutes: z.string().nullable(),
|
|
968
1023
|
mode: z.enum(MODES),
|
|
969
1024
|
workers: count,
|
|
@@ -984,7 +1039,7 @@ const recordOf = (a, main) => {
|
|
|
984
1039
|
liveness: probe,
|
|
985
1040
|
startup: probe.nullable()
|
|
986
1041
|
}),
|
|
987
|
-
declaration,
|
|
1042
|
+
declaration: declaration.nullable(),
|
|
988
1043
|
workload: z.object({
|
|
989
1044
|
every: time,
|
|
990
1045
|
timeout: time
|
|
@@ -1009,7 +1064,12 @@ const recordOf = (a, main) => {
|
|
|
1009
1064
|
sliceEmpty: a.optional(),
|
|
1010
1065
|
rules: z.record(z.string(), a.nullable()).optional(),
|
|
1011
1066
|
left: a.optional(),
|
|
1012
|
-
signals: z.array(a.nullable()).optional()
|
|
1067
|
+
signals: z.array(a.nullable()).optional(),
|
|
1068
|
+
windows: z.array(z.object({
|
|
1069
|
+
pod: z.string(),
|
|
1070
|
+
intake: z.number().nullable(),
|
|
1071
|
+
deletion: z.number()
|
|
1072
|
+
})).optional()
|
|
1013
1073
|
});
|
|
1014
1074
|
return {
|
|
1015
1075
|
cell,
|
|
@@ -1071,6 +1131,6 @@ const readRecord = (data) => {
|
|
|
1071
1131
|
throw new Error(`a kind cell record has the format ${FORMAT} or ${FORMAT_V1}, not ${String(format)}`);
|
|
1072
1132
|
};
|
|
1073
1133
|
//#endregion
|
|
1074
|
-
export {
|
|
1134
|
+
export { HEALTH_PORT as $, byClients as A, podsOf as B, reusesReset as C, middle as D, medianInterval as E, failuresOf as F, signalOf as G, requestsOf as H, intakeOf as I, sliceEmptyOf as J, signalsOf as K, kernelCounts as L, deletionOf as M, dropOf as N, quantileOf as O, emptyOf as P, CLIENT as Q, outcomesOf as R, referenceOf as S, RELATIVE as T, restartsAt as U, recordsOf as V, rulesOf as W, stoppingOf as X, slicesOf as Y, transitionOf as Z, checkRun as _, calibrationOf as a, seconds as at, judgeKind as b, predictionText as c, FORMAT_V1 as d, LOG_LABEL as et, judge as f, LAST_SLACK as g, KIND_NOISE as h, readRecord as i, bandOf as it, byNode as j, backendsAt as k, timeSpread as l, upgradeCell as m, kindCellRecordSchema as n, REDIS as nt, cellsTable as o, secondsOf as ot, upgrade as p, sliceDropOf as q, kindCellRecordV1Schema as r, SERVER as rt, medianText as s, showBand as st, STAGES as t, PORT as tt, FORMAT as u, deletionsOf as v, unitOf as w, propagationOf as x, factsOf as y, podsAt as z };
|
|
1075
1135
|
|
|
1076
|
-
//# sourceMappingURL=judge-
|
|
1136
|
+
//# sourceMappingURL=judge-reWPMVr4.js.map
|