@mark1russell7/harness 0.11.21 → 0.11.23

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 CHANGED
@@ -125,6 +125,9 @@ A step-down (`step-down`) moves the leadership to the first follower (`rabbitmq-
125
125
  - **An HTTP client** (`Cell.client`: `go-http`, `http-agent` or `undici`) is a client of the harness from its own image. It sends a GET of the root, then a second request (`probe.http`):
126
126
  - after the signal (`inflight`), into the fault
127
127
  - or by itself, a gap after its first reply (`gap`), for the keep-alive race.
128
+ - **A client of a keyed limiter.** The flag `limiter-key` (`probe.http.options`) makes the client of undici or of Go send a key of its process in the header `x-limiter-key`. Thus each run has its own count in the limiter of the target.
129
+ - The flag `retry` sends each request of undici through its `RetryAgent`, at the defaults of undici. Each `retry-` flag sets one option over them.
130
+ - The client of undici then writes an `attempt` line for each send of a request, with its number `n` and its socket. A `RetryAgent` that gives up gives `UND_ERR_REQ_RETRY` on the `error` line, with the status of the last reply and the count of sends.
128
131
  - **The lines of an HTTP client.** It writes the lines of the TCP client, with the number `i` of the request on each line of a request. It also writes the connection of each try of a request (`request`, then `retry`), with its local address and a flag for a reused connection. Each connection gets a `conn` line, and each end of a connection a line (`eof`, `conn-error`, `closed`). The end of the second request is a `done` line.
129
132
  - **A target with a script.** A target can take files before it starts (`Target.files`), for example the script of a Node server on the stock Node image. A target that writes JSON lines (`Target.lines`) gives them to each run (`Run.server`). The manifest holds the sha256 of each file and the first line of the target.
130
133
  - **The WebSocket client** (`Cell.client` is `procedures-ws`) is the `WebSocketTransport` of `@procedures/client` 0.7.0: the `client` program of the `procedures` subject. The cell gives each of its settings (`probe.ws`): the class of its sockets, the heartbeat, the reconnect, and the times of a call.
@@ -330,6 +333,11 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
330
333
  - **A driver band** (`measureDriver`, `driver.ts`). A driver with more than one socket names the socket of its band (`DriverBand.socket`).
331
334
  - The band counts from its reference on that socket. It ends at a line of the client (`ends`), or at the report of the kernel on the socket.
332
335
  - A band that a line ends is a timer of the driver. It counts from the last segment with data that the socket received or sent. The idle timer of a Node socket starts again at each read and each write of data.
336
+ - A band can name that segment as its reference (`last-data`). For example, an idle gRPC client goes to IDLE at a GOAWAY of its proxy. The idle timer of the proxy counts from the last data of the stream.
337
+ - A reference that this judge does not know (for example `ping-sent`) does not come. Thus its band is a finding. Before 0.11.22, such a reference counted from the first close of a socket of the peer. Through a proxy, that close can be the idle close of a socket toward the target. Since 0.11.23, the TCP judge does the same.
338
+ - A retry after a refusal counts from that refusal (`last-reply`), for example a 429 with `Retry-After`. That is the last segment with data that a socket of the client received before the line that ends the band. Each band gets its own, thus each retry counts from the refusal before it.
339
+ - The retry can go on a new connection. For example, undici closes an idle socket 3 s after a reply of a server at its defaults, before a retry 30 s later. Thus `last-reply` reads each socket of the client.
340
+ - The bands from `last-reply` follow one another. Thus the deadline of the client and the wait of the run hold their sum: five retries of 30 s take 150 s (`longestOf`).
333
341
  - The MongoDB client is ready when its first write has its reply and its monitor streams. The monitor sends its first awaited hello `minHeartbeatFrequencyMS` (500 ms) after its first check. A fault before that hello meets the start of the stream, not the stream that a cell measures.
334
342
  - Each later band (`then`) has its own reference and line. A later band outside its band, or absent, is a finding. So is a line that the prediction names as quiet, after the fault.
335
343
  - For a silent prediction, a flow of an open socket of the driver that a router forgot is a finding.
@@ -312,8 +312,13 @@ interface Toxic {
312
312
  *
313
313
  * A connect timer counts from the change of its socket to SYN_SENT (`syn-sent`). A pooler connects
314
314
  * to the server by itself, thus the kernel stamps that connect, and no line of the client does.
315
+ *
316
+ * A retry of a client after a refusal counts from the refusal (`last-reply`), for example a 429 with
317
+ * `Retry-After`. That is the last segment with data that a socket of the client received before
318
+ * the line that ends the band. The retry can go on a new connection. Each band of a driver has
319
+ * its own, thus each retry counts from the refusal before it.
315
320
  */
316
- type Reference = "last-received" | "last-data" | "first-retransmission" | "request-sent" | "peer-closed" | "fault" | "client-start" | "ping-sent" | "first-sent" | "syn-sent";
321
+ type Reference = "last-received" | "last-reply" | "last-data" | "first-retransmission" | "request-sent" | "peer-closed" | "fault" | "client-start" | "ping-sent" | "first-sent" | "syn-sent";
317
322
  /**
318
323
  * This prediction tells when a connection moves again after a stall. That occurs at the first
319
324
  * firing of its retransmission timer after the stall stops, and its reply comes back one round
@@ -1123,4 +1128,4 @@ interface RunTopology {
1123
1128
  }
1124
1129
  //#endregion
1125
1130
  export { Subject as $, Prediction as A, RequestPrediction as B, KernelRecord as C, MiddleboxForm as D, MiddleboxExpectation as E, Recovers as F, RunPart as G, RttRange as H, Reference as I, SentinelStage as J, RunTopology as K, ReplicaSet as L, ProxyHop as M, ProxyTime as N, Observer as O, Reconnects as P, StageBand as Q, ReplicaSetStage as R, ImageSource as S, MiddleboxEvent as T, Run as U, RouterHop as V, RunGroup as W, ShippedImage as X, Shipped as Y, Stage as Z, Hop as _, Cluster as a, latestEndOf as at, HopSignal as b, DriverBand as c, FaultPlace as d, Target as et, Group as f, HarnessImage as g, GroupRole as h, ClientImage as i, isHopPlace as it, ProbeEvent as j, PinnedImage as k, DriverStep as l, GroupPrediction as m, Cell as n, Toxic as nt, ClusterClientAt as o, GroupPlace as p, SentinelEvent as q, Client as r, WsProbe as rt, ClusterStage as s, BuildImage as t, Topology as tt, Fault as u, HopEvent as v, LineMatch as w, HttpProbe as x, HopPlace as y, Replies as z };
1126
- //# sourceMappingURL=cell-BxBZUssi.d.ts.map
1131
+ //# sourceMappingURL=cell-Dn1TvJcA.d.ts.map
package/build/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as Subject, A as Prediction, B as RequestPrediction, C as KernelRecord, D as MiddleboxForm, E as MiddleboxExpectation, F as Recovers, G as RunPart, H as RttRange, I as Reference, J as SentinelStage, K as RunTopology, L as ReplicaSet, M as ProxyHop, N as ProxyTime, O as Observer, P as Reconnects, Q as StageBand, R as ReplicaSetStage, S as ImageSource, T as MiddleboxEvent, U as Run, V as RouterHop, W as RunGroup, X as ShippedImage, Y as Shipped, Z as Stage, _ as Hop, a as Cluster, at as latestEndOf, b as HopSignal, c as DriverBand, d as FaultPlace, et as Target, f as Group, g as HarnessImage, h as GroupRole, i as ClientImage, it as isHopPlace, j as ProbeEvent, k as PinnedImage, l as DriverStep, m as GroupPrediction, n as Cell, nt as Toxic, o as ClusterClientAt, p as GroupPlace, q as SentinelEvent, r as Client, rt as WsProbe, s as ClusterStage, t as BuildImage, tt as Topology, u as Fault, v as HopEvent, w as LineMatch, x as HttpProbe, y as HopPlace, z as Replies } from "./cell-BxBZUssi.js";
1
+ import { $ as Subject, A as Prediction, B as RequestPrediction, C as KernelRecord, D as MiddleboxForm, E as MiddleboxExpectation, F as Recovers, G as RunPart, H as RttRange, I as Reference, J as SentinelStage, K as RunTopology, L as ReplicaSet, M as ProxyHop, N as ProxyTime, O as Observer, P as Reconnects, Q as StageBand, R as ReplicaSetStage, S as ImageSource, T as MiddleboxEvent, U as Run, V as RouterHop, W as RunGroup, X as ShippedImage, Y as Shipped, Z as Stage, _ as Hop, a as Cluster, at as latestEndOf, b as HopSignal, c as DriverBand, d as FaultPlace, et as Target, f as Group, g as HarnessImage, h as GroupRole, i as ClientImage, it as isHopPlace, j as ProbeEvent, k as PinnedImage, l as DriverStep, m as GroupPrediction, n as Cell, nt as Toxic, o as ClusterClientAt, p as GroupPlace, q as SentinelEvent, r as Client, rt as WsProbe, s as ClusterStage, t as BuildImage, tt as Topology, u as Fault, v as HopEvent, w as LineMatch, x as HttpProbe, y as HopPlace, z as Replies } from "./cell-Dn1TvJcA.js";
2
2
  import { Quantity } from "@mark1russell7/quantity";
3
3
  import "testcontainers";
4
4
  //#region src/application.d.ts
@@ -130,11 +130,16 @@ export declare const socketAt: (events: readonly ProbeEvent[], role: string, at:
130
130
  * the driver. It counts from the last segment with data that the socket received or sent.
131
131
  * - `request-sent`: the send of the operation into the fault, the second `sent` line.
132
132
  * - `first-retransmission`: the first retransmission of the RTO timer on the socket.
133
+ * - `last-data`: the last segment with data that the socket received or sent before the fault
134
+ * held, for a band of the kernel too. An idle timer of a peer counts from the end of a stream.
133
135
  * - `peer-closed`: the change of state of the socket of the target, when its process dies.
134
136
  * - `fault`: the start of the commit of the fault. `client-start`: the `connecting` line.
135
137
  * - `first-sent`: the first segment with data that the socket sends after the fault.
138
+ * - `last-reply`: the refusal that a retry follows. It is the last segment with data that a socket
139
+ * of the client received before the end of the band (`end`). The retry can go on another socket.
140
+ * A reference that this judge does not know (`ping-sent`) does not come: its band is a finding.
136
141
  */
137
- export declare const referenceOf: (run: Run, from: Reference, role: string, data: boolean) => number | undefined;
142
+ export declare const referenceOf: (run: Run, from: Reference, role: string, data: boolean, end?: number) => number | undefined;
138
143
  /**
139
144
  * This function reads one run of a driver cell.
140
145
  * - The band of the cell counts from its reference on its socket. It ends at its line, or at the
package/build/index.js CHANGED
@@ -736,20 +736,27 @@ const outsetOf = (run) => {
736
736
  * the driver. It counts from the last segment with data that the socket received or sent.
737
737
  * - `request-sent`: the send of the operation into the fault, the second `sent` line.
738
738
  * - `first-retransmission`: the first retransmission of the RTO timer on the socket.
739
+ * - `last-data`: the last segment with data that the socket received or sent before the fault
740
+ * held, for a band of the kernel too. An idle timer of a peer counts from the end of a stream.
739
741
  * - `peer-closed`: the change of state of the socket of the target, when its process dies.
740
742
  * - `fault`: the start of the commit of the fault. `client-start`: the `connecting` line.
741
743
  * - `first-sent`: the first segment with data that the socket sends after the fault.
744
+ * - `last-reply`: the refusal that a retry follows. It is the last segment with data that a socket
745
+ * of the client received before the end of the band (`end`). The retry can go on another socket.
746
+ * A reference that this judge does not know (`ping-sent`) does not come: its band is a finding.
742
747
  */
743
- const referenceOf = (run, from, role, data) => {
748
+ const referenceOf = (run, from, role, data, end = Infinity) => {
744
749
  const ready = run.events.find((event) => event.ev === "ready");
745
750
  const before = run.fault?.t1 ?? ready?.t ?? Infinity;
746
751
  if (from === "fault") return run.fault?.t0;
747
752
  if (from === "client-start") return (run.events.find((event) => event.ev === "connecting") ?? run.events[0])?.t;
748
753
  if (from === "request-sent") return run.events.filter((event) => event.ev === "sent")[1]?.t;
754
+ if (from === "last-reply") return run.kernel.filter((record) => record.netns === run.netns && record.mono < end && record.ev === "rx" && (record.seg?.len.value ?? 0) > 0).at(-1)?.mono;
749
755
  const { socket, place } = kernelOf(run, role, before);
750
756
  if (place === void 0) return void 0;
751
- if (from === "last-received") {
752
- const counts = (record) => data ? (record.ev === "rx" || record.ev === "tx") && (record.seg?.len.value ?? 0) > 0 : record.ev === "rx";
757
+ if (from === "last-received" || from === "last-data") {
758
+ const withData = data || from === "last-data";
759
+ const counts = (record) => withData ? (record.ev === "rx" || record.ev === "tx") && (record.seg?.len.value ?? 0) > 0 : record.ev === "rx";
753
760
  return place.records.filter((record) => record.mono <= before && counts(record)).at(-1)?.mono;
754
761
  }
755
762
  if (from === "first-sent") return place.records.find((record) => record.ev === "tx" && record.mono > before && (record.seg?.len.value ?? 0) > 0)?.mono;
@@ -758,6 +765,7 @@ const referenceOf = (run, from, role, data) => {
758
765
  const request = run.events.filter((event) => event.ev === "sent")[1]?.t ?? before;
759
766
  return place.records.find((record) => record.ev === "retrans" && record.why === "rto" && record.mono > request)?.mono;
760
767
  }
768
+ if (from !== "peer-closed") return void 0;
761
769
  const closed = (socket === void 0 ? void 0 : peerPlace(run, socket.local))?.records.find((record) => record.ev === "state" && oldState$1(record) === 1)?.mono;
762
770
  if (closed !== void 0 || run.topology === void 0) return closed;
763
771
  return proxiedClose(run);
@@ -807,13 +815,19 @@ const reportOf = (place, after) => {
807
815
  outcome: { kind: "eof" }
808
816
  };
809
817
  };
810
- /** This function gives the latest instant of the bands of a driver prediction, in seconds from their references. */
818
+ /**
819
+ * This function gives the latest instant of the bands of a driver prediction, in seconds from their
820
+ * references. A band from the refusal before it (`last-reply`) follows the band before it. Thus the
821
+ * times of those bands add: five retries of 30 s end 150 s after the first refusal.
822
+ */
811
823
  const longestOf = (cell) => {
812
824
  const { prediction } = cell;
813
825
  if (prediction.detects === null) return valueIn(prediction.window, "s");
814
- const steps = (prediction.driver?.then ?? []).map((step) => valueIn(step.high, "s"));
826
+ const then = prediction.driver?.then ?? [];
827
+ const steps = then.map((step) => valueIn(step.high, "s"));
815
828
  const own = prediction.detects ? valueIn(prediction.high, "s") : valueIn(prediction.window, "s");
816
- return Math.max(own, ...steps);
829
+ const chained = [...prediction.detects && prediction.from === "last-reply" ? [own] : [], ...then.filter((step) => step.from === "last-reply").map((step) => valueIn(step.high, "s"))];
830
+ return Math.max(own, ...steps, chained.reduce((total, high) => total + high, 0));
817
831
  };
818
832
  /**
819
833
  * This function tells if a run of a driver cell has its outcome, so that its wait can stop. For a
@@ -951,7 +965,7 @@ const measureDriver = (cell, run) => {
951
965
  }
952
966
  const stepTimes = (driver.then ?? []).map((step) => {
953
967
  const line = lineAfter(events, step.ends, after);
954
- const from = referenceOf(run, step.from, step.socket ?? driver.socket, true);
968
+ const from = referenceOf(run, step.from, step.socket ?? driver.socket, true, line?.t);
955
969
  if (line === void 0 || from === void 0) {
956
970
  findings.push(line === void 0 ? `${step.what}: ${wordsOf(step.ends)} never came` : `${step.what}: its reference (${step.from}) never came`);
957
971
  return {
@@ -981,7 +995,7 @@ const measureDriver = (cell, run) => {
981
995
  finding: findings[0] ?? null
982
996
  };
983
997
  const line = lineAfter(events, driver.reply.ends, after);
984
- const from = referenceOf(run, driver.reply.from, driver.socket, true);
998
+ const from = referenceOf(run, driver.reply.from, driver.socket, true, line?.t);
985
999
  if (line === void 0 || from === void 0) return {
986
1000
  ...base,
987
1001
  ...nothing,
@@ -1020,7 +1034,7 @@ const measureDriver = (cell, run) => {
1020
1034
  void: voided,
1021
1035
  finding: findings[0] ?? `nothing detected it: ${prediction.timer} never fired`
1022
1036
  };
1023
- const reference = referenceOf(run, prediction.from, driver.socket, driver.ends !== void 0);
1037
+ const reference = referenceOf(run, prediction.from, driver.socket, driver.ends !== void 0, ended);
1024
1038
  if (reference === void 0) return {
1025
1039
  ...base,
1026
1040
  ...nothing,
@@ -1483,7 +1497,7 @@ const count = (event, field) => event === void 0 ? 0 : Number(event[field] ?? 0)
1483
1497
  /** This function gives the view of the client on the same interval: TCP_INFO, with a sample each 2 ms. */
1484
1498
  const socketVantage = (cell, events, ready, request) => {
1485
1499
  const { from } = cell.prediction.detects === true ? cell.prediction : { from: void 0 };
1486
- if (from === void 0 || from === "peer-closed" || from === "fault" || from === "ping-sent" || from === "first-sent" || from === "last-data") return null;
1500
+ if (from === void 0 || from === "peer-closed" || from === "fault" || from === "ping-sent" || from === "first-sent" || from === "last-data" || from === "last-reply") return null;
1487
1501
  const ended = events.find((event) => event.ev === "error" || event.ev === "eof");
1488
1502
  const reference = from === "last-received" ? ready : from === "request-sent" ? request : events.find((event) => event.ev === "kernel" && event.t >= request.t && count(event, "retransmits") > 0);
1489
1503
  return ended === void 0 || reference === void 0 ? null : between(reference.t, ended.t);
@@ -1824,7 +1838,7 @@ const measureSocket = (cell, run) => {
1824
1838
  if (from === "fault") return run.fault?.t0;
1825
1839
  if (from === "client-start") return (events.find((event) => event.ev === "connecting") ?? events[0])?.t;
1826
1840
  if (from === "ping-sent") return firstUnanswered(pings, socket, ended)?.at;
1827
- const record = from === "last-received" ? socket.filter((each) => each.ev === "rx" && each.mono <= before).at(-1) : from === "last-data" ? socket.filter((each) => each.ev === "rx" && (each.seg?.len.value ?? 0) > 0 && each.mono <= (ended ?? before)).at(-1) : from === "first-retransmission" ? retransmitted.find((each) => each.why === "rto" && each.mono > before) : from === "first-sent" ? socket.find((each) => each.ev === "tx" && each.mono > before && (each.seg?.len.value ?? 0) > 0) : from === "syn-sent" ? socket.find((each) => each.ev === "state" && newState(each) === 2) : kernel.find((each) => each.netns === peerNetns && each.cookie === peerCookie && each.ev === "state" && oldState(each) === 1);
1841
+ const record = from === "last-received" ? socket.filter((each) => each.ev === "rx" && each.mono <= before).at(-1) : from === "last-data" ? socket.filter((each) => each.ev === "rx" && (each.seg?.len.value ?? 0) > 0 && each.mono <= (ended ?? before)).at(-1) : from === "last-reply" ? socket.filter((each) => each.ev === "rx" && (each.seg?.len.value ?? 0) > 0 && each.mono < (ended ?? Infinity)).at(-1) : from === "first-retransmission" ? retransmitted.find((each) => each.why === "rto" && each.mono > before) : from === "first-sent" ? socket.find((each) => each.ev === "tx" && each.mono > before && (each.seg?.len.value ?? 0) > 0) : from === "syn-sent" ? socket.find((each) => each.ev === "state" && newState(each) === 2) : from === "peer-closed" ? kernel.find((each) => each.netns === peerNetns && each.cookie === peerCookie && each.ev === "state" && oldState(each) === 1) : void 0;
1828
1842
  if (record === void 0 && from === "peer-closed" && peerCookie === void 0) return proxiedClose(run);
1829
1843
  return record?.mono;
1830
1844
  };