@mark1russell7/kind 0.12.2 → 0.12.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,6 +23,61 @@ const showBand = (band) => {
23
23
  const { lo, hi } = secondsOf(band);
24
24
  return `[${lo}, ${hi}] s`;
25
25
  };
26
+ /** This function rounds a time to the microsecond, thus a band has no float residue. */
27
+ const us = (value) => Math.round(value * 1e6) / 1e6;
28
+ /**
29
+ * This function gives the end band of a window with the backlog term (`BacklogTerm`). It adds n × d
30
+ * to each end of `end`, the end band without the term. The value n is the sessions on the target.
31
+ */
32
+ const backlogEnd = (end, term, sessions) => {
33
+ const ends = secondsOf(end);
34
+ const drain = secondsOf(term.drain);
35
+ return bandOf(us(ends.lo + sessions * drain.lo), us(ends.hi + sessions * drain.hi));
36
+ };
37
+ /** This function gives a count rounded to six places, thus 10 s / 20 ms gives 500 and not 499.99999. */
38
+ const counted = (value) => Math.round(value * 1e6) / 1e6;
39
+ /** This function gives the turn of a session in seconds: the mean period of a client times its sessions. */
40
+ const turnOf = (mean, connections) => {
41
+ const turn = valueIn(mean, "s") * connections;
42
+ if (!(turn > 0)) throw new RangeError(`a turn of ${turn} s cannot count requests`);
43
+ return turn;
44
+ };
45
+ /**
46
+ * This function gives the drain failures of one session on the target (SYS-43). A session fails a
47
+ * request each turn until the window closes. With n sessions on the target, the window closes up to
48
+ * n × d.hi after the band end (`backlogEnd`). Thus one session fails 0 to ceil(n × d.hi / turn)
49
+ * requests in the drain. The turn is the mean period of a client (`mean`) times its sessions
50
+ * (`connections`). The term r is not in this band.
51
+ */
52
+ const drainBand = (term, mean, connections, sessions) => {
53
+ const turn = turnOf(mean, connections);
54
+ return {
55
+ lo: 0,
56
+ hi: Math.ceil(counted(sessions * valueIn(term.drain.hi, "s") / turn))
57
+ };
58
+ };
59
+ /**
60
+ * This function gives the failed requests that the backlog term predicts (`BacklogTerm`). Each of
61
+ * the n sessions on the target gets a request each turn. The turn is the measured mean period of a
62
+ * client (`mean`) times the sessions of the client (`connections`). The requests of a session fail
63
+ * from the start of the window to the band end (`span`). That span holds `span / turn` requests, one
64
+ * less at its low edge and one more at its high edge. Each session fails r more requests in its drain time.
65
+ *
66
+ * A term with `drains` (SYS-43) also adds the drain failures of each session (`drainBand`).
67
+ */
68
+ const requestBand = (term, span, mean, connections, sessions) => {
69
+ const turn = turnOf(mean, connections);
70
+ const each = counted(valueIn(span, "s") / turn);
71
+ if (term.drains === true && term.drain === void 0) throw new RangeError("a term with drains has no drain time d");
72
+ const drained = term.drains === true ? drainBand({ drain: term.drain }, mean, connections, sessions) : {
73
+ lo: 0,
74
+ hi: 0
75
+ };
76
+ return {
77
+ lo: sessions * (Math.floor(each) - 1 + term.requests.lo + drained.lo),
78
+ hi: sessions * (Math.ceil(each) + 1 + term.requests.hi + drained.hi)
79
+ };
80
+ };
26
81
  //#endregion
27
82
  //#region src/names.ts
28
83
  /** These are the ports of the app: its server, and its health server for the probes. */
@@ -427,7 +482,8 @@ const windowOf = (prediction) => prediction.kind === "window" ? prediction : pre
427
482
  const observationAfter = (cell) => {
428
483
  const window = windowOf(cell.prediction);
429
484
  if (window === null || window.end === null) return null;
430
- return seconds(sec(valueIn(window.end.band.hi, "ms") + valueIn(cell.workload.timeout, "ms") + NOISE));
485
+ const drained = window.backlog === void 0 ? 0 : window.backlog.most * valueIn(window.backlog.drain.hi, "ms");
486
+ return seconds(sec(valueIn(window.end.band.hi, "ms") + drained + valueIn(cell.workload.timeout, "ms") + NOISE));
431
487
  };
