@mark1russell7/harness 0.2.0 → 0.4.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 +76 -19
- package/build/index.d.ts +446 -31
- package/build/index.js +1106 -336
- package/build/index.js.map +1 -1
- package/image/native/src/bin/client.rs +63 -3
- package/image/native/src/bin/inject.rs +119 -7
- package/image/router/Dockerfile +8 -0
- package/image/router/ctwatch.py +66 -0
- package/image/router/mbx.sh +61 -0
- package/image/router/resetter.py +104 -0
- package/package.json +7 -3
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Quantity } from "@mark1russell7/quantity";
|
|
2
2
|
import { KernelRecord } from "@mark1russell7/prober";
|
|
3
|
+
import "testcontainers";
|
|
3
4
|
//#region src/cell.d.ts
|
|
4
5
|
/**
|
|
5
6
|
* A fault is a failure that the harness injects, by the name of its manifestation. Each name is
|
|
@@ -19,17 +20,110 @@ import { KernelRecord } from "@mark1russell7/prober";
|
|
|
19
20
|
* - `mtu-blackhole` drops each segment to the observer that is longer than `Cell.mtu` bytes.
|
|
20
21
|
* - `netem` is tc netem on the egress of the target (`Cell.netem`): loss, duplication,
|
|
21
22
|
* reordering or corruption. Its outcome is a distribution.
|
|
23
|
+
*
|
|
24
|
+
* These faults are of a topology (`Cell.topology`):
|
|
25
|
+
* - `middlebox-forget` injects nothing. The router of the topology forgets an idle flow at its own
|
|
26
|
+
* idle timeout, and its form tells what a late packet then meets.
|
|
27
|
+
* - `proxy-idle` injects nothing. The proxy of the topology closes an idle connection at its own
|
|
28
|
+
* timeout.
|
|
29
|
+
* - `toxic` adds a toxic of Toxiproxy (`Cell.toxic`) through its HTTP API.
|
|
30
|
+
* - `server-close` makes the server close the connection of its peer, with the command of the
|
|
31
|
+
* target (`Target.close`): `CLIENT KILL` in Redis.
|
|
32
|
+
* - `none` injects nothing. A calibration cell measures the path itself: for example the time of a
|
|
33
|
+
* proxy between its two sockets (`Cell.calibrates`).
|
|
34
|
+
*
|
|
35
|
+
* A fault at a router of the topology (`Cell.faultAt`) acts on what the router forwards toward
|
|
36
|
+
* one side. It is a black hole, a reset or an ICMP unreachable. It can also be a simplex drop, an
|
|
37
|
+
* MTU drop or netem.
|
|
38
|
+
*/
|
|
39
|
+
type Fault = "blackhole" | "freeze-process" | "rst" | "stall" | "fin" | "unreachable" | "simplex" | "mtu-blackhole" | "netem" | "middlebox-forget" | "proxy-idle" | "toxic" | "server-close" | "none";
|
|
40
|
+
/**
|
|
41
|
+
* A middlebox form tells what a router does with a flow that it forgot, when a late packet comes:
|
|
42
|
+
* - `reset-both` sends a reset to the two ends when it forgets the flow.
|
|
43
|
+
* - `reset-next` answers the next packet from inside with a reset to its sender, and drops a late
|
|
44
|
+
* packet from outside.
|
|
45
|
+
* - `silent-drop` drops each late packet, in each direction.
|
|
46
|
+
* - `recreate` lets a late packet from inside through, and the flow starts again. It drops a late
|
|
47
|
+
* packet from outside.
|
|
48
|
+
* - `none` is a plain NAT router. A late packet from inside starts the flow again, and the stack of
|
|
49
|
+
* the router answers a late packet from outside with a reset.
|
|
50
|
+
*/
|
|
51
|
+
type MiddleboxForm = "reset-both" | "reset-next" | "silent-drop" | "recreate" | "none";
|
|
52
|
+
/**
|
|
53
|
+
* A router is a hop that forwards and translates the addresses of the side of the client (NAT),
|
|
54
|
+
* with conntrack. It is the router image of the harness.
|
|
55
|
+
*/
|
|
56
|
+
interface RouterHop {
|
|
57
|
+
readonly name: string;
|
|
58
|
+
readonly kind: "router";
|
|
59
|
+
readonly form: MiddleboxForm;
|
|
60
|
+
/** This time is the idle timeout of an established flow: nf_conntrack_tcp_timeout_established, in whole seconds. */
|
|
61
|
+
readonly idle: Quantity;
|
|
62
|
+
/**
|
|
63
|
+
* This time is the interval of the listing that evicts each expired flow. Without it, the
|
|
64
|
+
* interval is 0.1 s. The DESTROY event of an expired flow waits for that listing.
|
|
65
|
+
*/
|
|
66
|
+
readonly pump?: Quantity;
|
|
67
|
+
/**
|
|
68
|
+
* These are the sysctls of the namespace of the router, which the router sets itself: it is
|
|
69
|
+
* privileged. An example is `net.ipv4.icmp_ratelimit` for the ICMP errors of a fault at the router.
|
|
70
|
+
*/
|
|
71
|
+
readonly sysctls?: Readonly<Record<string, string>>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A proxy is a hop that ends each TCP connection from the side of the client and opens its own to
|
|
75
|
+
* the next hop. It is a stock image, pinned by digest.
|
|
22
76
|
*/
|
|
23
|
-
|
|
77
|
+
interface ProxyHop {
|
|
78
|
+
readonly name: string;
|
|
79
|
+
readonly kind: "proxy";
|
|
80
|
+
readonly image: string;
|
|
81
|
+
/** This port is the port that the proxy listens on for the side of the client. */
|
|
82
|
+
readonly port: number;
|
|
83
|
+
readonly command?: readonly string[];
|
|
84
|
+
/** These files go into the proxy before it starts, by their path. In their content, {{next}} is the address and port of the next hop. */
|
|
85
|
+
readonly files?: Readonly<Record<string, string>>;
|
|
86
|
+
/** This text is the log line that tells that the proxy serves. */
|
|
87
|
+
readonly ready?: string;
|
|
88
|
+
/** This record is the HTTP API of Toxiproxy: its port, and the name of the proxy that gets the toxics. */
|
|
89
|
+
readonly api?: {
|
|
90
|
+
readonly port: number;
|
|
91
|
+
readonly proxy: string;
|
|
92
|
+
};
|
|
93
|
+
/** These are the sysctls of the namespace of the proxy. A privileged sidecar sets them before each run. */
|
|
94
|
+
readonly sysctls?: Readonly<Record<string, string>>;
|
|
95
|
+
/** These are the settings of the proxy, for the manifest: for example its idle timeouts. */
|
|
96
|
+
readonly settings?: Readonly<Record<string, string>>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A hop is one box between the client and the target. Each hop is on two networks: the network
|
|
100
|
+
* of the side of the client, and the network of the side of the target.
|
|
101
|
+
*/
|
|
102
|
+
type Hop = RouterHop | ProxyHop;
|
|
103
|
+
/**
|
|
104
|
+
* A topology is the chain of hops between the client and the target, in sequence from the client.
|
|
105
|
+
* Without hops, the client and the target are a direct pair.
|
|
106
|
+
*/
|
|
107
|
+
interface Topology {
|
|
108
|
+
readonly name: string;
|
|
109
|
+
readonly hops: readonly Hop[];
|
|
110
|
+
}
|
|
111
|
+
/** A toxic is one toxic of Toxiproxy, as its API takes it. Its toxicity is 1. */
|
|
112
|
+
interface Toxic {
|
|
113
|
+
readonly type: "latency" | "timeout" | "reset_peer" | "slow_close" | "bandwidth" | "limit_data" | "slicer";
|
|
114
|
+
readonly stream: "downstream" | "upstream";
|
|
115
|
+
readonly attributes: Readonly<Record<string, number>>;
|
|
116
|
+
}
|
|
24
117
|
/**
|
|
25
118
|
* A reference is the event that the time of a detector counts from: the event that arms the
|
|
26
119
|
* timer in its machine document. Keepalive counts from the last received segment. The give-up of
|
|
27
120
|
* the retransmissions counts from the first retransmission of the RTO timer, not from the tail
|
|
28
121
|
* loss probe one RTO before it. The request itself meets a reset, thus a reset counts from the
|
|
29
122
|
* send of the client. A FIN counts from the change of state of the socket of the peer when its
|
|
30
|
-
* process dies (`peer-closed`).
|
|
123
|
+
* process dies (`peer-closed`). A toxic of Toxiproxy counts from the start of its commit, as the
|
|
124
|
+
* injector stamps it (`fault`).
|
|
31
125
|
*/
|
|
32
|
-
type Reference = "last-received" | "first-retransmission" | "request-sent" | "peer-closed";
|
|
126
|
+
type Reference = "last-received" | "first-retransmission" | "request-sent" | "peer-closed" | "fault";
|
|
33
127
|
/**
|
|
34
128
|
* This prediction tells when a connection moves again after a stall. That occurs at the first
|
|
35
129
|
* firing of its retransmission timer after the stall stops, and its reply comes back one round
|
|
@@ -52,14 +146,50 @@ interface Recovers {
|
|
|
52
146
|
readonly high: Quantity;
|
|
53
147
|
};
|
|
54
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* This prediction tells when the reply to the request comes back, from the send of the request.
|
|
151
|
+
* For example, the reply comes after the latency of a toxic, or after a forgotten flow starts again.
|
|
152
|
+
*/
|
|
153
|
+
interface Replies {
|
|
154
|
+
readonly low: Quantity;
|
|
155
|
+
readonly high: Quantity;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* An RTT range is the range of the first round trip of the observer that a band assumes. The
|
|
159
|
+
* models read a round trip, for example 1 ms. The band at that value holds within the noise for
|
|
160
|
+
* each round trip in the range. A run whose first round trip is above the range is void.
|
|
161
|
+
*/
|
|
162
|
+
interface RttRange {
|
|
163
|
+
readonly low: Quantity;
|
|
164
|
+
readonly high: Quantity;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* A proxy time is the time of one proxy in one direction that a band includes. It is an upper
|
|
168
|
+
* quantile of the times that a calibration cell of the proxy measured. Each time goes from an
|
|
169
|
+
* arrival on one socket of the proxy to its write on the other. `up` goes toward the target, and
|
|
170
|
+
* `down` toward the client.
|
|
171
|
+
*/
|
|
172
|
+
interface ProxyTime {
|
|
173
|
+
readonly hop: string;
|
|
174
|
+
readonly direction: "up" | "down";
|
|
175
|
+
/** This fraction is the quantile of the calibration, for example 0.99. */
|
|
176
|
+
readonly quantile: number;
|
|
177
|
+
readonly time: Quantity;
|
|
178
|
+
}
|
|
55
179
|
/**
|
|
56
180
|
* A prediction is what a cell predicts. Either a detection occurs in an interval [T_lo, T_hi]
|
|
57
181
|
* after the reference event, or nothing detects the fault in a window. Nothing detects a stall
|
|
58
182
|
* that is shorter than the give-up, and the connection recovers from it. The prediction then also
|
|
59
|
-
* tells when.
|
|
183
|
+
* tells when. A silent prediction can also tell when the reply to the request comes back
|
|
184
|
+
* (`replies`).
|
|
185
|
+
*
|
|
186
|
+
* A band can name the first round trip that it assumes (`rtt`), and the proxy times that it
|
|
187
|
+
* includes (`through`).
|
|
60
188
|
*
|
|
61
189
|
* A cell whose outcome is a distribution, for example under netem loss, has no band. Its
|
|
62
|
-
* prediction is `detects: null` with the reason, and its runs are measurements only.
|
|
190
|
+
* prediction is `detects: null` with the reason, and its runs are measurements only. A
|
|
191
|
+
* measurement-only cell that names a reference (`from`) also records the time from it to the end
|
|
192
|
+
* of the connection, or to the last data.
|
|
63
193
|
*/
|
|
64
194
|
type Prediction = {
|
|
65
195
|
readonly detects: true;
|
|
@@ -70,16 +200,22 @@ type Prediction = {
|
|
|
70
200
|
readonly from: Reference;
|
|
71
201
|
/** This time is the closed form, less the most that the timer can fire early. */
|
|
72
202
|
readonly low: Quantity;
|
|
73
|
-
/** This time is the closed form, plus the most that the timer can fire late. */
|
|
203
|
+
/** This time is the closed form, plus the most that the timer can fire late, plus each proxy time. */
|
|
74
204
|
readonly high: Quantity;
|
|
205
|
+
readonly rtt?: RttRange;
|
|
206
|
+
readonly through?: readonly ProxyTime[];
|
|
75
207
|
} | {
|
|
76
208
|
readonly detects: false;
|
|
77
209
|
readonly window: Quantity;
|
|
78
210
|
readonly recovers?: Recovers;
|
|
211
|
+
readonly replies?: Replies;
|
|
212
|
+
readonly rtt?: RttRange;
|
|
213
|
+
readonly through?: readonly ProxyTime[];
|
|
79
214
|
} | {
|
|
80
215
|
readonly detects: null;
|
|
81
216
|
readonly window: Quantity;
|
|
82
217
|
readonly why: string;
|
|
218
|
+
readonly from?: Reference;
|
|
83
219
|
};
|
|
84
220
|
/** A target is the server that a cell connects to: a stock image. */
|
|
85
221
|
interface Target {
|
|
@@ -104,16 +240,35 @@ interface Target {
|
|
|
104
240
|
readonly command?: readonly string[];
|
|
105
241
|
/** This command stops the server process in the target with SIGKILL, as argv, for a `fin` fault. */
|
|
106
242
|
readonly kill?: readonly string[];
|
|
243
|
+
/** This command makes the server close the connection of each client, as argv, for a `server-close` fault. */
|
|
244
|
+
readonly close?: readonly string[];
|
|
107
245
|
}
|
|
108
246
|
/** A client is what the runs of a cell use: the TCP client of the harness, or a driver on its socket. */
|
|
109
247
|
type Client = "tcp" | "ioredis";
|
|
248
|
+
/**
|
|
249
|
+
* A client image is a process that a lab starts as the client of each run, in the network
|
|
250
|
+
* namespace of the client. An example is an agent of an application on Node. Its command gets the
|
|
251
|
+
* arguments of the TCP client of the harness. These are the address and the port of the target, then
|
|
252
|
+
* the options of the cell. It writes JSON lines with an `ev` and a `t` (CLOCK_MONOTONIC in
|
|
253
|
+
* nanoseconds) on its standard output, as the client of the harness does.
|
|
254
|
+
*/
|
|
255
|
+
interface ClientImage {
|
|
256
|
+
readonly image: string;
|
|
257
|
+
readonly command: readonly string[];
|
|
258
|
+
}
|
|
110
259
|
/**
|
|
111
260
|
* An observer is the side whose detection a cell measures. The client is one observer. The
|
|
112
|
-
* server is
|
|
261
|
+
* server is another observer. It is the hold of the target on the connection of the client, at
|
|
113
262
|
* its server socket in its namespace. The fault is on the peer of the observer. Thus
|
|
114
263
|
* freeze-process freezes the target for the client, and the client for the server.
|
|
264
|
+
*
|
|
265
|
+
* A proxy of a topology is the third observer: its socket toward the client, or its socket toward
|
|
266
|
+
* the target.
|
|
115
267
|
*/
|
|
116
|
-
type Observer = "client" | "server"
|
|
268
|
+
type Observer = "client" | "server" | {
|
|
269
|
+
readonly hop: string;
|
|
270
|
+
readonly toward: "client" | "target";
|
|
271
|
+
};
|
|
117
272
|
/** A cell is one cell of the matrix: a topology, its settings, a workload, a fault and the prediction. */
|
|
118
273
|
interface Cell {
|
|
119
274
|
readonly name: string;
|
|
@@ -159,7 +314,10 @@ interface Cell {
|
|
|
159
314
|
readonly drops?: "to-server" | "to-client";
|
|
160
315
|
/** This length is the longest segment, in bytes, that an `mtu-blackhole` fault lets through to the observer. */
|
|
161
316
|
readonly mtu?: number;
|
|
162
|
-
/**
|
|
317
|
+
/**
|
|
318
|
+
* This specification is the tc netem of a `netem` fault, for example "loss 5%". It is on the
|
|
319
|
+
* egress of the target, or at a hop (`faultAt`) on the egress of its device toward one side.
|
|
320
|
+
*/
|
|
163
321
|
readonly netem?: string;
|
|
164
322
|
/**
|
|
165
323
|
* This delay goes on the egress of the target before the client connects, with tc netem. Thus
|
|
@@ -175,6 +333,22 @@ interface Cell {
|
|
|
175
333
|
readonly targetSettings?: Readonly<Record<string, string>>;
|
|
176
334
|
/** These are the sysctls of the network namespace of the target, for a cell on the server side: its tcp_retries2. */
|
|
177
335
|
readonly targetSysctls?: Readonly<Record<string, string>>;
|
|
336
|
+
/** This topology puts hops between the client and the target. Without it, the client and the target are a direct pair. */
|
|
337
|
+
readonly topology?: Topology;
|
|
338
|
+
/** This place is where the fault goes, when it is not the peer of the observer: for example one side of a hop. */
|
|
339
|
+
readonly faultAt?: FaultPlace;
|
|
340
|
+
/** This time is how long an in-flight client stays idle after the fault holds, before it sends its request. */
|
|
341
|
+
readonly idleBefore?: Quantity;
|
|
342
|
+
/** This toxic is the toxic of a `toxic` fault, which the Toxiproxy hop gets. */
|
|
343
|
+
readonly toxic?: Toxic;
|
|
344
|
+
/** This image replaces the TCP client of the harness for each run. */
|
|
345
|
+
readonly client?: ClientImage;
|
|
346
|
+
/**
|
|
347
|
+
* This name is the proxy hop whose own time a calibration cell measures. The time of a request
|
|
348
|
+
* (`up`) starts at its arrival on the socket of the proxy toward the client. It stops at the
|
|
349
|
+
* write on the socket toward the target. The time of its reply (`down`) goes the other way.
|
|
350
|
+
*/
|
|
351
|
+
readonly calibrates?: string;
|
|
178
352
|
/** This count is the number of runs of the cell (11), across new environments (two at least). */
|
|
179
353
|
readonly runs: number;
|
|
180
354
|
readonly environments: number;
|
|
@@ -185,6 +359,22 @@ interface Cell {
|
|
|
185
359
|
*/
|
|
186
360
|
readonly concurrency?: number;
|
|
187
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* A fault place is the namespace that gets the rules of a fault. "target" and "client" are the two
|
|
364
|
+
* ends. A hop place is a hop, with the side that the rules face. That side is the next hop toward
|
|
365
|
+
* the client, or the next hop toward the target. The rules of a proxy act on its sockets. The
|
|
366
|
+
* rules of a router act on what it forwards, by its device toward that side: the FORWARD chain.
|
|
367
|
+
*/
|
|
368
|
+
type FaultPlace = "target" | "client" | {
|
|
369
|
+
readonly hop: string;
|
|
370
|
+
readonly toward: "client" | "target";
|
|
371
|
+
};
|
|
372
|
+
/** A middlebox event is one line of the router: a change of a conntrack entry, or a reset that it sent. `t` is CLOCK_MONOTONIC in nanoseconds. */
|
|
373
|
+
interface MiddleboxEvent {
|
|
374
|
+
readonly ev: string;
|
|
375
|
+
readonly t: number;
|
|
376
|
+
readonly [field: string]: unknown;
|
|
377
|
+
}
|
|
188
378
|
/** A probe event is one line that the client wrote. `t` is CLOCK_MONOTONIC in nanoseconds: an instant, not an amount. */
|
|
189
379
|
interface ProbeEvent {
|
|
190
380
|
readonly ev: string;
|
|
@@ -195,6 +385,11 @@ interface ProbeEvent {
|
|
|
195
385
|
interface Run {
|
|
196
386
|
readonly environment: number;
|
|
197
387
|
readonly index: number;
|
|
388
|
+
/**
|
|
389
|
+
* This flag marks the warm-up of an environment. It occurs before the counted runs, and the
|
|
390
|
+
* verdict does not count it. On a loaded machine, a new environment is sometimes slow at first.
|
|
391
|
+
*/
|
|
392
|
+
readonly warmup?: true;
|
|
198
393
|
readonly events: readonly ProbeEvent[];
|
|
199
394
|
/** This bracket is the time of the injector around the commit of the fault, on the same clock. It is null for a freeze. */
|
|
200
395
|
readonly fault: {
|
|
@@ -217,6 +412,25 @@ interface Run {
|
|
|
217
412
|
* the run void. Null also makes the run void: no clock line came after the run to tell the count.
|
|
218
413
|
*/
|
|
219
414
|
readonly lost: number | null;
|
|
415
|
+
/** These are the addresses and the namespaces of a topology, which the measurement of a run reads. */
|
|
416
|
+
readonly topology?: RunTopology;
|
|
417
|
+
/** These are the lines of the routers of a topology from the start to the end of the run. */
|
|
418
|
+
readonly middlebox?: readonly MiddleboxEvent[];
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* A run topology tells where each socket of a run is, in a topology:
|
|
422
|
+
* - `targetPeer` is the address of the peer of the target, as the target sees it: the address of
|
|
423
|
+
* the last hop.
|
|
424
|
+
* - `hops` are the network namespaces of the hops, by their names, with the address of the peer of
|
|
425
|
+
* each hop on each side.
|
|
426
|
+
*/
|
|
427
|
+
interface RunTopology {
|
|
428
|
+
readonly targetPeer: string;
|
|
429
|
+
readonly hops: Readonly<Record<string, {
|
|
430
|
+
readonly netns: number;
|
|
431
|
+
readonly client: string;
|
|
432
|
+
readonly target: string;
|
|
433
|
+
}>>;
|
|
220
434
|
}
|
|
221
435
|
//#endregion
|
|
222
436
|
//#region src/observe.d.ts
|
|
@@ -264,14 +478,90 @@ interface Measured {
|
|
|
264
478
|
readonly retransmissions: number;
|
|
265
479
|
/** This count is the number of segments that the socket received after the request. */
|
|
266
480
|
readonly acks: number;
|
|
481
|
+
/** This count is the number of bytes that the client read after the request. */
|
|
482
|
+
readonly bytes?: number;
|
|
483
|
+
/**
|
|
484
|
+
* This time is when a router of the topology forgot the flow of the client. It counts from the
|
|
485
|
+
* last segment that the client received before the fault. It is null when no router forgot the flow.
|
|
486
|
+
*/
|
|
487
|
+
readonly forgot?: Quantity | null;
|
|
488
|
+
/**
|
|
489
|
+
* This time is the first round trip of the socket of the observer. It is the smoothed round trip
|
|
490
|
+
* in its first record, which is the sample of the handshake. It is null when no record has it.
|
|
491
|
+
*/
|
|
492
|
+
readonly rtt?: Quantity | null;
|
|
493
|
+
/** This record tells what reordering did in a run of a measurement-only cell, from the records of its two ends. */
|
|
494
|
+
readonly reordering?: Reordering;
|
|
495
|
+
/** These are the times of the proxy of a calibration cell, one for each request and its reply. */
|
|
496
|
+
readonly crossings?: Crossings;
|
|
267
497
|
/** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */
|
|
268
498
|
readonly void: string | null;
|
|
269
499
|
/** This text tells what in the run disagrees with the prediction, other than its timing, or null. */
|
|
270
500
|
readonly finding: string | null;
|
|
271
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* A reordering record is what reordering, duplicates and losses did to the data of a run. The
|
|
504
|
+
* receiver is the client, and the sender is the socket of its peer.
|
|
505
|
+
* - `outOfOrder` counts the arrivals at the client beyond a hole, and `duplicates` counts the
|
|
506
|
+
* arrivals whose data the client had. The prober reads the two from each arrival, against `rcv_nxt`.
|
|
507
|
+
* - An arrival can hold more than one segment. The kernel joins the segments that wait in the
|
|
508
|
+
* backlog of a socket while its owner holds it.
|
|
509
|
+
* - `ooo` is the count of the kernel of the client in segments (`rcv_ooopack`).
|
|
510
|
+
* - `sacked` and `lost` are the most segments that the sender held as SACKed and as lost.
|
|
511
|
+
* - `dsacks` is the DSACK blocks that the sender received, and `fast` its fast retransmissions.
|
|
512
|
+
* - `degree` is the highest reordering degree of the sender (`tp->reordering`), and `seen` the
|
|
513
|
+
* reorderings that it detected (`reord_seen`).
|
|
514
|
+
*
|
|
515
|
+
* The counts of the kernel are null for a record of the prober before version 0.2.
|
|
516
|
+
*/
|
|
517
|
+
interface Reordering {
|
|
518
|
+
readonly outOfOrder: number;
|
|
519
|
+
readonly duplicates: number;
|
|
520
|
+
readonly ooo: number | null;
|
|
521
|
+
readonly sacked: number | null;
|
|
522
|
+
readonly lost: number | null;
|
|
523
|
+
readonly dsacks: number | null;
|
|
524
|
+
readonly fast: number;
|
|
525
|
+
readonly degree: number | null;
|
|
526
|
+
readonly seen: number | null;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* A crossings record is the time of a proxy for each request (`up`) and for each reply (`down`).
|
|
530
|
+
* The time of a request starts at its arrival on the socket toward the client. It stops at the
|
|
531
|
+
* write on the socket toward the target. The time of a reply goes the other way. A write is a
|
|
532
|
+
* `tx` record of the prober.
|
|
533
|
+
*/
|
|
534
|
+
interface Crossings {
|
|
535
|
+
readonly hop: string;
|
|
536
|
+
readonly up: readonly Quantity[];
|
|
537
|
+
readonly down: readonly Quantity[];
|
|
538
|
+
}
|
|
539
|
+
/** A socket place is a socket of a run: its namespace and its cookie. */
|
|
540
|
+
interface SocketPlace {
|
|
541
|
+
readonly netns: number;
|
|
542
|
+
readonly cookie: number;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* This function finds the socket of the observer of a cell in a run:
|
|
546
|
+
* - the client socket, by the address that it connected from, in its namespace.
|
|
547
|
+
* - the socket of the target for the same connection, in its namespace.
|
|
548
|
+
* - a socket of a proxy of the topology, toward the client or toward the target.
|
|
549
|
+
*/
|
|
550
|
+
export declare const socketOf: (cell: Cell, run: Run) => SocketPlace | undefined;
|
|
551
|
+
/** This function gives what reordering did in a run, from the records of the receiver and of the sender. */
|
|
552
|
+
export declare const reorderingOf: (receiver: readonly KernelRecord[], sender: readonly KernelRecord[]) => Reordering;
|
|
553
|
+
/**
|
|
554
|
+
* This function gives the times of a proxy in a run, for each request after the first. The time
|
|
555
|
+
* of a request starts at its arrival on the socket of the proxy toward the client. It stops at the
|
|
556
|
+
* next write on the socket toward the target. The time of its reply starts at its arrival on the
|
|
557
|
+
* socket toward the target, and it stops at the next write toward the client. A request without
|
|
558
|
+
* each record gives no time.
|
|
559
|
+
*/
|
|
560
|
+
export declare const crossingsOf: (run: Run, hop: string, toClient: SocketPlace | undefined, toTarget: SocketPlace | undefined) => Crossings;
|
|
272
561
|
/**
|
|
273
562
|
* This function reads one run against its cell, from the records of the kernel first.
|
|
274
|
-
* - The socket of the run is the socket that the client connected
|
|
563
|
+
* - The socket of the run is the socket of its observer. It is the socket that the client connected
|
|
564
|
+
* from, in its namespace, or the socket of the target or of a proxy for that connection.
|
|
275
565
|
* - The fault must hold before the event that it must meet. That is before the request goes into
|
|
276
566
|
* it, and before the first keepalive probe. A live peer answers that probe, and the answer
|
|
277
567
|
* starts the idle time again. A run where the fault was late is void. A run whose records the
|
|
@@ -302,6 +592,14 @@ interface Interval {
|
|
|
302
592
|
readonly low: Quantity;
|
|
303
593
|
readonly high: Quantity;
|
|
304
594
|
}
|
|
595
|
+
/** A spread is the distribution of a set of times: its count, its median and its upper quantiles by nearest rank. */
|
|
596
|
+
interface Spread {
|
|
597
|
+
readonly count: number;
|
|
598
|
+
readonly median: Quantity;
|
|
599
|
+
readonly p99: Quantity;
|
|
600
|
+
readonly p999: Quantity;
|
|
601
|
+
readonly max: Quantity;
|
|
602
|
+
}
|
|
305
603
|
/** A verdict is the judgement of a cell on its runs. */
|
|
306
604
|
interface Verdict {
|
|
307
605
|
readonly decision: Decision;
|
|
@@ -317,8 +615,20 @@ interface Verdict {
|
|
|
317
615
|
readonly median: Quantity | null;
|
|
318
616
|
/** This interval is the order-statistic interval of the median at 95 %, or the whole range for fewer than six runs. */
|
|
319
617
|
readonly medianInterval: Interval | null;
|
|
618
|
+
/** This record is the sum of what reordering did in each counted run of a measurement-only cell. */
|
|
619
|
+
readonly reordering?: Reordering;
|
|
620
|
+
/** This record is the time of the proxy of a calibration cell, in each direction. */
|
|
621
|
+
readonly proxy?: {
|
|
622
|
+
readonly hop: string;
|
|
623
|
+
readonly up: Spread | null;
|
|
624
|
+
readonly down: Spread | null;
|
|
625
|
+
};
|
|
320
626
|
readonly why: string;
|
|
321
627
|
}
|
|
628
|
+
/** This function gives the value at quantile p of sorted values, by nearest rank: x_(⌈p·n⌉). */
|
|
629
|
+
export declare const quantileOf: (sorted: readonly number[], p: number) => number;
|
|
630
|
+
/** This function gives the spread of a set of times, or null for none. */
|
|
631
|
+
export declare const spreadOf: (times: readonly Quantity[]) => Spread | null;
|
|
322
632
|
/**
|
|
323
633
|
* This function gives the distribution-free interval of a median: [x_(k), x_(n+1−k)], for the
|
|
324
634
|
* largest k whose two tails each hold 2.5 % or less. For eleven runs, it gives [x_(2), x_(10)],
|
|
@@ -344,6 +654,20 @@ export declare const verdictOf: (prediction: Prediction, runs: readonly Measured
|
|
|
344
654
|
export declare const harnessImage: () => Promise<string>;
|
|
345
655
|
/** This function gives the image of the Node client. */
|
|
346
656
|
export declare const nodeClientImage: () => Promise<string>;
|
|
657
|
+
/** This function gives the image of the router. */
|
|
658
|
+
export declare const routerImage: () => Promise<string>;
|
|
659
|
+
/**
|
|
660
|
+
* This time is the interval of the listing of a router, when its hop does not set one. A listing
|
|
661
|
+
* evicts each expired flow that it walks past, and the DESTROY event of that flow follows.
|
|
662
|
+
*/
|
|
663
|
+
export declare const PUMP: Quantity;
|
|
664
|
+
/**
|
|
665
|
+
* This time is the longest listing of the conntrack table of a router that a prediction assumes.
|
|
666
|
+
* A DESTROY follows an expiry within the interval of the pump and two listings. On 2026-10-15,
|
|
667
|
+
* the slowest of 500 listings in an idle router took 19.4 ms, and the median took 5.5 ms. A
|
|
668
|
+
* longer listing writes a `pump-slow` line, and a run with one near its detection is void.
|
|
669
|
+
*/
|
|
670
|
+
export declare const LISTING: Quantity;
|
|
347
671
|
/**
|
|
348
672
|
* A network fault is a fault that the injector applies as a filter rule. It is a black hole (both
|
|
349
673
|
* directions, or one), or a reset on the next segment. It can also be an ICMP unreachable for each
|
|
@@ -367,6 +691,17 @@ interface FaultOptions {
|
|
|
367
691
|
* because it fires only for a retransmission that went out.
|
|
368
692
|
*/
|
|
369
693
|
type FaultSide = "target" | "client";
|
|
694
|
+
/** A hop of a manifest is one hop of a topology, as the lab started it. */
|
|
695
|
+
interface ManifestHop {
|
|
696
|
+
readonly name: string;
|
|
697
|
+
readonly kind: "router" | "proxy";
|
|
698
|
+
readonly image: string;
|
|
699
|
+
readonly id: string;
|
|
700
|
+
/** These are the settings of the hop: the form and the idle timeout of a router, the files and settings of a proxy. */
|
|
701
|
+
readonly settings: Readonly<Record<string, string>>;
|
|
702
|
+
/** These are the sysctls of the namespace of the hop, as read back after the harness set them. */
|
|
703
|
+
readonly sysctls: Readonly<Record<string, string>>;
|
|
704
|
+
}
|
|
370
705
|
/** A manifest is what a lab records about its machine, its images and its settings. */
|
|
371
706
|
interface Manifest {
|
|
372
707
|
readonly kernel: string;
|
|
@@ -388,18 +723,32 @@ interface Manifest {
|
|
|
388
723
|
readonly id: string;
|
|
389
724
|
readonly packages: string[];
|
|
390
725
|
};
|
|
391
|
-
/** This record is the client of each run: the TCP client of the harness,
|
|
726
|
+
/** This record is the client of each run: the TCP client of the harness, a driver, or an image that the cell gives. */
|
|
392
727
|
readonly client: {
|
|
393
|
-
readonly kind: Client;
|
|
728
|
+
readonly kind: Client | "image";
|
|
394
729
|
readonly image: string;
|
|
395
730
|
};
|
|
396
731
|
/** These are the namespaces that the prober watched, by inode. */
|
|
397
732
|
readonly netns: {
|
|
398
733
|
readonly client: number;
|
|
399
734
|
readonly target: number;
|
|
735
|
+
readonly hops?: Readonly<Record<string, number>>;
|
|
400
736
|
};
|
|
401
737
|
/** These are the sysctls of the client namespace, as read back after the harness set them. */
|
|
402
738
|
readonly sysctls: Readonly<Record<string, string>>;
|
|
739
|
+
/** These are the hops of a topology, in sequence from the client. */
|
|
740
|
+
readonly hops?: readonly ManifestHop[];
|
|
741
|
+
}
|
|
742
|
+
/** These are the JSON lines of a program, as they arrive. */
|
|
743
|
+
interface Lines<T extends {
|
|
744
|
+
readonly ev: string;
|
|
745
|
+
}> {
|
|
746
|
+
readonly seen: T[];
|
|
747
|
+
/** These are the other lines that it wrote, for example an error. They explain a program that stopped its output. */
|
|
748
|
+
readonly other: string[];
|
|
749
|
+
consume: (stream: NodeJS.ReadableStream) => void;
|
|
750
|
+
/** This method gives the first line of one of these kinds, or it rejects after `within` ms. */
|
|
751
|
+
until(kinds: readonly string[], within: number, who: string): Promise<T>;
|
|
403
752
|
}
|
|
404
753
|
/** A probe is the client container of one run. */
|
|
405
754
|
interface Probe {
|
|
@@ -415,14 +764,46 @@ interface Probe {
|
|
|
415
764
|
readonly events: readonly ProbeEvent[];
|
|
416
765
|
stop(): Promise<void>;
|
|
417
766
|
}
|
|
418
|
-
/**
|
|
767
|
+
/** An agent is a container that a lab starts in the namespaces of the target, for example a Node process that signals the server. */
|
|
768
|
+
interface Agent {
|
|
769
|
+
/** These are the JSON lines that it wrote, as they arrive. */
|
|
770
|
+
readonly lines: Lines<{
|
|
771
|
+
readonly ev: string;
|
|
772
|
+
}>;
|
|
773
|
+
/** This method starts a command in the agent, and gives its standard output. */
|
|
774
|
+
exec(argv: readonly string[]): Promise<string>;
|
|
775
|
+
stop(): Promise<void>;
|
|
776
|
+
}
|
|
777
|
+
/** A lab is one environment: the target, the client, the hops of a topology, the injectors and the prober. */
|
|
419
778
|
interface Lab {
|
|
779
|
+
/** This address is the address that the client connects to: the target, or the first proxy of a topology. */
|
|
420
780
|
readonly targetIp: string;
|
|
781
|
+
/** This port is the port that the client connects to. */
|
|
782
|
+
readonly targetPort: number;
|
|
421
783
|
readonly clientIp: string;
|
|
422
784
|
readonly netns: {
|
|
423
785
|
readonly client: number;
|
|
424
786
|
readonly target: number;
|
|
787
|
+
readonly hops?: Readonly<Record<string, number>>;
|
|
425
788
|
};
|
|
789
|
+
/** This record is where each socket of a topology is, or null for a direct pair. */
|
|
790
|
+
readonly topology: RunTopology | null;
|
|
791
|
+
/** These are the lines of the routers of a topology, in sequence, each with the name of its hop. A direct pair has none. */
|
|
792
|
+
readonly middlebox: readonly MiddleboxEvent[];
|
|
793
|
+
/** This method adds a toxic to the Toxiproxy hop of a topology, or removes it with null. The stamps of the injector bracket it. */
|
|
794
|
+
toxic(toxic: Toxic | null): Promise<{
|
|
795
|
+
t0: number;
|
|
796
|
+
t1: number;
|
|
797
|
+
}>;
|
|
798
|
+
/** This method makes the target close the connection of each client, with the command of the target. */
|
|
799
|
+
serverClose(): Promise<void>;
|
|
800
|
+
/**
|
|
801
|
+
* This method starts an agent in the network namespace of the target, and in its process
|
|
802
|
+
* namespace with `pid`. Its command keeps it up, for example `sleep infinity`.
|
|
803
|
+
*/
|
|
804
|
+
agent(image: string, command: readonly string[], options?: {
|
|
805
|
+
readonly pid?: boolean;
|
|
806
|
+
}): Promise<Agent>;
|
|
426
807
|
/** These are all the records that the prober wrote, in sequence. */
|
|
427
808
|
readonly kernel: readonly KernelRecord[];
|
|
428
809
|
/** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */
|
|
@@ -432,29 +813,31 @@ interface Lab {
|
|
|
432
813
|
/** This method sets the sysctls of the target namespace through a privileged sidecar there, and reads them back. */
|
|
433
814
|
targetSysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;
|
|
434
815
|
/**
|
|
435
|
-
* This method applies a fault between the client and the target, from the namespace
|
|
436
|
-
* target. The fault is a black hole, or a reset that answers the next segment of the
|
|
437
|
-
* the
|
|
816
|
+
* This method applies a fault between the client and the target, by default from the namespace
|
|
817
|
+
* of the target. The fault is a black hole, or a reset that answers the next segment of the
|
|
818
|
+
* client. At a proxy of a topology, the rules face the peer of the proxy on one side. The stamps
|
|
819
|
+
* of the injector bracket it.
|
|
438
820
|
*/
|
|
439
|
-
fault(kind: NetworkFault, at?:
|
|
821
|
+
fault(kind: NetworkFault, at?: FaultPlace, options?: FaultOptions): Promise<{
|
|
440
822
|
t0: number;
|
|
441
823
|
t1: number;
|
|
442
824
|
}>;
|
|
443
825
|
/** This method removes a fault. */
|
|
444
|
-
lift(kind: NetworkFault, at?:
|
|
826
|
+
lift(kind: NetworkFault, at?: FaultPlace, options?: FaultOptions): Promise<void>;
|
|
445
827
|
/**
|
|
446
828
|
* This method puts tc netem on the egress of a namespace, for example "delay 20ms" or "loss 5%",
|
|
447
|
-
* or removes it with null.
|
|
829
|
+
* or removes it with null. At a hop, it is the egress of the device of the hop toward one side.
|
|
830
|
+
* The stamps of the injector bracket it.
|
|
448
831
|
*/
|
|
449
|
-
netem(spec: string | null, at?:
|
|
832
|
+
netem(spec: string | null, at?: FaultPlace): Promise<{
|
|
450
833
|
t0: number;
|
|
451
834
|
t1: number;
|
|
452
835
|
}>;
|
|
453
836
|
/**
|
|
454
837
|
* This method sets the TSO, GSO and GRO of a namespace. With them off, netem and a length match
|
|
455
|
-
* act on segments, not on GSO super-packets of up to 64 KB.
|
|
838
|
+
* act on segments, not on GSO super-packets of up to 64 KB. At a hop, it sets the two devices of the hop.
|
|
456
839
|
*/
|
|
457
|
-
offload(on: boolean, at?:
|
|
840
|
+
offload(on: boolean, at?: FaultPlace): Promise<void>;
|
|
458
841
|
/** This method stops the server process in the target with SIGKILL, with the command of the target. */
|
|
459
842
|
kill(): Promise<void>;
|
|
460
843
|
/**
|
|
@@ -470,7 +853,7 @@ interface Lab {
|
|
|
470
853
|
* waits for that instant in the VM, thus the length of a stall does not include the latency of
|
|
471
854
|
* a docker exec. Stamps bracket it, as for a fault.
|
|
472
855
|
*/
|
|
473
|
-
heal(at:
|
|
856
|
+
heal(at: FaultPlace, when: number): Promise<{
|
|
474
857
|
t0: number;
|
|
475
858
|
t1: number;
|
|
476
859
|
}>;
|
|
@@ -493,20 +876,48 @@ interface Lab {
|
|
|
493
876
|
manifest(sysctls: Readonly<Record<string, string>>, settings?: Readonly<Record<string, string>>, targetSysctls?: Readonly<Record<string, string>>): Promise<Manifest>;
|
|
494
877
|
close(): Promise<void>;
|
|
495
878
|
}
|
|
879
|
+
/** This function gives the content of a file of a proxy, with the address of the next hop for each {{next}}. */
|
|
880
|
+
export declare const templated: (content: string, next: string) => string;
|
|
881
|
+
/** This function gives the JSON body of a toxic for the API of Toxiproxy. Each toxic of a cell has the name `fault`. */
|
|
882
|
+
export declare const toxicBody: (toxic: Toxic) => string;
|
|
883
|
+
/**
|
|
884
|
+
* This function gives the routes of a chain: for each router, the networks after it, which the
|
|
885
|
+
* element before it reaches through it. Element 0 is the client, and element k is hop k - 1. Network
|
|
886
|
+
* j is the network between element j and element j + 1.
|
|
887
|
+
*/
|
|
888
|
+
export declare const routesOf: (hops: readonly Pick<Hop, "kind">[]) => Array<{
|
|
889
|
+
readonly from: number;
|
|
890
|
+
readonly via: number;
|
|
891
|
+
readonly networks: readonly number[];
|
|
892
|
+
}>;
|
|
893
|
+
/**
|
|
894
|
+
* This function gives the endpoint that an element connects to: the first element after it that
|
|
895
|
+
* is not a router. Element 0 is the client, element k is hop k - 1, and the last element is the target.
|
|
896
|
+
*/
|
|
897
|
+
export declare const endpointAfter: (hops: readonly Pick<Hop, "kind">[], element: number) => number;
|
|
898
|
+
/** This function gives the device of a route from its line in `ip -o route get`: "10.0.0.2 dev eth0 src …". */
|
|
899
|
+
export declare const deviceOf: (route: string) => string;
|
|
496
900
|
/**
|
|
497
901
|
* This function opens one environment:
|
|
498
|
-
* - a new
|
|
902
|
+
* - a chain of new networks, and the stock target on the last one. A direct pair has one network.
|
|
499
903
|
* - a client that holds its own network namespace, with a privileged sidecar in it for its
|
|
500
|
-
* sysctls. Docker Desktop keeps /proc/sys read-only in a container that operates
|
|
904
|
+
* sysctls and its routes. Docker Desktop keeps /proc/sys read-only in a container that operates
|
|
905
|
+
* without privileges.
|
|
906
|
+
* - each hop of the topology, on the network of each of its sides. A hop is a router, or a stock
|
|
907
|
+
* proxy with an injector sidecar in its namespace.
|
|
501
908
|
* - an injector sidecar in the namespace of the target.
|
|
502
|
-
* - the prober, a privileged container that loads the kernel program and watches the
|
|
503
|
-
*
|
|
909
|
+
* - the prober, a privileged container that loads the kernel program and watches the namespace of
|
|
910
|
+
* the client, of the target and of each hop.
|
|
504
911
|
*
|
|
505
912
|
* The client of each run is its own container in the namespace of the client. It is the TCP
|
|
506
|
-
* client of the harness,
|
|
507
|
-
* lines.
|
|
913
|
+
* client of the harness, a driver from its own image (ioredis on Node), or an image that the
|
|
914
|
+
* caller gives. Each prints the same lines.
|
|
915
|
+
*
|
|
916
|
+
* The client reaches the networks after a router through that router, by a route that its
|
|
917
|
+
* privileged sidecar adds. A router translates the address of the side of the client, thus the
|
|
918
|
+
* target and each later hop see the router as the peer.
|
|
508
919
|
*/
|
|
509
|
-
export declare const openLab: (target: Target, kind?: Client) => Promise<Lab>;
|
|
920
|
+
export declare const openLab: (target: Target, kind?: Client | ClientImage, topology?: Topology) => Promise<Lab>;
|
|
510
921
|
//#endregion
|
|
511
922
|
//#region src/run.d.ts
|
|
512
923
|
/** A cell result is a cell with its manifests, its runs, their measurements and its verdict. */
|
|
@@ -514,6 +925,8 @@ interface CellResult {
|
|
|
514
925
|
readonly cell: Cell;
|
|
515
926
|
readonly manifests: readonly Manifest[];
|
|
516
927
|
readonly runs: readonly Run[];
|
|
928
|
+
/** These are the warm-up runs, one for each environment before its counted runs. The verdict does not count them. */
|
|
929
|
+
readonly warmups: readonly Run[];
|
|
517
930
|
readonly measured: readonly Measured[];
|
|
518
931
|
readonly verdict: Verdict;
|
|
519
932
|
/** These times are when its first environment opened and its last environment closed, on the wall clock. */
|
|
@@ -539,8 +952,10 @@ export declare const spread: (runs: number, environments: number) => number[];
|
|
|
539
952
|
* the cell from 11 runs to 22, and then to 44, each extension in new environments. With a
|
|
540
953
|
* concurrency of more than one, that many environments operate at the same time, each with its
|
|
541
954
|
* own prober. The runs then go across at least as many environments.
|
|
955
|
+
*
|
|
956
|
+
* Each environment does a warm-up first, and the result keeps it apart from the counted runs.
|
|
542
957
|
*/
|
|
543
958
|
export declare const runCell: (cell: Cell, say?: (line: string) => void) => Promise<CellResult>;
|
|
544
959
|
//#endregion
|
|
545
|
-
export type { Cell, CellResult, Client, Decision, Fault, FaultSide, Interval, KernelRecord, Lab, Manifest, Measured, NetworkFault, Observer, Outcome, Prediction, Probe, ProbeEvent, Recovers, Reference, Run, Target, Verdict };
|
|
960
|
+
export type { Agent, Cell, CellResult, Client, ClientImage, Crossings, Decision, Fault, FaultPlace, FaultSide, Hop, Interval, KernelRecord, Lab, Manifest, ManifestHop, Measured, MiddleboxEvent, MiddleboxForm, NetworkFault, Observer, Outcome, Prediction, Probe, ProbeEvent, ProxyHop, ProxyTime, Recovers, Reference, Reordering, Replies, RouterHop, RttRange, Run, RunTopology, SocketPlace, Spread, Target, Topology, Toxic, Verdict };
|
|
546
961
|
//# sourceMappingURL=index.d.ts.map
|