@mark1russell7/harness 0.5.0 → 0.6.0
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 +20 -3
- package/build/index.d.ts +132 -8
- package/build/index.js +163 -15
- package/build/index.js.map +1 -1
- package/image/go-http/Dockerfile +14 -0
- package/image/go-http/go.mod +3 -0
- package/image/go-http/main.go +324 -0
- package/image/node-http/Dockerfile +12 -0
- package/image/node-http/client-agent.mjs +163 -0
- package/image/node-http/client-undici.mjs +141 -0
- package/image/node-http/lines.mjs +59 -0
- package/image/node-http/npm-lock.json +22 -0
- package/image/node-http/package.json +8 -0
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -82,11 +82,16 @@ A Sentinel that aborted a failover does not try again for 2 × `failover-timeout
|
|
|
82
82
|
## The clients and the agents
|
|
83
83
|
|
|
84
84
|
- **A client image** (`ClientImage`, `Cell.client`) replaces the TCP client of the harness in each run. It operates in the namespace of the client. Its command gets the arguments of the TCP client, and it prints the same JSON lines.
|
|
85
|
+
- **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`):
|
|
86
|
+
- after the signal (`inflight`), into the fault
|
|
87
|
+
- or by itself, a gap after its first reply (`gap`), for the keep-alive race.
|
|
88
|
+
- **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.
|
|
89
|
+
- **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.
|
|
85
90
|
- **An agent** (`lab.agent(image, command, { pid })`) is a container in the network namespace of the target. With `pid`, it also shares the process namespace of the target, thus it can signal the server process. Its JSON lines and its `exec` stay available to the caller.
|
|
86
91
|
|
|
87
92
|
## The images
|
|
88
93
|
|
|
89
|
-
The harness builds
|
|
94
|
+
The harness builds five images. The tag of each image comes from the content of all the files that its Dockerfile reads. The harness copies these files into a new build context, from its own package and from the prober package. Thus the build works in a workspace and from npm.
|
|
90
95
|
|
|
91
96
|
- **The harness image** (`image/Dockerfile`) has two stages, each a digest-pinned Alpine. `Cargo.lock` pins the crates. The image holds:
|
|
92
97
|
- the kernel program of the prober, compiled against the BTF of the kernel that builds it, and its Rust collector.
|
|
@@ -95,6 +100,8 @@ The harness builds three images. The tag of each image comes from the content of
|
|
|
95
100
|
- `inject`, which applies a fault in one `iptables-restore` commit, with monotonic stamps around the commit. Thus the start of a fault is known in the VM. A `docker exec` from Windows comes 190 ms late at the median. With `--via DEV`, the rules go in the FORWARD chain, on the device toward one side.
|
|
96
101
|
- `inject` also sends a signal to a process by its name (`signal`), and one command to a server of the Redis protocol (`resp`). Stamps bracket each.
|
|
97
102
|
- **The Node client image** (`image/node`) is a driver client: ioredis 6.0.0 on Node 24.21.0. The digest of the image and an npm lockfile pin them. `client-ioredis.mjs` prints the lines of the TCP client on the same clock, and it adds the status changes of ioredis as `status` lines. The same image holds the client of a group in Sentinel mode (`client-sentinel.mjs`) and the observer of its Sentinels (`observer.mjs`).
|
|
103
|
+
- **The Go HTTP image** (`image/go-http`) is the `http.Transport` of Go 1.27.2 at its defaults (`client-go`). The program uses the standard library only, thus the digest of the Go builder pins all of its code. The build does `gofmt` and `go vet` first. A wrapper on each connection writes what the read loop of the transport reads. The trace of the transport writes each try of a request.
|
|
104
|
+
- **The Node HTTP image** (`image/node-http`) holds the `http.Agent` of Node 24.21.0 (`client-agent.mjs`) and an HTTP/1.1 `Client` of undici 8.12.2 (`client-undici.mjs`). The digest of the image and an npm lockfile pin them. It is apart from the image of ioredis, thus the cells of ioredis keep their image.
|
|
98
105
|
- **The router image** (`image/router`) is a digest-pinned Alpine with conntrack-tools, nftables, iproute2 and Python. `mbx.sh` sets the form of the router in one nftables table, `ctwatch.py` writes the conntrack events, and `resetter.py` sends the resets of the `reset-both` form. Each run records its packages in the manifest.
|
|
99
106
|
|
|
100
107
|
The build does the `cargo test` of the two crates. Thus the record decoder passes its tests before an image exists. Each amount that the native programs print is a quantity in a canonical unit. For example, an RTO is in `jiffy`, a round trip in `us`, and a segment in `B`.
|
|
@@ -110,7 +117,7 @@ ioredis sets the keepalive of its socket itself, after it connects. Thus a cell
|
|
|
110
117
|
- **An ICMP unreachable** (`unreachable`, `Cell.icmp`) answers each segment from the observer with host, net or port unreachable, or admin prohibited. Linux keeps it as a soft error, continues to retransmit, and gives the errno of the ICMP at the timeout. Host and net unreachable also revert the backoff (RFC 6069).
|
|
111
118
|
- **A simplex partition** (`simplex`, `Cell.drops`) drops the segments to the server, or the segments to the client, but not both.
|
|
112
119
|
- **A FIN** (`fin`) stops the process of the peer with SIGKILL while its kernel stays up. For the client, the target starts its server as the child of a shell (`Target.command`), and `Target.kill` stops it. For the server, the harness stops the container of the client.
|
|
113
|
-
- **An MTU black hole** (`mtu-blackhole`, `Cell.mtu`) drops each segment longer than the length, in the namespace that receives it.
|
|
120
|
+
- **An MTU black hole** (`mtu-blackhole`, `Cell.mtu`) drops each segment longer than the length, in the namespace that receives it. At a router (`Cell.faultAt`), it drops what the router forwards from one side.
|
|
114
121
|
- **netem** (`netem`, `Cell.netem`) is tc netem on the egress of the target: loss, duplication, reordering or corruption. The outcome is a distribution. At a hop, netem is on the egress of the device of the hop toward one side.
|
|
115
122
|
- **A latency** (`Cell.latency`) is tc netem delay on the egress of the target before the client connects. Thus the round trip and the RTO include it.
|
|
116
123
|
- **A fault at a proxy** (`Cell.faultAt`) puts the rules of a fault in the namespace of the proxy, toward the client or toward the target. The rules act on the sockets of the proxy.
|
|
@@ -125,6 +132,7 @@ ioredis sets the keepalive of its socket itself, after it connects. Thus a cell
|
|
|
125
132
|
- **An idle proxy** (`proxy-idle`) injects nothing. The proxy closes an idle connection at its own timeout.
|
|
126
133
|
- **A toxic** (`toxic`, `Cell.toxic`) is one toxic of Toxiproxy, added through its HTTP API by `inject http`, with monotonic stamps around the request. Its name is `fault`, and the harness removes it after the run. A cell whose fault is another one adds its toxic first: a slow close, then the close of the server.
|
|
127
134
|
- **A close of the server** (`server-close`) makes the server close the connection of each client, with `Target.close`: `CLIENT KILL` for Redis.
|
|
135
|
+
- **A stall of the application** (`app-stall`) injects nothing. The server process holds the reply, because the request asks for it (`HttpProbe.hold`), and its kernel stays up.
|
|
128
136
|
- **A fault on a group** acts on the part that `Cell.faultAt` names by its role:
|
|
129
137
|
- A black hole (`blackhole`) drops each segment between the part and each peer. A reset (`rst`) answers each segment from a peer with a reset, thus the part refuses each connection.
|
|
130
138
|
- A partial partition (`partition`) is a black hole between the part and some of its peers (`GroupPlace.cuts`). `group` cuts the part from the other members and the Sentinels, and `clients` cuts it from the client.
|
|
@@ -156,6 +164,11 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
|
|
|
156
164
|
- **Reordering.** A measurement-only run also gives what reordering, duplicates and losses did (`reordering`). At the client, it counts the arrivals beyond a hole and the arrivals of data that it had already, against `rcv_nxt`. From the sender, it gives the most SACKed and lost segments, the DSACKs, the fast retransmissions, and the reordering degree and count of the kernel.
|
|
157
165
|
- **An arrival is not always one segment.** The segments that arrive while the owner holds the socket wait in its backlog, and the kernel joins the contiguous ones. Thus the kernel counts out-of-order segments (`rcv_ooopack`), and the prober counts arrivals.
|
|
158
166
|
- **A proxy's own time.** A calibration cell (`Cell.calibrates`) gives the time of its proxy for each request and its reply (`crossings`). Each time goes from an arrival on one socket of the proxy to its next write on the other socket (a `tx` record).
|
|
167
|
+
- **An HTTP client** (`requestOf`). The run gives what the second request met (`request`): the connection of its first try, a new try, and its error or its reply. The prediction gives the same (`Prediction.request`), and another outcome is a finding.
|
|
168
|
+
- In a gap, the idle connection counts from the first reply, and the run gives the gap from the lines of the client.
|
|
169
|
+
- A new try that gets no connection writes only its failed dial (`dial-error`). That line counts as a new try.
|
|
170
|
+
- A close of the observer itself (errno `CLOSED`) is the change of its socket from ESTABLISHED to FIN_WAIT1: its pool closed an idle connection.
|
|
171
|
+
- A timer of the client or the server is not a timer of the kernel. Thus a delayed keepalive timer of the socket does not make such a run void.
|
|
159
172
|
- **A router that forgets in silence.** A silent prediction can say that the router forgets the flow in a band after the last segment (`Prediction.middlebox`). The run then holds when the forget is in that band and nothing detects it. A missing forget, or one outside its band, is a finding. Without it, the router must keep the flow.
|
|
160
173
|
- **A group** (`measureGroup`, `timeline.ts`). The time counts from the start of the commit of the fault, or from the first write into a stall.
|
|
161
174
|
- It ends at the first write that a primary accepted after the fault. That primary is a new one, or the same one after the lift, as the prediction says (`GroupPrediction.lands`).
|
|
@@ -184,6 +197,8 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
|
|
|
184
197
|
- A run that did not announce the stage is outside too. A stage outside its band fails that stage. The decision of the cell stays the decision of its band.
|
|
185
198
|
- The verdict also gives the median of each stage, the lost writes of each run, and the errors on the way.
|
|
186
199
|
|
|
200
|
+
- **An HTTP client** (`Verdict.requests`). The verdict counts each outcome of the second request over the counted runs. A measurement-only cell also gives them in its reason.
|
|
201
|
+
|
|
187
202
|
The distribution test needs a simulator, which does not exist at this time.
|
|
188
203
|
|
|
189
204
|
## Runs
|
|
@@ -200,6 +215,8 @@ The distribution test needs a simulator, which does not exist at this time.
|
|
|
200
215
|
- Its in-flight data is the reply of the server. Thus the client sends `probe.request` first, for example a BLPOP with a timeout, and the fault occurs while the server holds it.
|
|
201
216
|
- **The pause before the fault.** In every other run, the fault comes 250 ms after the client's first reply (`QUIET_MS`). That is more than Linux's longest delayed ACK, 200 ms. A fault that comes earlier meets the client's delayed ACK of the reply, not the request or the idle time that the cell measures. On a fast host the fault came 35 ms after the reply, and the ACK met a reset first.
|
|
202
217
|
- A run of a group has no such pause. Its fault comes after the 20th accepted write of its stream, and each band of a group counts from the fault.
|
|
218
|
+
- An HTTP client in a gap counts its gap from its own first reply, thus the pause does not change it. Its run ends at the end of its second request (`done`).
|
|
219
|
+
- **An HTTP client.** A run of an HTTP client in flight ends at the end of its second request, after a new try of the client. The client waits one second after it (`LINGER_MS`), thus its own close comes after the window.
|
|
203
220
|
- **`judge(cell, runs)`** (`run.ts`) measures and judges the raw records of a cell with the code at this time. `runCell` uses it during a cell. A holder of raw records can judge them again after a change to `observe.ts` or to the verdict.
|
|
204
221
|
- **Testcontainers loads when a lab first opens.** Code that only measures and judges does not load it.
|
|
205
222
|
|
|
@@ -217,6 +234,6 @@ A run needs Docker, and privileged containers for the prober and the sidecars. T
|
|
|
217
234
|
|
|
218
235
|
The tests also measure a group run from test lines. They find the first accepted write, the stages, the writes that the group kept, and the void runs. They test the views that make a group healthy, and the peers that a fault on a part cuts.
|
|
219
236
|
|
|
220
|
-
The cells themselves need Docker and privileged containers. CI does not start them. `HARNESS_LAB=1` starts the lab tests on Docker. They start a client image and an agent, and they put a reset and netem at a router. They also fail a group of Valkey 9.1 over with a planned failover.
|
|
237
|
+
The cells themselves need Docker and privileged containers. CI does not start them. `HARNESS_LAB=1` starts the lab tests on Docker. They start a client image and an agent, and they put a reset and netem at a router. They also fail a group of Valkey 9.1 over with a planned failover. Each HTTP client sends two requests on one connection to a Node server, from a script in the stock Node image.
|
|
221
238
|
|
|
222
239
|
`pnpm --filter @mark1russell7/harness build` writes the package for npm into `build/`.
|
package/build/index.d.ts
CHANGED
|
@@ -43,8 +43,12 @@ import "testcontainers";
|
|
|
43
43
|
* - `freeze-process` stops the server process of the part with SIGSTOP, and SIGCONT thaws it. Its
|
|
44
44
|
* kernel stays up. `fin` stops the process with SIGKILL.
|
|
45
45
|
* - `planned-failover` asks a Sentinel for a failover: `SENTINEL FAILOVER <group>`.
|
|
46
|
+
*
|
|
47
|
+
* This fault is of an HTTP server:
|
|
48
|
+
* - `app-stall` injects nothing. The server process holds the reply to the request, because the
|
|
49
|
+
* request asks for that (`HttpProbe.hold`), while its kernel stays up.
|
|
46
50
|
*/
|
|
47
|
-
type Fault = "blackhole" | "freeze-process" | "rst" | "stall" | "fin" | "unreachable" | "simplex" | "mtu-blackhole" | "netem" | "middlebox-forget" | "proxy-idle" | "toxic" | "server-close" | "partition" | "planned-failover" | "none";
|
|
51
|
+
type Fault = "blackhole" | "freeze-process" | "rst" | "stall" | "fin" | "unreachable" | "simplex" | "mtu-blackhole" | "netem" | "middlebox-forget" | "proxy-idle" | "toxic" | "server-close" | "partition" | "planned-failover" | "app-stall" | "none";
|
|
48
52
|
/**
|
|
49
53
|
* A middlebox form tells what a router does with a flow that it forgot, when a late packet comes:
|
|
50
54
|
* - `reset-both` sends a reset to the two ends when it forgets the flow.
|
|
@@ -279,6 +283,21 @@ interface MiddleboxExpectation {
|
|
|
279
283
|
readonly high: Quantity;
|
|
280
284
|
};
|
|
281
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* A request prediction tells what the second request of an HTTP client meets, beside the band of
|
|
288
|
+
* its cell. The client writes it in its lines: the connection of each try, and the outcome.
|
|
289
|
+
* - `connection` is the connection of its first try: the one that the first request used
|
|
290
|
+
* (`reused`), or a new one.
|
|
291
|
+
* - `retried` tells that the client sent it again on a new connection by itself.
|
|
292
|
+
* - `error` is the error that reaches the caller, or null for a reply.
|
|
293
|
+
* - `pending` tells that nothing ends the request in the window of the cell.
|
|
294
|
+
*/
|
|
295
|
+
interface RequestPrediction {
|
|
296
|
+
readonly connection: "reused" | "new";
|
|
297
|
+
readonly retried: boolean;
|
|
298
|
+
readonly error: string | null;
|
|
299
|
+
readonly pending?: true;
|
|
300
|
+
}
|
|
282
301
|
/**
|
|
283
302
|
* A prediction is what a cell predicts. Either a detection occurs in an interval [T_lo, T_hi]
|
|
284
303
|
* after the reference event, or nothing detects the fault in a window. Nothing detects a stall
|
|
@@ -296,12 +315,18 @@ interface MiddleboxExpectation {
|
|
|
296
315
|
*
|
|
297
316
|
* For a group cell, the band is the first write that a primary accepts after the fault, and its
|
|
298
317
|
* errno is `OK`. `group` tells which primary, and the stages of the group on the way.
|
|
318
|
+
*
|
|
319
|
+
* For an HTTP cell, `request` tells what the second request meets. The errno `CLOSED` is a close
|
|
320
|
+
* of the observer itself: its pool closes an idle connection, a FIN that it sends.
|
|
299
321
|
*/
|
|
300
322
|
type Prediction = {
|
|
301
323
|
readonly detects: true;
|
|
302
324
|
/** This name is the timer of the machine that fires first, or the event that detects the fault, with its errno. */
|
|
303
325
|
readonly timer: string;
|
|
304
|
-
/**
|
|
326
|
+
/**
|
|
327
|
+
* This name is the errno of the detection, or EOF for an end of file without an error. It is
|
|
328
|
+
* OK for a write that a primary accepts, and CLOSED for a close of the observer itself.
|
|
329
|
+
*/
|
|
305
330
|
readonly errno: string;
|
|
306
331
|
readonly from: Reference;
|
|
307
332
|
/** This time is the closed form, less the most that the timer can fire early. */
|
|
@@ -311,6 +336,7 @@ type Prediction = {
|
|
|
311
336
|
readonly rtt?: RttRange;
|
|
312
337
|
readonly through?: readonly ProxyTime[];
|
|
313
338
|
readonly group?: GroupPrediction;
|
|
339
|
+
readonly request?: RequestPrediction;
|
|
314
340
|
} | {
|
|
315
341
|
readonly detects: false;
|
|
316
342
|
readonly window: Quantity;
|
|
@@ -320,12 +346,14 @@ type Prediction = {
|
|
|
320
346
|
readonly rtt?: RttRange;
|
|
321
347
|
readonly through?: readonly ProxyTime[];
|
|
322
348
|
readonly group?: GroupPrediction;
|
|
349
|
+
readonly request?: RequestPrediction;
|
|
323
350
|
} | {
|
|
324
351
|
readonly detects: null;
|
|
325
352
|
readonly window: Quantity;
|
|
326
353
|
readonly why: string;
|
|
327
354
|
readonly from?: Reference;
|
|
328
355
|
readonly group?: GroupPrediction;
|
|
356
|
+
readonly request?: RequestPrediction;
|
|
329
357
|
};
|
|
330
358
|
/** A target is the server that a cell connects to: a stock image. */
|
|
331
359
|
interface Target {
|
|
@@ -352,9 +380,40 @@ interface Target {
|
|
|
352
380
|
readonly kill?: readonly string[];
|
|
353
381
|
/** This command makes the server close the connection of each client, as argv, for a `server-close` fault. */
|
|
354
382
|
readonly close?: readonly string[];
|
|
383
|
+
/**
|
|
384
|
+
* These files go into the target before it starts, by their path, for example the script of a
|
|
385
|
+
* server on a stock Node image. The image stays stock.
|
|
386
|
+
*/
|
|
387
|
+
readonly files?: Readonly<Record<string, string>>;
|
|
388
|
+
/**
|
|
389
|
+
* This flag tells that the target writes JSON lines with an `ev` and a `t` on its standard
|
|
390
|
+
* output, on CLOCK_MONOTONIC. Each run then keeps its lines (`Run.server`).
|
|
391
|
+
*/
|
|
392
|
+
readonly lines?: true;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* A client is what the runs of a cell use: the TCP client of the harness, or a driver on its
|
|
396
|
+
* socket. The HTTP clients are the `http.Transport` of Go (`go-http`), the `http.Agent` of Node
|
|
397
|
+
* (`http-agent`) and an HTTP/1.1 `Client` of undici (`undici`).
|
|
398
|
+
*/
|
|
399
|
+
type Client = "tcp" | "ioredis" | "go-http" | "http-agent" | "undici";
|
|
400
|
+
/**
|
|
401
|
+
* An HTTP probe is the workload of an HTTP client. The first request is a GET of the root. The
|
|
402
|
+
* second request follows the mode of the probe: after the signal (`inflight`), or after a gap
|
|
403
|
+
* (`gap`).
|
|
404
|
+
*/
|
|
405
|
+
interface HttpProbe {
|
|
406
|
+
/** This method is the method of the second request. */
|
|
407
|
+
readonly method: "GET" | "POST";
|
|
408
|
+
/** This time is the gap of a `gap` workload: from the end of the first reply to the second request. */
|
|
409
|
+
readonly gap?: Quantity;
|
|
410
|
+
/** This time is how long the server holds the reply to the second request (`/hold?ms=`). */
|
|
411
|
+
readonly hold?: Quantity;
|
|
412
|
+
/** This length is the length of the body of the reply to the second request, in bytes (`/big?bytes=`). */
|
|
413
|
+
readonly bytes?: number;
|
|
414
|
+
/** These are settings of the client over its defaults, by the names of its flags: for example `idle-conn-timeout-ms`. */
|
|
415
|
+
readonly options?: Readonly<Record<string, number>>;
|
|
355
416
|
}
|
|
356
|
-
/** A client is what the runs of a cell use: the TCP client of the harness, or a driver on its socket. */
|
|
357
|
-
type Client = "tcp" | "ioredis";
|
|
358
417
|
/**
|
|
359
418
|
* A client image is a process that a lab starts as the client of each run, in the network
|
|
360
419
|
* namespace of the client. An example is an agent of an application on Node. Its command gets the
|
|
@@ -389,8 +448,9 @@ interface Cell {
|
|
|
389
448
|
/**
|
|
390
449
|
* This mode tells if the client is idle after its first reply, or sends a request into the
|
|
391
450
|
* fault. In a stream, it sends `count` requests into it, each `interval` after the reply before.
|
|
451
|
+
* An HTTP client in a `gap` sends its second request a gap after its first reply, by itself.
|
|
392
452
|
*/
|
|
393
|
-
readonly mode: "idle" | "inflight" | "stream";
|
|
453
|
+
readonly mode: "idle" | "inflight" | "stream" | "gap";
|
|
394
454
|
/** This count is the number of requests of a stream. */
|
|
395
455
|
readonly count?: number;
|
|
396
456
|
/** This time is the pause after each reply of a stream, before the next request. */
|
|
@@ -414,6 +474,8 @@ interface Cell {
|
|
|
414
474
|
readonly name: "ioredis";
|
|
415
475
|
readonly keepAlive: Quantity;
|
|
416
476
|
};
|
|
477
|
+
/** This workload is the workload of an HTTP client (`Cell.client` is an HTTP client). The client then ignores `keepalive` and `userTimeout`. */
|
|
478
|
+
readonly http?: HttpProbe;
|
|
417
479
|
};
|
|
418
480
|
readonly fault: Fault;
|
|
419
481
|
/** This time is the length of a stall, from the send of the request. */
|
|
@@ -451,8 +513,8 @@ interface Cell {
|
|
|
451
513
|
readonly idleBefore?: Quantity;
|
|
452
514
|
/** This toxic is the toxic of a `toxic` fault, which the Toxiproxy hop gets. */
|
|
453
515
|
readonly toxic?: Toxic;
|
|
454
|
-
/** This image replaces the TCP client of the harness for each run. */
|
|
455
|
-
readonly client?: ClientImage;
|
|
516
|
+
/** This image, or this client of the harness, replaces the TCP client of the harness for each run. An HTTP client is one. */
|
|
517
|
+
readonly client?: ClientImage | Client;
|
|
456
518
|
/**
|
|
457
519
|
* This name is the proxy hop whose own time a calibration cell measures. The time of a request
|
|
458
520
|
* (`up`) starts at its arrival on the socket of the proxy toward the client. It stops at the
|
|
@@ -549,6 +611,8 @@ interface Run {
|
|
|
549
611
|
readonly middlebox?: readonly MiddleboxEvent[];
|
|
550
612
|
/** This record is the group of a group cell: its parts, and what it did in the run. */
|
|
551
613
|
readonly group?: RunGroup;
|
|
614
|
+
/** These are the lines of a target that writes them (`Target.lines`), from the start to the end of the run. */
|
|
615
|
+
readonly server?: readonly ProbeEvent[];
|
|
552
616
|
}
|
|
553
617
|
/** A Sentinel event is one line of the observer of a group: an event that a Sentinel published, on the clock of the VM. */
|
|
554
618
|
interface SentinelEvent {
|
|
@@ -697,7 +761,21 @@ type Outcome = {
|
|
|
697
761
|
readonly kind: "silent";
|
|
698
762
|
} | {
|
|
699
763
|
readonly kind: "answered";
|
|
764
|
+
} | {
|
|
765
|
+
readonly kind: "closed";
|
|
700
766
|
};
|
|
767
|
+
/**
|
|
768
|
+
* A request reading is what the second request of an HTTP client met, from the lines of the
|
|
769
|
+
* client. It holds the connection of its first try, a new try, and its outcome. `gap` is the time
|
|
770
|
+
* from the end of the first reply to the send of the second request.
|
|
771
|
+
*/
|
|
772
|
+
interface RequestReading {
|
|
773
|
+
readonly connection: "reused" | "new";
|
|
774
|
+
readonly retried: boolean;
|
|
775
|
+
readonly error: string | null;
|
|
776
|
+
readonly pending: boolean;
|
|
777
|
+
readonly gap: Quantity | null;
|
|
778
|
+
}
|
|
701
779
|
/** A measurement is what one run measured, against the prediction of its cell. */
|
|
702
780
|
interface Measured {
|
|
703
781
|
readonly environment: number;
|
|
@@ -746,6 +824,8 @@ interface Measured {
|
|
|
746
824
|
readonly crossings?: Crossings;
|
|
747
825
|
/** This record is what a run of a group cell measured: the stages of its timeline, and what became of each write. */
|
|
748
826
|
readonly group?: GroupMeasured;
|
|
827
|
+
/** This record is what the second request of an HTTP client met, for a cell whose prediction says it. */
|
|
828
|
+
readonly request?: RequestReading | null;
|
|
749
829
|
/** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */
|
|
750
830
|
readonly void: string | null;
|
|
751
831
|
/** This text tells what in the run disagrees with the prediction, other than its timing, or null. */
|
|
@@ -818,10 +898,28 @@ export declare const crossingsOf: (run: Run, hop: string, toClient: SocketPlace
|
|
|
818
898
|
* it, and before the first keepalive probe. A live peer answers that probe, and the answer
|
|
819
899
|
* starts the idle time again. A run where the fault was late is void. A run whose records the
|
|
820
900
|
* prober lost is also void.
|
|
901
|
+
* - For an HTTP client, it also reads what the second request met (`requestOf`). An outcome other
|
|
902
|
+
* than the one that the prediction gives is a finding.
|
|
821
903
|
*/
|
|
822
904
|
export declare const measure: (cell: Cell, run: Run) => Measured;
|
|
905
|
+
/**
|
|
906
|
+
* This function reads the second request of an HTTP client from its lines. Its first try is its
|
|
907
|
+
* first `request` line, and each `retry` line is a new try. Its end is its `error` line, or its
|
|
908
|
+
* `replied` line. A request with neither is pending. It gives null when the client wrote no try.
|
|
909
|
+
*
|
|
910
|
+
* A new try that gets no connection writes no `retry` line. Its dial writes a `dial-error` line.
|
|
911
|
+
* Thus a `dial-error` line after the first try and before the end is also a new try.
|
|
912
|
+
*/
|
|
913
|
+
export declare const requestOf: (events: readonly ProbeEvent[]) => RequestReading | null;
|
|
823
914
|
//#endregion
|
|
824
915
|
//#region src/verdict.d.ts
|
|
916
|
+
/** This function gives the words of the outcome of a second request: its connection, a retry, and its end. */
|
|
917
|
+
export declare const requestWords: (outcome: {
|
|
918
|
+
readonly connection: "reused" | "new";
|
|
919
|
+
readonly retried: boolean;
|
|
920
|
+
readonly error: string | null;
|
|
921
|
+
readonly pending?: boolean | undefined;
|
|
922
|
+
}) => string;
|
|
825
923
|
/**
|
|
826
924
|
* This amount is the noise of the pipeline itself, δ_abs, as the calibration measured it. It is
|
|
827
925
|
* the 99.9th percentile of the whole latency of a reset, at the concurrency cap or less. Its
|
|
@@ -877,6 +975,12 @@ interface Verdict {
|
|
|
877
975
|
};
|
|
878
976
|
/** This record is what the group of a group cell did over the counted runs: its stages, and its writes. */
|
|
879
977
|
readonly group?: GroupVerdict;
|
|
978
|
+
/**
|
|
979
|
+
* These are the outcomes of the second request of an HTTP client over the counted runs, each
|
|
980
|
+
* with its count: for example "reused, a retry, a reply". An outcome other than the predicted
|
|
981
|
+
* one is a finding of its run.
|
|
982
|
+
*/
|
|
983
|
+
readonly requests?: Readonly<Record<string, number>>;
|
|
880
984
|
readonly why: string;
|
|
881
985
|
}
|
|
882
986
|
/**
|
|
@@ -961,6 +1065,10 @@ export declare const harnessImage: () => Promise<string>;
|
|
|
961
1065
|
export declare const nodeClientImage: () => Promise<string>;
|
|
962
1066
|
/** This function gives the image of the router. */
|
|
963
1067
|
export declare const routerImage: () => Promise<string>;
|
|
1068
|
+
/** This function gives the image of the Go HTTP client. */
|
|
1069
|
+
export declare const goHttpImage: () => Promise<string>;
|
|
1070
|
+
/** This function gives the image of the Node HTTP clients. */
|
|
1071
|
+
export declare const nodeHttpImage: () => Promise<string>;
|
|
964
1072
|
/**
|
|
965
1073
|
* This time is the interval of the listing of a router, when its hop does not set one. A listing
|
|
966
1074
|
* evicts each expired flow that it walks past, and the DESTROY event of that flow follows.
|
|
@@ -1022,6 +1130,10 @@ interface Manifest {
|
|
|
1022
1130
|
readonly settings: Readonly<Record<string, string>>;
|
|
1023
1131
|
/** These are the sysctls of its namespace, as read back after the harness set them. */
|
|
1024
1132
|
readonly sysctls: Readonly<Record<string, string>>;
|
|
1133
|
+
/** These are the files that went into the target, by their path, each with the sha256 of its content. */
|
|
1134
|
+
readonly files?: Readonly<Record<string, string>>;
|
|
1135
|
+
/** This line is the first line of a target that writes them: a server that tells its settings as it reads them. */
|
|
1136
|
+
readonly first?: ProbeEvent;
|
|
1025
1137
|
};
|
|
1026
1138
|
readonly harness: {
|
|
1027
1139
|
readonly image: string;
|
|
@@ -1113,6 +1225,8 @@ interface Lab {
|
|
|
1113
1225
|
readonly topology: RunTopology | null;
|
|
1114
1226
|
/** These are the lines of the routers of a topology, in sequence, each with the name of its hop. A direct pair has none. */
|
|
1115
1227
|
readonly middlebox: readonly MiddleboxEvent[];
|
|
1228
|
+
/** These are the lines of a target that writes them (`Target.lines`), in sequence. Another target has none. */
|
|
1229
|
+
readonly server: readonly ProbeEvent[];
|
|
1116
1230
|
/** This method adds a toxic to the Toxiproxy hop of a topology, or removes it with null. The stamps of the injector bracket it. */
|
|
1117
1231
|
toxic(toxic: Toxic | null): Promise<{
|
|
1118
1232
|
t0: number;
|
|
@@ -1402,6 +1516,16 @@ export declare const judge: (cell: Cell, runs: readonly Run[]) => {
|
|
|
1402
1516
|
measured: Measured[];
|
|
1403
1517
|
verdict: Verdict;
|
|
1404
1518
|
};
|
|
1519
|
+
/**
|
|
1520
|
+
* This time is the pause between the client's first reply and the fault. It is more than Linux's
|
|
1521
|
+
* TCP_DELACK_MAX of 200 ms. Thus the delayed ACK of the reply is out before the fault.
|
|
1522
|
+
*/
|
|
1523
|
+
export declare const QUIET_MS = 250;
|
|
1524
|
+
/**
|
|
1525
|
+
* This time is how long an HTTP client waits after its second request, before it exits. The exit
|
|
1526
|
+
* closes its sockets, thus that close comes after the window of the cell.
|
|
1527
|
+
*/
|
|
1528
|
+
export declare const LINGER_MS = 1e3;
|
|
1405
1529
|
/** This function divides a count of runs across environments, as equally as possible. */
|
|
1406
1530
|
export declare const spread: (runs: number, environments: number) => number[];
|
|
1407
1531
|
/**
|
|
@@ -1415,5 +1539,5 @@ export declare const spread: (runs: number, environments: number) => number[];
|
|
|
1415
1539
|
*/
|
|
1416
1540
|
export declare const runCell: (cell: Cell, say?: (line: string) => void) => Promise<CellResult>;
|
|
1417
1541
|
//#endregion
|
|
1418
|
-
export type { Agent, Cell, CellResult, Client, ClientImage, Crossings, Decision, ErrorOnTheWay, Fault, FaultPlace, FaultSide, Group, GroupClient, GroupLab, GroupMeasured, GroupNetworkFault, GroupPlace, GroupPrediction, GroupReading, GroupRole, GroupVerdict, GroupView, Hop, HopPlace, Interval, KernelRecord, Lab, Manifest, ManifestHop, Measured, MiddleboxEvent, MiddleboxExpectation, MiddleboxForm, NetworkFault, Observer, Outcome, Prediction, Probe, ProbeEvent, ProxyHop, ProxyTime, Recovers, Reference, Reordering, Replies, RouterHop, RttRange, Run, RunGroup, RunPart, RunTopology, SentinelEvent, SocketPlace, Spread, Stage, StageBand, StageVerdict, Stamp, Target, Topology, Toxic, Verdict, Writes };
|
|
1542
|
+
export type { Agent, Cell, CellResult, Client, ClientImage, Crossings, Decision, ErrorOnTheWay, Fault, FaultPlace, FaultSide, Group, GroupClient, GroupLab, GroupMeasured, GroupNetworkFault, GroupPlace, GroupPrediction, GroupReading, GroupRole, GroupVerdict, GroupView, Hop, HopPlace, HttpProbe, Interval, KernelRecord, Lab, Manifest, ManifestHop, Measured, MiddleboxEvent, MiddleboxExpectation, MiddleboxForm, NetworkFault, Observer, Outcome, Prediction, Probe, ProbeEvent, ProxyHop, ProxyTime, Recovers, Reference, Reordering, Replies, RequestPrediction, RequestReading, RouterHop, RttRange, Run, RunGroup, RunPart, RunTopology, SentinelEvent, SocketPlace, Spread, Stage, StageBand, StageVerdict, Stamp, Target, Topology, Toxic, Verdict, Writes };
|
|
1419
1543
|
//# sourceMappingURL=index.d.ts.map
|