432
488
  /**
433
489
  * This function gives the earliest end of the observation of a run, as a stamp, or null when its
@@ -585,6 +641,27 @@ const removalOfCycles = (cell, run, prediction, done) => {
585
641
  tail: inner.tail
586
642
  };
587
643
  };
644
+ /**
645
+ * This function counts the failed requests of a run with the backlog term (`requestBand`). The span
646
+ * is from the start of the window to the end band without the term. The turn of a session comes
647
+ * from the measured mean period of the clients. A run with no measured period cannot be counted.
648
+ */
649
+ const countOf = (cell, window, term, onTarget, period, failed) => {
650
+ if (period === void 0) return {
651
+ ok: false,
652
+ why: "the run has no measured period, thus the backlog term cannot count its failed requests"
653
+ };
654
+ const span = seconds(Math.round((valueIn(window.end.band.hi, "s") - valueIn(window.start.band.lo, "s")) * 1e6) / 1e6);
655
+ const band = requestBand(term, span, period.mean, cell.workload.connections ?? 1, onTarget);
656
+ const at = `the count [${band.lo}, ${band.hi}] of ${onTarget} session${onTarget === 1 ? "" : "s"} on the target at a mean period of ${Math.round(valueIn(period.mean, "ms") * 1e3) / 1e3} ms`;
657
+ return failed >= band.lo && failed <= band.hi ? {
658
+ ok: true,
659
+ why: `in ${at}`
660
+ } : {
661
+ ok: false,
662
+ why: `${failed} failed requests, outside ${at}`
663
+ };
664
+ };
588
665
  /** This function judges one run against the prediction of its cell. */
589
666
  const checkRun = (cell, run) => {
590
667
  const lines = run.lines;
@@ -614,7 +691,10 @@ const checkRun = (cell, run) => {
614
691
  case "window": {
615
692
  const each = prediction.each === true;
616
693
  const opens = secondsOf(prediction.start.band);
617
- const closes = prediction.end === null ? null : secondsOf(prediction.end.band);
694
+ const backlog = prediction.backlog;
695
+ const onTarget = facts.sessions?.onTarget;
696
+ if (backlog !== void 0 && (prediction.end === null || prediction.end.from !== prediction.start.from || onTarget === void 0)) return done(false, onTarget === void 0 ? "the run has no sessions on the target at the fault, thus the backlog term has no n" : "a window with the backlog term has an end with the reference of its start", null);
697
+ const closes = prediction.end === null ? null : secondsOf(backlog === void 0 ? prediction.end.band : backlogEnd(prediction.end.band, backlog, onTarget));
618
698
  const droppedAfter = (origin, propagation) => (node) => {
619
699
  const propagated = propagation[node];
620
700
  return origin === null || propagated === void 0 || propagated === null ? null : origin + valueIn(propagated.by, "ms");
@@ -708,6 +788,8 @@ const checkRun = (cell, run) => {
708
788
  if (others.length > 0) problems.push(`${others.length} failed requests on nodes whose kube-proxy did not pause`);
709
789
  if (wrong.length > 0) problems.push(`${wrong.length} failed requests in the window ended ${JSON.stringify(outcomesOf(wrong))}, not ${allowed.join(", ")}`);
710
790
  if (prediction.certain && relevant.length === 0) problems.push("no request failed in a window that the law opens");
791
+ const counted = backlog === void 0 ? null : countOf(cell, prediction, backlog, onTarget, facts.period, relevant.length);
792
+ if (counted !== null && !counted.ok) problems.push(counted.why);
711
793
  const final = windows.reduce((a, b) => (b.end ?? Number.NEGATIVE_INFINITY) > (a.end ?? Number.NEGATIVE_INFINITY) ? b : a);
712
794
  if (flaps && prediction.certain && relevant.length > 0) windows.forEach((window, k) => {
713
795
  const own = relevant.filter((request) => request.t >= window.lo && request.t <= window.hi);
@@ -725,7 +807,7 @@ const checkRun = (cell, run) => {
725
807
  const inside = relevant.filter((request) => !late.includes(request));
726
808
  const span = inside.length === 0 ? 0 : sec(inside.at(-1).t - inside[0].t);
727
809
  const where = each ? `the windows of ${windows.length} deleted replicas` : flaps ? `the windows of ${windows.length} cycles` : "the window";
728
- 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`;
810
+ 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`) + (counted === null ? "" : `, ${counted.why}`);
729
811
  const checked = done(problems.length === 0, problems.length === 0 ? agrees : problems.join("; "), relevant.length);
730
812
  if (problems.length > 0) return checked;
731
813
  const notes = [];
@@ -1155,7 +1237,7 @@ const upgradeTrigger = (trigger) => {
1155
1237
  };
1156
1238
  };
1157
1239
  const upgradeWindow = (window) => {
1158
- const { afterReset, propagation, ...rest } = window;
1240
+ const { afterReset, propagation, backlog, ...rest } = window;
1159
1241
  return {
1160
1242
  ...rest,
1161
1243
  start: {
@@ -1170,7 +1252,11 @@ const upgradeWindow = (window) => {
1170
1252
  life: seconds(afterReset.life),
1171
1253
  outcome: afterReset.outcome
1172
1254
  } },
1173
- ...propagation === void 0 ? {} : { propagation: band(propagation) }
1255
+ ...propagation === void 0 ? {} : { propagation: band(propagation) },
1256
+ ...backlog === void 0 ? {} : { backlog: {
1257
+ ...backlog,
1258
+ drain: band(backlog.drain)
1259
+ } }
1174
1260
  };
1175
1261
  };
1176
1262
  const upgradePrediction = (prediction) => {
@@ -1318,7 +1404,7 @@ const atBand = (from, band) => {
1318
1404
  const predictionText = (prediction) => {
1319
1405
  switch (prediction.kind) {
1320
1406
  case "none": return prediction.outcomes === void 0 ? "no failure" : `no ${prediction.outcomes.join(", ")}`;
1321
- 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.flaps === true ? ", in each cycle" : ""}${prediction.nodes === "paused" ? ", on the paused node" : ""}${prediction.certain ? ", at least one" : ""}${prediction.outcomes === void 0 ? "" : `, each one ${prediction.outcomes.join(" or ")}`}${prediction.afterReset === void 0 ? "" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}${prediction.propagation === void 0 ? "" : `, the rules drop the replica in ${showBand(prediction.propagation)}`}`;
1407
+ 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.flaps === true ? ", in each cycle" : ""}${prediction.nodes === "paused" ? ", on the paused node" : ""}${prediction.certain ? ", at least one" : ""}${prediction.outcomes === void 0 ? "" : `, each one ${prediction.outcomes.join(" or ")}`}${prediction.afterReset === void 0 ? "" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}${prediction.propagation === void 0 ? "" : `, the rules drop the replica in ${showBand(prediction.propagation)}`}${prediction.backlog === void 0 ? "" : `, the end n × ${showBand(prediction.backlog.drain)} later and ${prediction.backlog.requests.lo}–${prediction.backlog.requests.hi} more failures for each of the n sessions on the target${prediction.backlog.drains === true ? ", and its failures each turn in the drain of the n sessions" : ""}, counted at the measured period`}`;
1322
1408
  case "removal": return `Service empty in ${showBand(prediction.band)}${prediction.flaps === true ? " after each fault, and ready again before the next" : ""}${prediction.window === void 0 ? "" : `; ${predictionText(prediction.window)}`}`;
1323
1409
  case "leaves": return `the replica leaves in ${showBand(prediction.band)}${prediction.window === void 0 ? "" : `; ${predictionText(prediction.window)}`}`;
1324
1410
  case "restarts": return prediction.band === null ? "no restart" : `each replica SIGTERM in ${showBand(prediction.band)}`;
@@ -1618,6 +1704,15 @@ const recordOf = (a, main) => {
1618
1704
  propagation: band.optional(),
1619
1705
  flaps: z.boolean().optional(),
1620
1706
  outcomes: z.array(z.string()).optional(),
1707
+ backlog: z.object({
1708
+ drain: band,
1709
+ requests: z.object({
1710
+ lo: count,
1711
+ hi: count
1712
+ }),
1713
+ most: count,
1714
+ drains: z.boolean().optional()
1715
+ }).optional(),
1621
1716
  why: z.string()
1622
1717
  });
1623
1718
  const none = z.object({
@@ -1843,6 +1938,6 @@ const readRecord = (data) => {
1843
1938
  throw new Error(`a kind cell record has the format ${FORMAT} or ${FORMAT_V1}, not ${String(format)}`);
1844
1939
  };
1845
1940
  //#endregion
1846
- export { restartsAt as $, RELATIVE as A, dropOf as B, observationEnd as C, secondsOf as Ct, reusesReset as D, referenceOf as E, TCP_STATE as F, kernelCounts as G, failuresOf as H, backendsAt as I, podsAt as J, outcomesOf as K, byClients as L, middle as M, quantileOf as N, unitOf as O, HBONE_PORT as P, restartOf as Q, byNode as R, observationAfter as S, seconds as St, propagationSource as T, intakeOf as U, emptyOf as V, isTunnelState as W, recordsOf as X, podsOf as Y, requestsOf as Z, checkRun as _, LOG_LABEL as _t, calibrationOf as a, sliceEmptyOf as at, factsOf as b, SERVER as bt, predictionText as c, slicesOf as ct, FORMAT_V1 as d, transitionOf as dt, rulesOf as et, judge as f, tunnelsOf as ft, LAST_SLACK as g, HEALTH_PORT as gt, KIND_NOISE as h, CLIENT as ht, readRecord as i, sliceDropOf as it, medianInterval as j, updatesOf as k, timeSpread as l, socketsOf as lt, upgradeCell as m, ztunnelReplaced as mt, kindCellRecordSchema as n, signalOf as nt, cellsTable as o, sliceReadyAt as ot, upgrade as p, ztunnelPodsOf as pt, periodOf as q, kindCellRecordV1Schema as r, signalsOf as rt, medianText as s, sliceReadyOf as st, STAGES as t, sessionsAt as tt, FORMAT as u, stoppingOf as ut, cyclesOf as v, PORT as vt, propagationOf as w, showBand as wt, judgeKind as x, bandOf as xt, deletionsOf as y, REDIS as yt, deletionOf as z };
1941
+ export { restartsAt as $, RELATIVE as A, dropOf as B, observationEnd as C, drainBand as Ct, reusesReset as D, showBand as Dt, referenceOf as E, secondsOf as Et, TCP_STATE as F, kernelCounts as G, failuresOf as H, backendsAt as I, podsAt as J, outcomesOf as K, byClients as L, middle as M, quantileOf as N, unitOf as O, HBONE_PORT as P, restartOf as Q, byNode as R, observationAfter as S, bandOf as St, propagationSource as T, seconds as Tt, intakeOf as U, emptyOf as V, isTunnelState as W, recordsOf as X, podsOf as Y, requestsOf as Z, checkRun as _, LOG_LABEL as _t, calibrationOf as a, sliceEmptyOf as at, factsOf as b, SERVER as bt, predictionText as c, slicesOf as ct, FORMAT_V1 as d, transitionOf as dt, rulesOf as et, judge as f, tunnelsOf as ft, LAST_SLACK as g, HEALTH_PORT as gt, KIND_NOISE as h, CLIENT as ht, readRecord as i, sliceDropOf as it, medianInterval as j, updatesOf as k, timeSpread as l, socketsOf as lt, upgradeCell as m, ztunnelReplaced as mt, kindCellRecordSchema as n, signalOf as nt, cellsTable as o, sliceReadyAt as ot, upgrade as p, ztunnelPodsOf as pt, periodOf as q, kindCellRecordV1Schema as r, signalsOf as rt, medianText as s, sliceReadyOf as st, STAGES as t, sessionsAt as tt, FORMAT as u, stoppingOf as ut, cyclesOf as v, PORT as vt, propagationOf as w, requestBand as wt, judgeKind as x, backlogEnd as xt, deletionsOf as y, REDIS as yt, deletionOf as z };
1847
1942
 
1848
- //# sourceMappingURL=judge-BRxKwiSp.js.map
1943
+ //# sourceMappingURL=judge-45z0yZ-r.js.map