@mark1russell7/harness 0.7.0 → 0.9.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["ERRNO","between","newState","oldState","between","sleep","STEADY","TAIL_MS","waitFor","droppedSoFar","sleep","TAIL_MS","droppedSoFar","driverSettled"],"sources":["../src/cell.ts","../src/verdict.ts","../src/driver.ts","../src/timeline.ts","../src/observe.ts","../src/lab.ts","../src/group.ts","../src/replset.ts","../src/run.ts"],"sourcesContent":["import type { KernelRecord } from \"@mark1russell7/prober\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\n\nexport type { KernelRecord };\n\n/**\n * A fault is a failure that the harness injects, by the name of its manifestation. Each name is\n * the last segment of the identifier of the manifestation in the corpus:\n * - `blackhole` is two DROP rules in the network namespace of the target. Thus the measurement\n * gets the kernel timers of the client.\n * - `freeze-process` is the cgroup freezer on the target, while its kernel stays up.\n * - `rst` answers the next segment of the client with a reset, from the namespace of the target.\n * It is the reference for the noise of the harness itself.\n * - `stall` is a black hole that stops by itself, `Cell.stall` after the client sends its\n * request. The VM times it. It shows what recovers, and when.\n * - `fin` stops the process of the peer with SIGKILL, while its kernel stays up. The kernel closes its sockets\n * with a FIN, or with a reset when unread data is in the queue.\n * - `unreachable` answers each segment from the observer with an ICMP unreachable (`Cell.icmp`).\n * Linux keeps it as a soft error on an established socket, and it continues to retransmit.\n * - `simplex` drops the segments in one direction only (`Cell.drops`).\n * - `mtu-blackhole` drops each segment to the observer that is longer than `Cell.mtu` bytes.\n * - `netem` is tc netem on the egress of the target (`Cell.netem`): loss, duplication,\n * reordering or corruption. Its outcome is a distribution.\n *\n * These faults are of a topology (`Cell.topology`):\n * - `middlebox-forget` injects nothing. The router of the topology forgets an idle flow at its own\n * idle timeout, and its form tells what a late packet then meets.\n * - `proxy-idle` injects nothing. The proxy of the topology closes an idle connection at its own\n * timeout.\n * - `toxic` adds a toxic of Toxiproxy (`Cell.toxic`) through its HTTP API.\n * - `server-close` makes the server close the connection of its peer, with the command of the\n * target (`Target.close`): `CLIENT KILL` in Redis.\n * - `none` injects nothing. A calibration cell measures the path itself: for example the time of a\n * proxy between its two sockets (`Cell.calibrates`).\n *\n * A fault at a router of the topology (`Cell.faultAt`) acts on what the router forwards toward\n * one side. It is a black hole, a reset or an ICMP unreachable. It can also be a simplex drop, an\n * MTU drop or netem.\n *\n * These faults are of a replicated group (`Cell.group` or `Cell.replicaSet`), on the part that `Cell.faultAt` names:\n * - `blackhole` drops each segment between the part and each of its peers. `rst` answers each\n * segment from a peer with a reset, thus the part refuses each connection.\n * - `partition` is a black hole between the part and some of its peers only (`GroupPlace.cuts`).\n * - `freeze-process` stops the server process of the part with SIGSTOP, and SIGCONT thaws it. Its\n * kernel stays up. `fin` stops the process with SIGKILL.\n * - `planned-failover` asks a Sentinel for a failover: `SENTINEL FAILOVER <group>`.\n *\n * This fault is of an HTTP server:\n * - `app-stall` injects nothing. The server process holds the reply to the request, because the\n * request asks for that (`HttpProbe.hold`), while its kernel stays up.\n *\n * This fault is of a replica set (`Cell.replicaSet`):\n * - `step-down` sends `replSetStepDown` to the primary, as a client sends a command.\n */\nexport type Fault =\n | \"blackhole\"\n | \"freeze-process\"\n | \"rst\"\n | \"stall\"\n | \"fin\"\n | \"unreachable\"\n | \"simplex\"\n | \"mtu-blackhole\"\n | \"netem\"\n | \"middlebox-forget\"\n | \"proxy-idle\"\n | \"toxic\"\n | \"server-close\"\n | \"partition\"\n | \"planned-failover\"\n | \"app-stall\"\n | \"step-down\"\n | \"none\";\n\n/**\n * A middlebox form tells what a router does with a flow that it forgot, when a late packet comes:\n * - `reset-both` sends a reset to the two ends when it forgets the flow.\n * - `reset-next` answers the next packet from inside with a reset to its sender, and drops a late\n * packet from outside.\n * - `silent-drop` drops each late packet, in each direction.\n * - `recreate` lets a late packet from inside through, and the flow starts again. It drops a late\n * packet from outside.\n * - `none` is a plain NAT router. A late packet from inside starts the flow again, and the stack of\n * the router answers a late packet from outside with a reset.\n */\nexport type MiddleboxForm = \"reset-both\" | \"reset-next\" | \"silent-drop\" | \"recreate\" | \"none\";\n\n/**\n * A router is a hop that forwards and translates the addresses of the side of the client (NAT),\n * with conntrack. It is the router image of the harness.\n */\nexport interface RouterHop {\n readonly name: string;\n readonly kind: \"router\";\n readonly form: MiddleboxForm;\n /** This time is the idle timeout of an established flow: nf_conntrack_tcp_timeout_established, in whole seconds. */\n readonly idle: Quantity;\n /**\n * This time is the interval of the listing that evicts each expired flow. Without it, the\n * interval is 0.1 s. The DESTROY event of an expired flow waits for that listing.\n */\n readonly pump?: Quantity;\n /**\n * These are the sysctls of the namespace of the router, which the router sets itself: it is\n * privileged. An example is `net.ipv4.icmp_ratelimit` for the ICMP errors of a fault at the router.\n */\n readonly sysctls?: Readonly<Record<string, string>>;\n}\n\n/**\n * A proxy is a hop that ends each TCP connection from the side of the client and opens its own to\n * the next hop. It is a stock image, pinned by digest.\n */\nexport interface ProxyHop {\n readonly name: string;\n readonly kind: \"proxy\";\n readonly image: string;\n /** This port is the port that the proxy listens on for the side of the client. */\n readonly port: number;\n readonly command?: readonly string[];\n /** 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. */\n readonly files?: Readonly<Record<string, string>>;\n /** This text is the log line that tells that the proxy serves. */\n readonly ready?: string;\n /** This record is the HTTP API of Toxiproxy: its port, and the name of the proxy that gets the toxics. */\n readonly api?: { readonly port: number; readonly proxy: string };\n /** These are the sysctls of the namespace of the proxy. A privileged sidecar sets them before each run. */\n readonly sysctls?: Readonly<Record<string, string>>;\n /** These are the settings of the proxy, for the manifest: for example its idle timeouts. */\n readonly settings?: Readonly<Record<string, string>>;\n}\n\n/**\n * A hop is one box between the client and the target. Each hop is on two networks: the network\n * of the side of the client, and the network of the side of the target.\n */\nexport type Hop = RouterHop | ProxyHop;\n\n/**\n * A topology is the chain of hops between the client and the target, in sequence from the client.\n * Without hops, the client and the target are a direct pair.\n */\nexport interface Topology {\n readonly name: string;\n readonly hops: readonly Hop[];\n}\n\n/**\n * A group is a replicated group under an external failover authority, as data. It is a primary and\n * its replicas, and the Sentinels that decide which member is the primary. Each member and each\n * Sentinel is a container from the same stock image, pinned by digest. Each has its own network\n * namespace, thus a fault can act on one part. The client finds the primary through the\n * Sentinels, and the parts and the client share one network.\n */\nexport interface Group {\n /** This name is the name of the group that the Sentinels monitor, for example `mymaster`. */\n readonly name: string;\n /** This image is the stock image of each member and each Sentinel, pinned by digest. */\n readonly image: string;\n /** These are the programs of the image: the server, the Sentinel and the command line client. */\n readonly programs: { readonly server: string; readonly sentinel: string; readonly cli: string };\n readonly port: number;\n readonly sentinelPort: number;\n /** These are the names of the members. The first one is the primary when the environment starts. */\n readonly members: readonly string[];\n readonly sentinels: readonly string[];\n /** This count is how many Sentinels must agree that the primary is down. */\n readonly quorum: number;\n /** These are the settings of each member, as its configuration file spells them. The harness adds `replicaof` for each replica. */\n readonly memberSettings: Readonly<Record<string, string>>;\n /**\n * These are the settings of each Sentinel for the group, as `sentinel <setting> <group> <value>`\n * spells them: `down-after-milliseconds`, `failover-timeout` and `parallel-syncs`.\n */\n readonly sentinelSettings: Readonly<Record<string, string>>;\n /** These lines tell that a member and a Sentinel serve. */\n readonly ready: { readonly member: string; readonly sentinel: string };\n}\n\n/**\n * A replica set is a replicated group with no external authority, as data: its members elect their\n * own primary. Each member is a container from the same stock image, pinned by digest, in its own\n * network namespace. The client finds the primary by its own monitors. The harness starts the set\n * with the address of each member, and it waits for a primary.\n */\nexport interface ReplicaSet {\n /** This name is the name of the set, for example `rs0`. */\n readonly name: string;\n /** This image is the stock image of each member, pinned by digest. */\n readonly image: string;\n /** These are the programs of the image: the server and its shell. */\n readonly programs: { readonly server: string; readonly shell: string };\n /** These are the options of the server of each member, after the name of the set: for example the size of its cache. */\n readonly args?: readonly string[];\n readonly port: number;\n /** These are the names of the members. The first one starts the set. */\n readonly members: readonly string[];\n /** These are the settings of the configuration of the set, as `rs.initiate` takes them: for example `electionTimeoutMillis`. */\n readonly settings: Readonly<Record<string, number>>;\n /** This line tells that a member serves. */\n readonly ready: string;\n}\n\n/** A group role is a part of a group by its role at the start of a run: the primary, a replica, or a Sentinel. */\nexport type GroupRole = \"primary\" | \"replica\" | \"sentinel\";\n\n/**\n * A group place is the part of a group that a fault acts on. A network fault cuts the part from\n * some of its peers:\n * - `all`: each other part of the group, and each client. This is the default.\n * - `group`: the other members and the Sentinels, but not the clients.\n * - `clients`: the clients, but not the group.\n * - `one`: one other member, in both directions.\n * - `one-way`: one other member, for the segments from the part only.\n */\nexport interface GroupPlace {\n readonly role: GroupRole;\n readonly cuts?: \"all\" | \"group\" | \"clients\" | \"one\" | \"one-way\";\n}\n\n/** A toxic is one toxic of Toxiproxy, as its API takes it. Its toxicity is 1. */\nexport interface Toxic {\n readonly type:\n | \"latency\"\n | \"timeout\"\n | \"reset_peer\"\n | \"slow_close\"\n | \"bandwidth\"\n | \"limit_data\"\n | \"slicer\";\n readonly stream: \"downstream\" | \"upstream\";\n readonly attributes: Readonly<Record<string, number>>;\n}\n\n/**\n * A reference is the event that the time of a detector counts from: the event that arms the\n * timer in its machine document. Keepalive counts from the last received segment. The give-up of\n * the retransmissions counts from the first retransmission of the RTO timer, not from the tail\n * loss probe one RTO before it. The request itself meets a reset, thus a reset counts from the\n * send of the client. A FIN counts from the change of state of the socket of the peer when its\n * process dies (`peer-closed`). A toxic of Toxiproxy counts from the start of its commit, as the\n * injector stamps it (`fault`).\n *\n * A client that starts into a fault counts from its `connecting` line, or from its first line\n * without one (`client-start`).\n */\nexport type Reference =\n | \"last-received\"\n | \"first-retransmission\"\n | \"request-sent\"\n | \"peer-closed\"\n | \"fault\"\n | \"client-start\";\n\n/**\n * This prediction tells when a connection moves again after a stall. That occurs at the first\n * firing of its retransmission timer after the stall stops, and its reply comes back one round\n * trip later.\n */\nexport interface Recovers {\n /** This count identifies the firing that gets through: the number of firings before it. */\n readonly firing: number;\n /** This time is the earliest return of the reply, from the send of the request. */\n readonly low: Quantity;\n /** This time is the latest return of the reply, from the send of the request. */\n readonly high: Quantity;\n /**\n * This range is where the stall must stop, from the send, so that this firing is the first\n * after it. The range starts after the latest time of the firing before, and it stops before\n * the earliest time of this firing.\n */\n readonly heal: { readonly low: Quantity; readonly high: Quantity };\n}\n\n/**\n * This prediction tells when the reply to the request comes back, from the send of the request.\n * For example, the reply comes after the latency of a toxic, or after a forgotten flow starts again.\n */\nexport interface Replies {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * An RTT range is the range of the first round trip of the observer that a band assumes. The\n * models read a round trip, for example 1 ms. The band at that value holds within the noise for\n * each round trip in the range. A run whose first round trip is above the range is void.\n */\nexport interface RttRange {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A proxy time is the time of one proxy in one direction that a band includes. It is an upper\n * quantile of the times that a calibration cell of the proxy measured. Each time goes from an\n * arrival on one socket of the proxy to its write on the other. `up` goes toward the target, and\n * `down` toward the client.\n */\nexport interface ProxyTime {\n readonly hop: string;\n readonly direction: \"up\" | \"down\";\n /** This fraction is the quantile of the calibration, for example 0.99. */\n readonly quantile: number;\n readonly time: Quantity;\n}\n\n/**\n * A stage is one step of the timeline of a group after a fault, by the event that a Sentinel\n * publishes for it. The stages are the subjective down, the objective down, the start of a\n * failover and the election. Then the selection of a replica, the promotion, the switch to the new\n * primary, and the conversion of the old primary into a replica.\n */\nexport type SentinelStage =\n | \"+sdown\"\n | \"+odown\"\n | \"+try-failover\"\n | \"+elected-leader\"\n | \"+selected-slave\"\n | \"+promoted-slave\"\n | \"+switch-master\"\n | \"+convert-to-slave\";\n\n/**\n * A replica set stage is one step of the timeline of a replica set after a fault. A line of the log\n * of a member marks it, or a line of the client. The stages are a member that marks the primary\n * DOWN, the start of an election and its win, and the end of the catch-up. Then come the first\n * write of the new primary and the step-down of the old one. The client's lines give its discovery\n * of the new primary, the write-off of the old one, and the first write that a primary accepts.\n */\nexport type ReplicaSetStage =\n | \"member-down\"\n | \"election-start\"\n | \"elected\"\n | \"caught-up\"\n | \"writable\"\n | \"old-primary-steps-down\"\n | \"discovered\"\n | \"written-off\"\n | \"first-write\";\n\n/** A stage is one step of the timeline of a group: of a group under Sentinels, or of a replica set. */\nexport type Stage = SentinelStage | ReplicaSetStage;\n\n/** A stage band is when the first part announces a stage, from the reference of the cell. */\nexport interface StageBand {\n readonly stage: Stage;\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A group prediction tells what a group does after the fault, beside the band of its cell:\n * - `lands` is the primary that the first accepted write after the fault lands on: a new one, or the same one.\n * - `failover` tells if a Sentinel announces a switch to a new primary in the window.\n * - `stages` are the bands of the stages of the timeline. Each band includes the error of the\n * stamps of the observer (`stamps`).\n * - `errors` are the errors that the client must meet on the way, each with the band of its\n * first occurrence. An example is the error of the offline queue when it gives up.\n * - `quiet` are the stages that must not occur after the fault. Each one that a run shows is a finding.\n */\nexport interface GroupPrediction {\n readonly lands: \"new\" | \"same\";\n readonly failover: boolean;\n readonly stages: readonly StageBand[];\n readonly stamps?: Quantity;\n readonly errors?: ReadonlyArray<{ readonly name: string; readonly low: Quantity; readonly high: Quantity }>;\n readonly quiet?: readonly Stage[];\n}\n\n/**\n * A middlebox expectation tells what the router of a topology does with the flow in the window of\n * a silent prediction. Without it, the router keeps the flow. With `forgets`, the router forgets\n * the flow in that band after the last segment of the client, and nothing detects it.\n */\nexport interface MiddleboxExpectation {\n readonly forgets: { readonly low: Quantity; readonly high: Quantity };\n}\n\n/**\n * A request prediction tells what the second request of an HTTP client meets, beside the band of\n * its cell. The client writes it in its lines: the connection of each try, and the outcome.\n * - `connection` is the connection of its first try: the one that the first request used\n * (`reused`), or a new one.\n * - `retried` tells that the client sent it again on a new connection by itself.\n * - `error` is the error that reaches the caller, or null for a reply.\n * - `pending` tells that nothing ends the request in the window of the cell.\n */\nexport interface RequestPrediction {\n readonly connection: \"reused\" | \"new\";\n readonly retried: boolean;\n readonly error: string | null;\n readonly pending?: true;\n}\n\n/**\n * A line match is a line of the client that a driver band waits for. It has the `ev` of the line,\n * and the value of each field that it names. A dotted name is a field in a field, for example\n * `failure.name`. `name` is the field that names the error of the line, for its outcome.\n */\nexport interface LineMatch {\n readonly ev: string;\n readonly where?: Readonly<Record<string, string | number | boolean>>;\n readonly name?: string;\n}\n\n/**\n * A driver step is one more band of a driver after the band of its cell. An example is the error\n * of the retry after the interruption. It has its own reference on a socket of the driver, its\n * line, and its band. A run whose step is outside its band, or absent, has a finding.\n */\nexport interface DriverStep {\n readonly what: string;\n /** This name is the name of the error of the line, or OK. */\n readonly name: string;\n /** This role is the socket of the reference. Without it, the socket is the socket of the band of the cell. */\n readonly socket?: string;\n readonly from: Reference;\n readonly ends: LineMatch;\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A driver band is a band of a cell whose client is a driver with more than one socket, for\n * example the MongoDB driver. It names the socket of the reference, and the line that ends the\n * band:\n * - `socket` is the role of the socket, as the client names it in its `socket` lines. The socket\n * `server` is the socket of the target for the first connection of the pool.\n * - `ends` is the line of the client that marks the detection. Without it, the detection is the\n * report of the kernel on the socket: an error, or the end of the file.\n * - `reply` is the line of a reply that a silent prediction times (`replies`), with its reference.\n * - `quiet` are the lines that must not occur after the fault, in the window of the prediction.\n * - `then` are the later bands, each one a test of its own.\n *\n * A band whose line ends it is a timer of the driver. Its `last-received` is the last segment with\n * data that its socket received or sent. The idle timer of a Node socket starts again at each read\n * and each write. A band of the kernel counts from the last segment of any kind that the socket\n * received.\n */\nexport interface DriverBand {\n readonly socket: string;\n readonly ends?: LineMatch;\n readonly reply?: { readonly from: Reference; readonly ends: LineMatch };\n readonly quiet?: readonly LineMatch[];\n readonly then?: readonly DriverStep[];\n}\n\n/**\n * A prediction is what a cell predicts. Either a detection occurs in an interval [T_lo, T_hi]\n * after the reference event, or nothing detects the fault in a window. Nothing detects a stall\n * that is shorter than the give-up, and the connection recovers from it. The prediction then also\n * tells when. A silent prediction can also tell when the reply to the request comes back\n * (`replies`), and what a router does with the flow (`middlebox`).\n *\n * A band can name the first round trip that it assumes (`rtt`), and the proxy times that it\n * includes (`through`).\n *\n * A cell whose outcome is a distribution, for example under netem loss, has no band. Its\n * prediction is `detects: null` with the reason, and its runs are measurements only. A\n * measurement-only cell that names a reference (`from`) also records the time from it to the end\n * of the connection, or to the last data.\n *\n * For a group cell, the band is the first write that a primary accepts after the fault, and its\n * errno is `OK`. `group` tells which primary, and the stages of the group on the way.\n *\n * For an HTTP cell, `request` tells what the second request meets. The errno `CLOSED` is a close\n * of the observer itself: its pool closes an idle connection, a FIN that it sends.\n *\n * A band of a driver (`driver`) counts from a socket of the driver to a line of the client.\n */\nexport type Prediction =\n | {\n readonly detects: true;\n /** This name is the timer of the machine that fires first, or the event that detects the fault, with its errno. */\n readonly timer: string;\n /**\n * This name is the errno of the detection, or EOF for an end of file without an error. It is\n * OK for a write that a primary accepts, and CLOSED for a close of the observer itself.\n */\n readonly errno: string;\n readonly from: Reference;\n /** This time is the closed form, less the most that the timer can fire early. */\n readonly low: Quantity;\n /** This time is the closed form, plus the most that the timer can fire late, plus each proxy time. */\n readonly high: Quantity;\n readonly rtt?: RttRange;\n readonly through?: readonly ProxyTime[];\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n readonly driver?: DriverBand;\n }\n | {\n readonly detects: false;\n readonly window: Quantity;\n readonly recovers?: Recovers;\n readonly replies?: Replies;\n readonly middlebox?: MiddleboxExpectation;\n readonly rtt?: RttRange;\n readonly through?: readonly ProxyTime[];\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n readonly driver?: DriverBand;\n }\n | {\n readonly detects: null;\n readonly window: Quantity;\n readonly why: string;\n readonly from?: Reference;\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n };\n\n/** A target is the server that a cell connects to: a stock image. */\nexport interface Target {\n /** This image is a stock image, pinned by digest. */\n readonly image: string;\n readonly port: number;\n /** This text is the log line that tells that the target serves. */\n readonly ready: string;\n /**\n * These commands set and read one setting of the target, as argv in it. The name and the value\n * come after them: `redis-cli CONFIG SET` and `CONFIG GET` for Redis. Without them, the target\n * has no settings to set.\n */\n readonly settings?: { readonly set: readonly string[]; readonly get: readonly string[] };\n /**\n * This command replaces the command of the image, for example a shell that holds the server\n * process as its child. Then a `fin` fault can stop the server, and the container stays up.\n */\n readonly command?: readonly string[];\n /** This command stops the server process in the target with SIGKILL, as argv, for a `fin` fault. */\n readonly kill?: readonly string[];\n /** This command makes the server close the connection of each client, as argv, for a `server-close` fault. */\n readonly close?: readonly string[];\n /**\n * These files go into the target before it starts, by their path, for example the script of a\n * server on a stock Node image. The image stays stock.\n */\n readonly files?: Readonly<Record<string, string>>;\n /**\n * This flag tells that the target writes JSON lines with an `ev` and a `t` on its standard\n * output, on CLOCK_MONOTONIC. Each run then keeps its lines (`Run.server`).\n */\n readonly lines?: true;\n /**\n * This name is the server process, as /proc/PID/comm holds it. With it, a freeze of the target\n * stops this process with SIGSTOP from a sidecar in its process namespace, and the injector\n * stamps it. SIGCONT then thaws it. Without it, the freeze is the cgroup freezer, with no stamps.\n */\n readonly program?: string;\n}\n\n/**\n * A client is what the runs of a cell use: the TCP client of the harness, or a driver on its\n * socket. The HTTP clients are the `http.Transport` of Go (`go-http`), the `http.Agent` of Node\n * (`http-agent`) and an HTTP/1.1 `Client` of undici (`undici`). The MongoDB driver is `mongodb`.\n */\nexport type Client = \"tcp\" | \"ioredis\" | \"go-http\" | \"http-agent\" | \"undici\" | \"mongodb\";\n\n/**\n * An HTTP probe is the workload of an HTTP client. The first request is a GET of the root. The\n * second request follows the mode of the probe: after the signal (`inflight`), or after a gap\n * (`gap`).\n */\nexport interface HttpProbe {\n /** This method is the method of the second request. */\n readonly method: \"GET\" | \"POST\";\n /** This time is the gap of a `gap` workload: from the end of the first reply to the second request. */\n readonly gap?: Quantity;\n /** This time is how long the server holds the reply to the second request (`/hold?ms=`). */\n readonly hold?: Quantity;\n /** This length is the length of the body of the reply to the second request, in bytes (`/big?bytes=`). */\n readonly bytes?: number;\n /** These are settings of the client over its defaults, by the names of its flags: for example `idle-conn-timeout-ms`. */\n readonly options?: Readonly<Record<string, number>>;\n}\n\n/**\n * A client image is a process that a lab starts as the client of each run, in the network\n * namespace of the client. An example is an agent of an application on Node. Its command gets the\n * arguments of the TCP client of the harness. These are the address and the port of the target, then\n * the options of the cell. It writes JSON lines with an `ev` and a `t` (CLOCK_MONOTONIC in\n * nanoseconds) on its standard output, as the client of the harness does.\n */\nexport interface ClientImage {\n readonly image: string;\n readonly command: readonly string[];\n}\n\n/**\n * An observer is the side whose detection a cell measures. The client is one observer. The\n * server is another observer. It is the hold of the target on the connection of the client, at\n * its server socket in its namespace. The fault is on the peer of the observer. Thus\n * freeze-process freezes the target for the client, and the client for the server.\n *\n * A proxy of a topology is the third observer: its socket toward the client, or its socket toward\n * the target.\n */\nexport type Observer =\n | \"client\"\n | \"server\"\n | { readonly hop: string; readonly toward: \"client\" | \"target\" };\n\n/** A cell is one cell of the matrix: a topology, its settings, a workload, a fault and the prediction. */\nexport interface Cell {\n readonly name: string;\n readonly target: Target;\n /** These are the sysctls of the network namespace of the client, as the kernel spells them. The harness sets them before the client connects. */\n readonly sysctls: Readonly<Record<string, string>>;\n readonly probe: {\n /**\n * This mode tells if the client is idle after its first reply, or sends a request into the\n * fault. In a stream, it sends `count` requests into it, each `interval` after the reply before.\n * An HTTP client in a `gap` sends its second request a gap after its first reply, by itself.\n */\n readonly mode: \"idle\" | \"inflight\" | \"stream\" | \"gap\";\n /** This count is the number of requests of a stream. */\n readonly count?: number;\n /** This time is the pause after each reply of a stream, before the next request. */\n readonly interval?: Quantity;\n /** This flag sets SO_KEEPALIVE without overrides for the socket: the keepalive sysctls of the namespace apply. */\n readonly keepalive: boolean;\n /** This time sets TCP_USER_TIMEOUT. Without it, the default of the kernel (off) applies. */\n readonly userTimeout?: Quantity;\n /**\n * This text is the request that an in-flight run sends, as a Redis inline command. Without it,\n * the request is PING. BLPOP with a timeout makes the server answer only at the timeout. Thus\n * a cell on the server side has a reply in flight when the fault occurs.\n */\n readonly request?: string;\n /**\n * This driver replaces the TCP client of the harness, with its own options. The driver then\n * ignores `keepalive` and `userTimeout`:\n * - ioredis 6 on Node, whose keepAlive is the keepalive delay of the socket.\n * - the MongoDB driver on Node, against the replica set `set`. Its operation is a write, or\n * the read of a long document (`read-long`). `Cell.driverOptions` are its options.\n */\n readonly driver?:\n | { readonly name: \"ioredis\"; readonly keepAlive: Quantity }\n | { readonly name: \"mongodb\"; readonly set: string; readonly op?: \"write\" | \"read-long\" };\n /** This workload is the workload of an HTTP client (`Cell.client` is an HTTP client). The client then ignores `keepalive` and `userTimeout`. */\n readonly http?: HttpProbe;\n };\n readonly fault: Fault;\n /** This time is the length of a stall, from the send of the request. */\n readonly stall?: Quantity;\n /** This code is the ICMP unreachable of an `unreachable` fault. */\n readonly icmp?: \"host\" | \"net\" | \"port\" | \"admin\";\n /** This direction is the one that a `simplex` fault drops: the segments to the server, or to the client. */\n readonly drops?: \"to-server\" | \"to-client\";\n /** This length is the longest segment, in bytes, that an `mtu-blackhole` fault lets through to the observer. */\n readonly mtu?: number;\n /**\n * This specification is the tc netem of a `netem` fault, for example \"loss 5%\". It is on the\n * egress of the target, or at a hop (`faultAt`) on the egress of its device toward one side.\n */\n readonly netem?: string;\n /**\n * This delay goes on the egress of the target before the client connects, with tc netem. Thus\n * each round trip is longer by the delay, and so is the RTO.\n */\n readonly latency?: Quantity;\n /** These commands run in the target before a client connects, each as argv: for example, a large value to read. */\n readonly targetSetup?: readonly (readonly string[])[];\n readonly prediction: Prediction;\n /** This side is the side whose detection the cell measures. Without it, the side is the client. */\n readonly observer?: Observer;\n /** These are the settings of the target. The harness sets them in each environment before a client connects. */\n readonly targetSettings?: Readonly<Record<string, string>>;\n /** These are the sysctls of the network namespace of the target, for a cell on the server side: its tcp_retries2. */\n readonly targetSysctls?: Readonly<Record<string, string>>;\n /** This topology puts hops between the client and the target. Without it, the client and the target are a direct pair. */\n readonly topology?: Topology;\n /** This place is where the fault goes, when it is not the peer of the observer: for example one side of a hop. */\n readonly faultAt?: FaultPlace;\n /** This time is how long an in-flight client stays idle after the fault holds, before it sends its request. */\n readonly idleBefore?: Quantity;\n /** This toxic is the toxic of a `toxic` fault, which the Toxiproxy hop gets. */\n readonly toxic?: Toxic;\n /** This image, or this client of the harness, replaces the TCP client of the harness for each run. An HTTP client is one. */\n readonly client?: ClientImage | Client;\n /**\n * This name is the proxy hop whose own time a calibration cell measures. The time of a request\n * (`up`) starts at its arrival on the socket of the proxy toward the client. It stops at the\n * write on the socket toward the target. The time of its reply (`down`) goes the other way.\n *\n * For a group cell, `sentinels` makes the prober watch the namespace of each Sentinel too. Each\n * stamp of the observer then gets the write of its Sentinel that it read (`lags`).\n */\n readonly calibrates?: string;\n /**\n * This group replaces the target: a replicated group under its Sentinels. The client is then\n * ioredis in Sentinel mode, and it writes a stream of sequence numbers. `target` stays for the\n * record, and the group does not read it.\n */\n readonly group?: Group;\n /**\n * This replica set replaces the target: a replicated group whose members elect their own\n * primary. The client is then the MongoDB driver, and it writes a stream of sequence numbers.\n * `target` stays for the record, and the set does not read it.\n */\n readonly replicaSet?: ReplicaSet;\n /**\n * This time is how long a fault on a group holds after its commit. The injector then lifts it at\n * that instant, on the clock of the VM. On a direct pair, a frozen target thaws after it.\n */\n readonly hold?: Quantity;\n /** This flag makes a planned failover coordinated: `SENTINEL FAILOVER <group> COORDINATED`. */\n readonly coordinated?: boolean;\n /**\n * These are options of the driver over its defaults: of ioredis for the client of a group, or of\n * the MongoDB driver for its client. A cell at the defaults has none.\n */\n readonly driverOptions?: Readonly<Record<string, unknown>>;\n /** This write concern is the w of each write of the MongoDB client. Without it, it is majority. */\n readonly writeConcern?: \"majority\" | 1;\n /** This flag makes the fault hold before the client starts. The client then meets it at its first selection. */\n readonly before?: boolean;\n /** This count is the number of runs of the cell (11), across new environments (two at least). */\n readonly runs: number;\n readonly environments: number;\n /**\n * This count is the number of environments at the same time, each with its own prober. Without\n * it, the count is one. The calibration does its reference cells at 1, 4 and 8, to find how\n * many environments the machine holds before its noise increases.\n */\n readonly concurrency?: number;\n}\n\n/** A hop place is a hop of a topology, with the side that the rules of a fault face. */\nexport interface HopPlace {\n readonly hop: string;\n readonly toward: \"client\" | \"target\";\n}\n\n/**\n * A fault place is the namespace that gets the rules of a fault. \"target\" and \"client\" are the two\n * ends. A hop place is a hop, with the side that the rules face. That side is the next hop toward\n * the client, or the next hop toward the target. The rules of a proxy act on its sockets. The\n * rules of a router act on what it forwards, by its device toward that side: the FORWARD chain.\n *\n * A group place is a part of a group, by its role.\n */\nexport type FaultPlace = \"target\" | \"client\" | HopPlace | GroupPlace;\n\n/** This function tells if a fault place is a hop of a topology. */\nexport const isHopPlace = (place: FaultPlace | undefined): place is HopPlace =>\n typeof place === \"object\" && \"hop\" in place;\n\n/** 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. */\nexport interface MiddleboxEvent {\n readonly ev: string;\n readonly t: number;\n readonly [field: string]: unknown;\n}\n\n/** A probe event is one line that the client wrote. `t` is CLOCK_MONOTONIC in nanoseconds: an instant, not an amount. */\nexport interface ProbeEvent {\n readonly ev: string;\n readonly t: number;\n readonly [field: string]: unknown;\n}\n\n/** A run is one run of a cell, as the harness records it. */\nexport interface Run {\n readonly environment: number;\n readonly index: number;\n /**\n * This flag marks the warm-up of an environment. It occurs before the counted runs, and the\n * verdict does not count it. On a loaded machine, a new environment is sometimes slow at first.\n */\n readonly warmup?: true;\n readonly events: readonly ProbeEvent[];\n /** This bracket is the time of the injector around the commit of the fault, on the same clock. It is null for a freeze. */\n readonly fault: { readonly t0: number; readonly t1: number } | null;\n /** This bracket is the time around the commit that stopped a stall. */\n readonly heal?: { readonly t0: number; readonly t1: number } | null;\n /** These are the records of the prober from the start to the end of the run, in the two namespaces. */\n readonly kernel: readonly KernelRecord[];\n /** This inode identifies the network namespace of the client, thus the records of the client. */\n readonly netns: number;\n /** This inode identifies the namespace of the target, where the records of its server socket are. */\n readonly targetNetns: number;\n /**\n * This count is the number of records that the prober lost in the run. One lost record makes\n * the run void. Null also makes the run void: no clock line came after the run to tell the count.\n */\n readonly lost: number | null;\n /** These are the addresses and the namespaces of a topology, which the measurement of a run reads. */\n readonly topology?: RunTopology;\n /** These are the lines of the routers of a topology from the start to the end of the run. */\n readonly middlebox?: readonly MiddleboxEvent[];\n /** This record is the group of a group cell: its parts, and what it did in the run. */\n readonly group?: RunGroup;\n /** These are the lines of a target that writes them (`Target.lines`), from the start to the end of the run. */\n readonly server?: readonly ProbeEvent[];\n}\n\n/** A Sentinel event is one line of the observer of a group: an event that a Sentinel published, on the clock of the VM. */\nexport interface SentinelEvent {\n readonly ev: string;\n readonly t: number;\n /** This text is the address and port of the Sentinel. */\n readonly sentinel?: string;\n /** This name is the event, for example `+switch-master`. */\n readonly event?: string;\n /** This text is the detail of the event, as the Sentinel wrote it. */\n readonly detail?: string;\n readonly [field: string]: unknown;\n}\n\n/** A part is one member or Sentinel of a group, as a run found it: its address and its network namespace. */\nexport interface RunPart {\n readonly ip: string;\n readonly netns: number;\n}\n\n/** A run group is the group of a run: its parts, the primary before and after, the timeline, and the writes that it kept. */\nexport interface RunGroup {\n readonly members: Readonly<Record<string, RunPart>>;\n readonly sentinels: Readonly<Record<string, RunPart>>;\n /** This name is the member that was the primary at the start of the run. */\n readonly primary: string;\n /** This name is the part that got the fault, or null for a fault on the whole group. */\n readonly at: string | null;\n /**\n * These are the lines of the timeline from the start to the end of the run. For a group under\n * Sentinels, they are the lines of the observer of the Sentinels. For a replica set, they are\n * the log lines of the members (`ev` \"mongod\"), with their times on the monotonic clock.\n */\n readonly timeline: readonly SentinelEvent[];\n /** This name is the member that was the primary when the group converged after the run, or null when it did not converge. */\n readonly after: string | null;\n /** These are the sequence numbers that the group holds after the run, when it converged, or null. */\n readonly holds: readonly number[] | null;\n}\n\n/**\n * A run topology tells where each socket of a run is, in a topology:\n * - `targetPeer` is the address of the peer of the target, as the target sees it: the address of\n * the last hop.\n * - `hops` are the network namespaces of the hops, by their names, with the address of the peer of\n * each hop on each side.\n */\nexport interface RunTopology {\n readonly targetPeer: string;\n readonly hops: Readonly<\n Record<string, { readonly netns: number; readonly client: string; readonly target: string }>\n >;\n}\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Prediction, Stage } from \"./cell.ts\";\nimport type { Measured, Reordering } from \"./observe.ts\";\n\n/** This function gives the words of the outcome of a second request: its connection, a retry, and its end. */\nexport const requestWords = (outcome: {\n readonly connection: \"reused\" | \"new\";\n readonly retried: boolean;\n readonly error: string | null;\n readonly pending?: boolean | undefined;\n}): string =>\n `${outcome.connection}${outcome.retried ? \", a retry\" : \"\"}, ${outcome.pending === true ? \"pending\" : (outcome.error ?? \"a reply\")}`;\n\n/**\n * This amount is the noise of the pipeline itself, δ_abs, as the calibration measured it. It is\n * the 99.9th percentile of the whole latency of a reset, at the concurrency cap or less. Its\n * minimum is one jiffy. On 2026-10-12 the worst of 60 resets at 1 and 4 environments took\n * 1.141 ms, thus the minimum sets it to 4 ms. The calibration occurs again after each change to\n * the harness, the kernel or Docker.\n */\nexport const NOISE: Quantity = quantity(4, \"ms\");\n/** This fraction, δ_rel, is how far the median of a cell can be outside the prediction and stay equivalent. */\nexport const RELATIVE = 0.05;\n/** These are the run counts of a cell: 11, then 22 and 44 while its median crosses the edge of the band. */\nexport const STEPS = [11, 22, 44] as const;\n\n/**\n * A decision is the result of a verdict. A cell whose outcome is a distribution has no band, and\n * its decision is `measured`: a record, not a test.\n */\nexport type Decision = \"holds\" | \"fails\" | \"extend\" | \"inconclusive\" | \"measured\";\n\n/** An interval is a range of times. */\nexport interface Interval {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/** A spread is the distribution of a set of times: its count, its median and its upper quantiles by nearest rank. */\nexport interface Spread {\n readonly count: number;\n readonly median: Quantity;\n readonly p99: Quantity;\n readonly p999: Quantity;\n readonly max: Quantity;\n}\n\n/** A verdict is the judgement of a cell on its runs. */\nexport interface Verdict {\n readonly decision: Decision;\n readonly counted: number;\n readonly voided: number;\n /** This band is where each run must land: [T_lo − δ_abs, T_hi + δ_abs]. It is null for a silent prediction. */\n readonly band: Interval | null;\n /** These are the runs outside the band, and the reason for each. Each is a finding. */\n readonly outside: ReadonlyArray<{ readonly run: string; readonly why: string }>;\n readonly median: Quantity | null;\n /** This interval is the order-statistic interval of the median at 95 %, or the whole range for fewer than six runs. */\n readonly medianInterval: Interval | null;\n /** This record is the sum of what reordering did in each counted run of a measurement-only cell. */\n readonly reordering?: Reordering;\n /** This record is the time of the proxy of a calibration cell, in each direction. */\n readonly proxy?: { readonly hop: string; readonly up: Spread | null; readonly down: Spread | null };\n /** This record is what the group of a group cell did over the counted runs: its stages, and its writes. */\n readonly group?: GroupVerdict;\n /**\n * These are the outcomes of the second request of an HTTP client over the counted runs, each\n * with its count: for example \"reused, a retry, a reply\". An outcome other than the predicted\n * one is a finding of its run.\n */\n readonly requests?: Readonly<Record<string, number>>;\n readonly why: string;\n}\n\n/**\n * A stage verdict is the test of one stage of the timeline of a group against its band. The band\n * is [T_lo − δ_abs, T_hi + the error of the stamps + δ_abs]. A run that did not announce the stage\n * is outside too.\n */\nexport interface StageVerdict {\n readonly stage: Stage;\n readonly band: Interval;\n readonly median: Quantity | null;\n readonly outside: ReadonlyArray<{ readonly run: string; readonly why: string }>;\n readonly decision: \"holds\" | \"fails\";\n}\n\n/**\n * A group verdict sums what the group of a group cell did in its counted runs:\n * - `stages` tests each stage that the prediction gives a band.\n * - `medians` is the median of each stage that a run announced, from the fault.\n * - `lost` counts the writes that a primary accepted and the group did not keep, in each run.\n * - `old` counts the writes that the old primary accepted after the fault, in each run.\n * - `unknown` counts the writes whose outcome the client did not see, and how many the group kept.\n * - `errors` are the errors on the way: the runs that met each, and the median of its first time.\n * - `lags` is the spread of the delays of the stamps, for a calibration.\n */\nexport interface GroupVerdict {\n readonly stages: readonly StageVerdict[];\n readonly medians: Partial<Record<Stage, Quantity>>;\n readonly lost: { readonly runs: number; readonly total: number; readonly median: number | null; readonly max: number | null };\n readonly old: { readonly median: number | null; readonly max: number | null };\n readonly unknown: { readonly total: number; readonly held: number };\n readonly errors: ReadonlyArray<{ readonly name: string; readonly runs: number; readonly first: Quantity }>;\n readonly lags?: Spread | null;\n}\n\nconst s = (value: number): Quantity => quantity(Math.round(value * 1e6) / 1e6, \"s\");\nconst name = (run: Measured): string => `env ${run.environment} run ${run.index}`;\n\n/** This function gives the value at quantile p of sorted values, by nearest rank: x_(⌈p·n⌉). */\nexport const quantileOf = (sorted: readonly number[], p: number): number =>\n sorted[Math.min(sorted.length, Math.max(1, Math.ceil(p * sorted.length))) - 1]!;\n\n/** This function gives the spread of a set of times, or null for none. */\nexport const spreadOf = (times: readonly Quantity[]): Spread | null => {\n if (times.length === 0) return null;\n const sorted = times.map((time) => valueIn(time, \"s\")).sort((a, b) => a - b);\n return {\n count: sorted.length,\n median: s(middle(sorted)),\n p99: s(quantileOf(sorted, 0.99)),\n p999: s(quantileOf(sorted, 0.999)),\n max: s(sorted.at(-1)!),\n };\n};\n\n/** This function adds the reordering records of runs: the counts add, and each largest value is the largest of all. */\nconst reorderingTotal = (runs: readonly Measured[]): Reordering | undefined => {\n const each = runs.flatMap((run) => (run.reordering === undefined ? [] : [run.reordering]));\n if (each.length === 0) return undefined;\n const sum = (key: \"outOfOrder\" | \"duplicates\" | \"fast\") => each.reduce((total, one) => total + one[key], 0);\n const add = (key: \"ooo\" | \"dsacks\" | \"seen\") =>\n each.some((one) => one[key] !== null) ? each.reduce((total, one) => total + (one[key] ?? 0), 0) : null;\n const top = (key: \"sacked\" | \"lost\" | \"degree\") =>\n each.some((one) => one[key] !== null) ? Math.max(...each.map((one) => one[key] ?? 0)) : null;\n return {\n outOfOrder: sum(\"outOfOrder\"),\n duplicates: sum(\"duplicates\"),\n ooo: add(\"ooo\"),\n sacked: top(\"sacked\"),\n lost: top(\"lost\"),\n dsacks: add(\"dsacks\"),\n fast: sum(\"fast\"),\n degree: top(\"degree\"),\n seen: add(\"seen\"),\n };\n};\n\n/** This function says a reordering record in words. */\nconst reorderingWords = (total: Reordering): string =>\n [\n `${total.outOfOrder} arrivals out of order and ${total.duplicates} duplicate arrivals at the client`,\n ...(total.ooo === null ? [] : [`its kernel counted ${total.ooo} segments out of order`]),\n ...(total.sacked === null ? [] : [`the server held up to ${total.sacked} segments SACKed`]),\n ...(total.dsacks === null ? [] : [`got ${total.dsacks} DSACKs`]),\n `sent ${total.fast} fast retransmissions`,\n ...(total.seen === null ? [] : [`saw ${total.seen} reorderings`]),\n ...(total.degree === null ? [] : [`and its reordering degree went to ${total.degree}`]),\n ].join(\", \");\n\n/** This function says a spread in words, in milliseconds. */\nconst spreadWords = (spread: Spread | null): string => {\n if (spread === null) return \"no time\";\n const ms = (time: Quantity) => Math.round(valueIn(time, \"s\") * 1e6) / 1e3;\n return `median ${ms(spread.median)} ms, p99 ${ms(spread.p99)} ms, p99.9 ${ms(spread.p999)} ms, max ${ms(spread.max)} ms of ${spread.count}`;\n};\n\n/** This function gives P(X ≤ j) for X ~ Binomial(n, 1/2), exactly. */\nconst below = (n: number, j: number): number => {\n let term = 1;\n let sum = 0;\n for (let i = 0; i <= j; i += 1) {\n sum += term;\n term = (term * (n - i)) / (i + 1);\n }\n return sum / 2 ** n;\n};\n\n/**\n * This function gives the distribution-free interval of a median: [x_(k), x_(n+1−k)], for the\n * largest k whose two tails each hold 2.5 % or less. For eleven runs, it gives [x_(2), x_(10)],\n * at 98.8 %.\n */\nexport const medianInterval = (sorted: readonly number[]): [number, number] => {\n const n = sorted.length;\n let k = 0;\n while (k + 1 <= n / 2 && below(n, k) <= 0.025) k += 1;\n return k === 0 ? [sorted[0]!, sorted[n - 1]!] : [sorted[k - 1]!, sorted[n - k]!];\n};\n\nconst middle = (sorted: readonly number[]): number => {\n const half = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 1 ? sorted[half]! : (sorted[half - 1]! + sorted[half]!) / 2;\n};\n\n/**\n * This function gives the verdict of a cell, from two tests:\n * - Containment: each run lands in [T_lo − δ_abs, T_hi + δ_abs]. A run outside is a finding.\n * - Median equivalence: the interval of the median is in the band, made wider by δ_rel. An\n * interval outside the band fails. An interval across its edge extends the cell to 22 runs, then\n * 44, and then the verdict is inconclusive.\n *\n * A silent prediction holds only if no run detected the fault in its window. After a stall, the\n * return of the reply gets the same test as a detection, by its interval, and a detection is a\n * finding. The distribution test (a DKW band) needs a predicted distribution from a simulator,\n * which does not exist at this time.\n */\nexport const verdictOf = (prediction: Prediction, runs: readonly Measured[]): Verdict => {\n const band = verdictOfBand(prediction, runs);\n const counted = runs.filter((run) => run.void === null);\n const verdict = counted.some((run) => run.request !== undefined) ? withRequests(band, counted) : band;\n if (prediction.group === undefined && !counted.some((run) => run.group !== undefined)) return verdict;\n return { ...verdict, group: groupVerdictOf(prediction, counted) };\n};\n\n/**\n * This function adds the outcomes of the second request of an HTTP client to a verdict. A cell\n * whose outcome is a distribution gets them in its reason too.\n */\nconst withRequests = (verdict: Verdict, counted: readonly Measured[]): Verdict => {\n const requests: Record<string, number> = {};\n for (const run of counted) {\n const words = run.request === null || run.request === undefined ? \"no second request\" : requestWords(run.request);\n requests[words] = (requests[words] ?? 0) + 1;\n }\n const listed = Object.entries(requests)\n .map(([words, n]) => `${n} ${words}`)\n .join(\"; \");\n return {\n ...verdict,\n requests,\n why: verdict.decision === \"measured\" ? `${verdict.why} Second requests: ${listed}.` : verdict.why,\n };\n};\n\n/** This function gives the median of some counts, or null for none. */\nconst medianCount = (values: readonly number[]): number | null =>\n values.length === 0 ? null : middle([...values].sort((a, b) => a - b));\n\n/** This function sums what the group of a group cell did in its counted runs, and tests each stage that has a band. */\nexport const groupVerdictOf = (prediction: Prediction, counted: readonly Measured[]): GroupVerdict => {\n const noise = valueIn(NOISE, \"s\");\n const stamps = prediction.group?.stamps === undefined ? 0 : valueIn(prediction.group.stamps, \"s\");\n const stages = (prediction.group?.stages ?? []).map((band): StageVerdict => {\n const interval = { low: s(valueIn(band.low, \"s\") - noise), high: s(valueIn(band.high, \"s\") + stamps + noise) };\n const outside: Array<{ run: string; why: string }> = [];\n const values: number[] = [];\n for (const run of counted) {\n const at = run.group?.stages[band.stage];\n if (at === undefined) {\n // a stage of a Sentinel starts with +: a Sentinel announces it. The members and the client of a replica set mark the others\n const who = band.stage.startsWith(\"+\") ? \"no Sentinel announced\" : \"no member or client marked\";\n outside.push({ run: name(run), why: `${who} ${band.stage}` });\n continue;\n }\n const value = valueIn(at, \"s\");\n values.push(value);\n if (value < valueIn(interval.low, \"s\") || value > valueIn(interval.high, \"s\"))\n outside.push({ run: name(run), why: `${band.stage} at ${value} s is outside [${interval.low.value}, ${interval.high.value}] s` });\n }\n values.sort((a, b) => a - b);\n return {\n stage: band.stage,\n band: interval,\n median: values.length === 0 ? null : s(middle(values)),\n outside,\n decision: outside.length === 0 && values.length > 0 ? \"holds\" : \"fails\",\n };\n });\n const medians: Partial<Record<Stage, Quantity>> = {};\n const seen = new Set(counted.flatMap((run) => Object.keys(run.group?.stages ?? {}) as Stage[]));\n for (const stage of seen) {\n const values = counted.flatMap((run) => {\n const at = run.group?.stages[stage];\n return at === undefined ? [] : [valueIn(at, \"s\")];\n });\n medians[stage] = s(middle(values.sort((a, b) => a - b)));\n }\n const lostCounts = counted.flatMap((run) => (run.group?.writes.lost === null || run.group === undefined ? [] : [run.group.writes.lost.length]));\n const oldCounts = counted.flatMap((run) => (run.group === undefined ? [] : [run.group.writes.old]));\n const unknown = counted.flatMap((run) => run.group?.writes.unknown ?? []);\n const errors = new Map<string, number[]>();\n for (const run of counted)\n for (const error of run.group?.errors ?? []) errors.set(error.name, [...(errors.get(error.name) ?? []), valueIn(error.first, \"s\")]);\n const lags = counted.flatMap((run) => run.group?.lags ?? []);\n return {\n stages,\n medians,\n lost: {\n runs: lostCounts.length,\n total: lostCounts.reduce((sum, n) => sum + n, 0),\n median: medianCount(lostCounts),\n max: lostCounts.length === 0 ? null : Math.max(...lostCounts),\n },\n old: { median: medianCount(oldCounts), max: oldCounts.length === 0 ? null : Math.max(...oldCounts) },\n unknown: { total: unknown.length, held: unknown.filter((each) => each.held === true).length },\n errors: [...errors].map(([error, firsts]) => ({ name: error, runs: firsts.length, first: s(middle(firsts.sort((a, b) => a - b))) })),\n ...(lags.length === 0 ? {} : { lags: spreadOf(lags) }),\n };\n};\n\n/** This function gives the verdict of the band of a cell: its containment and the equivalence of its median. */\nconst verdictOfBand = (prediction: Prediction, runs: readonly Measured[]): Verdict => {\n const counted = runs.filter((run) => run.void === null);\n const voided = runs.length - counted.length;\n const outside = counted\n .filter((run) => run.finding !== null)\n .map((run) => ({ run: name(run), why: run.finding! }));\n const empty = { counted: counted.length, voided, median: null, medianInterval: null };\n if (counted.length === 0)\n return { ...empty, decision: \"inconclusive\", band: null, outside, why: \"no run counted\" };\n\n if (prediction.detects === null) {\n // a calibration of a proxy: its time in each direction, over all the requests of the runs\n const crossed = counted.flatMap((run) => (run.crossings === undefined ? [] : [run.crossings]));\n if (crossed.length > 0) {\n const hop = crossed[0]!.hop;\n const up = spreadOf(crossed.flatMap((each) => each.up));\n const down = spreadOf(crossed.flatMap((each) => each.down));\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: up?.median ?? null,\n proxy: { hop, up, down },\n why: `${counted.length} runs measured ${hop}: toward the target ${spreadWords(up)}; toward the client ${spreadWords(down)}. ${prediction.why}`,\n };\n }\n // a measurement-only cell with a reference: the outcomes, and the times from the reference\n if (prediction.from !== undefined) {\n const times = counted\n .flatMap((run) => (run.elapsed === null ? [] : [valueIn(run.elapsed, \"s\")]))\n .sort((a, b) => a - b);\n const kinds = new Map<string, number>();\n for (const run of counted) {\n const kind = run.outcome.kind === \"error\" ? run.outcome.errno : run.outcome.kind;\n kinds.set(kind, (kinds.get(kind) ?? 0) + 1);\n }\n const outcomes = [...kinds].map(([kind, n]) => `${n} ${kind}`).join(\", \");\n const timed =\n times.length === 0\n ? \"no time\"\n : `median ${s(middle(times)).value} s from ${prediction.from}, range [${s(times[0]!).value}, ${s(times.at(-1)!).value}] s`;\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: times.length === 0 ? null : s(middle(times)),\n why: `${counted.length} runs measured: ${outcomes}; ${timed}. ${prediction.why}`,\n };\n }\n const samples = counted\n .flatMap((run) => (run.samples ?? []).map((sample) => valueIn(sample, \"s\")))\n .sort((a, b) => a - b);\n const retransmissions = counted.reduce((sum, run) => sum + run.retransmissions, 0);\n const ended = counted.filter((run) => run.outcome.kind === \"error\" || run.outcome.kind === \"eof\");\n const reordering = reorderingTotal(counted);\n // a run in which nothing came out of order, twice or SACKed says nothing about reordering\n const anything =\n reordering !== undefined &&\n [reordering.outOfOrder, reordering.duplicates, reordering.ooo, reordering.sacked, reordering.dsacks, reordering.fast, reordering.seen].some(\n (count) => (count ?? 0) > 0,\n );\n const reordered = anything ? ` Reordering: ${reorderingWords(reordering)}.` : \"\";\n const why =\n samples.length === 0\n ? `${counted.length} runs measured, with no reply: ${prediction.why}`\n : `${counted.length} runs, ${samples.length} replies: median ${s(middle(samples)).value} s, p99 ${s(quantileOf(samples, 0.99)).value} s, slowest ${s(samples.at(-1)!).value} s, ${retransmissions} retransmissions, ${ended.length} connections ended.${reordered} ${prediction.why}`;\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: samples.length === 0 ? null : s(middle(samples)),\n ...(reordering === undefined ? {} : { reordering }),\n why,\n };\n }\n\n if (!prediction.detects && prediction.recovers === undefined && prediction.replies === undefined) {\n const decision = outside.length === 0 ? \"holds\" : \"fails\";\n const why =\n decision === \"holds\"\n ? `nothing detected it in ${counted.length} runs of ${valueIn(prediction.window, \"s\")} s`\n : `${outside.length} of ${counted.length} runs detected it`;\n return { ...empty, decision, band: null, outside, why };\n }\n\n // the interval of a detection, of the return of the reply after a stall, or of a reply in its band\n const timed = prediction.detects ? prediction : (prediction.recovers ?? prediction.replies)!;\n const noise = valueIn(NOISE, \"s\");\n const low = valueIn(timed.low, \"s\");\n const high = valueIn(timed.high, \"s\");\n const band = { low: s(low - noise), high: s(high + noise) };\n const values: number[] = [];\n for (const run of counted) {\n if (run.elapsed === null) continue;\n const value = valueIn(run.elapsed, \"s\");\n values.push(value);\n if (value < low - noise || value > high + noise)\n outside.push({\n run: name(run),\n why: `${value} s is outside [${band.low.value}, ${band.high.value}] s`,\n });\n }\n values.sort((a, b) => a - b);\n if (values.length === 0)\n return { ...empty, decision: \"fails\", band, outside, why: \"no run measured its time\" };\n const [from, to] = medianInterval(values);\n const median = s(middle(values));\n const interval = { low: s(from), high: s(to) };\n const wide = { low: low * (1 - RELATIVE), high: high * (1 + RELATIVE) };\n const inside = from >= wide.low && to <= wide.high;\n const disjoint = to < wide.low || from > wide.high;\n // a run can be outside for its time and for a finding: it counts once\n const off = new Set(outside.map((each) => each.run)).size;\n const counts = `${counted.length - off} of ${counted.length} runs inside [${band.low.value}, ${band.high.value}] s`;\n const centre = `median ${median.value} s, its interval [${interval.low.value}, ${interval.high.value}] s`;\n const result = {\n counted: counted.length,\n voided,\n band,\n outside,\n median,\n medianInterval: interval,\n };\n if (outside.length > 0 || disjoint)\n return { ...result, decision: \"fails\", why: `${counts}; ${centre}` };\n if (inside) return { ...result, decision: \"holds\", why: `${counts}; ${centre}` };\n const decision = counted.length < STEPS[2] ? \"extend\" : \"inconclusive\";\n return { ...result, decision, why: `${counts}; ${centre} straddles the band` };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, DriverBand, KernelRecord, LineMatch, ProbeEvent, Reference, Run } from \"./cell.ts\";\nimport type { Measured, Outcome } from \"./observe.ts\";\nimport { NOISE } from \"./verdict.ts\";\n\n/**\n * This module measures a run of a cell whose client is a driver with more than one socket, for\n * example the MongoDB driver. Its monitor, its pool and its round-trip pinger each have their own\n * socket, and the client names each socket by its role. A band counts from an event of one socket,\n * as the kernel records it. It ends at a line of the client, or at the report of the kernel on\n * that socket.\n */\n\n/** A driver socket is one socket of the driver in a run: its role, its local address, its server, and when it opened and ended. */\nexport interface DriverSocket {\n readonly role: string;\n readonly local: string;\n readonly server: string;\n readonly opened: number;\n readonly ended: number | null;\n}\n\n/** A driver step time is the time of one later band of a run. It is null when its line did not come. */\nexport interface DriverStepTime {\n readonly what: string;\n readonly name: string | null;\n readonly elapsed: Quantity | null;\n}\n\n/** These are the errno numbers that the kernel reports to a socket, by the names that predictions use. */\nconst ERRNO: Readonly<Record<number, string>> = {\n 32: \"EPIPE\",\n 101: \"ENETUNREACH\",\n 103: \"ECONNABORTED\",\n 104: \"ECONNRESET\",\n 110: \"ETIMEDOUT\",\n 111: \"ECONNREFUSED\",\n 113: \"EHOSTUNREACH\",\n};\n\nconst between = (from: number, to: number): Quantity => quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\nconst round = (seconds: number): number => Math.round(seconds * 1e6) / 1e6;\n\n/** This function gives the value of a field of a line by its name. A dotted name is a field in a field. */\nexport const fieldOf = (line: unknown, path: string): unknown =>\n path.split(\".\").reduce<unknown>((value, key) => (value !== null && typeof value === \"object\" ? (value as Record<string, unknown>)[key] : undefined), line);\n\n/** This function tells if a line of the client matches a line match: its `ev`, and each field that the match names. */\nexport const matches = (line: ProbeEvent, match: LineMatch): boolean =>\n line.ev === match.ev && Object.entries(match.where ?? {}).every(([path, value]) => fieldOf(line, path) === value);\n\n/** This function says a line match in words, for a finding. */\nexport const wordsOf = (match: LineMatch): string => {\n const where = Object.entries(match.where ?? {}).map(([path, value]) => `${path} ${String(value)}`);\n return where.length === 0 ? `a ${match.ev} line` : `a ${match.ev} line with ${where.join(\", \")}`;\n};\n\n/** This function gives the sockets of the driver in a run, from the `socket` and `socket-end` lines of the client. */\nexport const socketsOf = (events: readonly ProbeEvent[]): DriverSocket[] =>\n events\n .filter((event) => event.ev === \"socket\")\n .map((event) => {\n const local = String(event[\"local\"]);\n const end = events.find((each) => each.ev === \"socket-end\" && each[\"local\"] === local && each.t >= event.t);\n return { role: String(event[\"role\"]), local, server: String(event[\"server\"]), opened: event.t, ended: end?.t ?? null };\n });\n\n/**\n * This function finds the socket of a role at an instant. The pool's socket is the first one, which\n * the client names in its `connected` line. Each other role has the latest socket that opened at\n * the instant or before it.\n */\nexport const socketAt = (events: readonly ProbeEvent[], role: string, at: number): DriverSocket | undefined => {\n const sockets = socketsOf(events);\n if (role === \"pool\") {\n const local = events.find((event) => event.ev === \"connected\")?.[\"local\"];\n return sockets.find((socket) => socket.local === local);\n }\n return sockets.filter((socket) => socket.role === role && socket.opened <= at).at(-1);\n};\n\n/** A kernel place is the namespace and the cookie of a socket, and its records in the run. */\ninterface KernelPlace {\n readonly netns: number;\n readonly cookie: number;\n readonly records: readonly KernelRecord[];\n}\n\nconst placeOf = (run: Run, netns: number, find: (record: KernelRecord) => boolean): KernelPlace | undefined => {\n const cookie = run.kernel.find((record) => record.netns === netns && find(record))?.cookie;\n if (cookie === undefined) return undefined;\n return { netns, cookie, records: run.kernel.filter((record) => record.netns === netns && record.cookie === cookie) };\n};\n\nconst portOf = (address: string): string => address.split(\":\").at(-1) ?? \"\";\n\n/**\n * This function gives the socket of the target for a socket of the client. In a direct pair, its\n * peer is the address of the client socket. Behind a router, its peer is the address of the router\n * with the port of the client socket. The router keeps the port when it is free.\n */\nconst peerPlace = (run: Run, local: string): KernelPlace | undefined => {\n const peer = run.topology === undefined ? local : `${run.topology.targetPeer}:${portOf(local)}`;\n return placeOf(run, run.targetNetns, (record) => record.dst === peer);\n};\n\n/** This function gives the kernel place of a socket of a role at an instant. The role `server` is a socket of the target. */\nconst kernelOf = (run: Run, role: string, at: number): { socket: DriverSocket | undefined; place: KernelPlace | undefined } => {\n if (role === \"server\") {\n const pool = socketAt(run.events, \"pool\", at);\n return { socket: pool, place: pool === undefined ? undefined : peerPlace(run, pool.local) };\n }\n const socket = socketAt(run.events, role, at);\n return { socket, place: socket === undefined ? undefined : placeOf(run, run.netns, (record) => record.src === socket.local) };\n};\n\nconst newState = (record: KernelRecord): number | undefined => (record as { readonly new?: number }).new;\nconst oldState = (record: KernelRecord): number | undefined => (record as { readonly old?: number }).old;\n\n/** This function gives the instant that a run starts to look for its outcome. It is the fault, the reply before it, or the start of the client. */\nconst outsetOf = (run: Run): number => {\n const ready = run.events.find((event) => event.ev === \"ready\");\n return run.fault?.t0 ?? ready?.t ?? run.events[0]?.t ?? 0;\n};\n\n/**\n * This function gives the instant of a reference in a run, on a socket of the driver:\n * - `last-received`: the last segment that the socket received before the fault held. A band of\n * the kernel counts from the last segment of any kind. A band that a line ends is a timer of\n * the driver. It counts from the last segment with data that the socket received or sent.\n * - `request-sent`: the send of the operation into the fault, the second `sent` line.\n * - `first-retransmission`: the first retransmission of the RTO timer on the socket.\n * - `peer-closed`: the change of state of the socket of the target, when its process dies.\n * - `fault`: the start of the commit of the fault. `client-start`: the `connecting` line.\n */\nexport const referenceOf = (run: Run, from: Reference, role: string, data: boolean): number | undefined => {\n const ready = run.events.find((event) => event.ev === \"ready\");\n const before = run.fault?.t1 ?? ready?.t ?? Infinity;\n if (from === \"fault\") return run.fault?.t0;\n if (from === \"client-start\") return (run.events.find((event) => event.ev === \"connecting\") ?? run.events[0])?.t;\n if (from === \"request-sent\") return run.events.filter((event) => event.ev === \"sent\")[1]?.t;\n const { socket, place } = kernelOf(run, role, before);\n if (place === undefined) return undefined;\n if (from === \"last-received\") {\n // the idle timer of a Node socket starts again at each read and at each write of data\n const counts = (record: KernelRecord) =>\n data ? (record.ev === \"rx\" || record.ev === \"tx\") && (record.seg?.len.value ?? 0) > 0 : record.ev === \"rx\";\n return place.records.filter((record) => record.mono <= before && counts(record)).at(-1)?.mono;\n }\n if (from === \"first-retransmission\") {\n const request = run.events.filter((event) => event.ev === \"sent\")[1]?.t ?? before;\n return place.records.find((record) => record.ev === \"retrans\" && record.why === \"rto\" && record.mono > request)?.mono;\n }\n // the close of the peer: the socket of the target leaves ESTABLISHED\n const peer = socket === undefined ? undefined : peerPlace(run, socket.local);\n return peer?.records.find((record) => record.ev === \"state\" && oldState(record) === 1)?.mono;\n};\n\n/** This function gives the first line of the client that matches, after an instant. */\nconst lineAfter = (events: readonly ProbeEvent[], match: LineMatch, after: number): ProbeEvent | undefined =>\n events.find((event) => event.t > after && matches(event, match));\n\n/** This function gives the name of the error of a line: the field that the match names, or `name`. */\nconst nameOf = (line: ProbeEvent, match: LineMatch): string => String(fieldOf(line, match.name ?? \"name\") ?? line.ev);\n\n/** This function gives the report of the kernel on a socket after an instant: an error, or the end of the file (CLOSE_WAIT). */\nconst reportOf = (place: KernelPlace, after: number): { at: number; outcome: Outcome } | undefined => {\n const report = place.records.find(\n (record) => record.mono > after && ((record.ev === \"error\" && (record.err ?? 0) !== 0) || (record.ev === \"state\" && newState(record) === 8)),\n );\n if (report === undefined) return undefined;\n return report.ev === \"error\"\n ? { at: report.mono, outcome: { kind: \"error\", errno: ERRNO[report.err!] ?? `E${report.err}` } }\n : { at: report.mono, outcome: { kind: \"eof\" } };\n};\n\n/** This function gives the latest instant of the bands of a driver prediction, in seconds from their references. */\nexport const longestOf = (cell: Cell): number => {\n const { prediction } = cell;\n if (prediction.detects === null) return valueIn(prediction.window, \"s\");\n const steps = (prediction.driver?.then ?? []).map((step) => valueIn(step.high, \"s\"));\n const own = prediction.detects ? valueIn(prediction.high, \"s\") : valueIn(prediction.window, \"s\");\n return Math.max(own, ...steps);\n};\n\n/**\n * This function tells if a run of a driver cell has its outcome, so that its wait can stop. For a\n * detection, the outcome is the end of its band and of each later band. A silent prediction waits\n * for its window.\n */\nexport const settled = (cell: Cell, run: Run): boolean => {\n const { prediction } = cell;\n if (prediction.detects !== true || prediction.driver === undefined) return false;\n const driver: DriverBand = prediction.driver;\n const after = outsetOf(run);\n const ended =\n driver.ends === undefined\n ? (() => {\n const { place } = kernelOf(run, driver.socket, run.fault?.t1 ?? after);\n return place !== undefined && reportOf(place, after) !== undefined;\n })()\n : lineAfter(run.events, driver.ends, after) !== undefined;\n return ended && (driver.then ?? []).every((step) => lineAfter(run.events, step.ends, after) !== undefined);\n};\n\n/**\n * This function examines the keepalive timer of a socket between two instants. A firing two\n * jiffies or more behind its wheel slot was delayed by the host, which stopped a vCPU. A firing\n * that found the socket held by its owner put itself off by HZ/20. Either way, the run measured\n * something other than the timer.\n */\nconst delayed = (place: KernelPlace, from: number, to: number): string | null => {\n const firings = place.records.filter((record) => record.ev === \"timer\" && record.what === \"keepalive\" && record.mono >= from && record.mono <= to);\n const late = firings.find((record) => (record.behind?.value ?? 0) >= 2);\n if (late !== undefined) return `its keepalive timer ran ${late.behind!.value} jiffies behind its wheel slot: the VM delayed it, not the kernel`;\n const held = firings.find((record) => record.owned === true);\n return held === undefined ? null : \"its keepalive timer found the socket held by its owner and put itself off by HZ/20: the run measured the owner, not the timer\";\n};\n\n/**\n * This function reads one run of a driver cell.\n * - The band of the cell counts from its reference on its socket. It ends at its line, or at the\n * report of the kernel on that socket.\n * - Each later band (`then`) gets the same test. A later band outside its band, or absent, is a finding.\n * - A line that the prediction names as quiet, after the fault and in its window, is a finding.\n * So is a flow of an open socket of the driver that a router forgot, for a silent prediction.\n * - A silent prediction can time a reply (`reply`), and the verdict tests it against `replies`.\n *\n * A run is void when the prober lost records, or when the client did not get its first reply. It\n * is also void when the operation went out before the fault held. A late lift of a hold and a\n * keepalive probe before the fault also make it void. So do a delayed timer and a round trip\n * above the assumption.\n */\nexport const measureDriver = (cell: Cell, run: Run): Measured => {\n const { prediction } = cell;\n if (prediction.detects === null || prediction.driver === undefined) throw new Error(`${cell.name} has no driver band`);\n const driver = prediction.driver;\n const { events } = run;\n const ready = events.find((event) => event.ev === \"ready\");\n const request = events.filter((event) => event.ev === \"sent\")[1];\n const after = outsetOf(run);\n const before = run.fault?.t1 ?? ready?.t ?? after;\n const { place } = kernelOf(run, driver.socket, before);\n const records = place?.records ?? [];\n const pool = kernelOf(run, \"pool\", before).place;\n const srtt = pool?.records.find((record) => record.tcp?.srtt !== undefined)?.tcp?.srtt;\n const rtt = srtt === undefined ? null : quantity(valueIn(srtt, \"us\") / 1000, \"ms\");\n const base = {\n environment: run.environment,\n index: run.index,\n probes: records.filter((record) => record.ev === \"probe\" && record.what === \"keepalive\").length,\n retransmissions: records.filter((record) => record.ev === \"retrans\").length,\n acks: request === undefined ? 0 : records.filter((record) => record.ev === \"rx\" && record.mono > request.t).length,\n rtt,\n };\n const nothing = { outcome: { kind: \"silent\" } as const, elapsed: null, socket: null, noticed: null, steps: [], finding: null };\n\n if (cell.before !== true && ready === undefined) return { ...base, ...nothing, void: \"the client never had its reply\" };\n if (run.lost === null) return { ...base, ...nothing, void: \"the prober wrote no clock line after the run, so nothing says what it dropped\" };\n if (run.lost > 0) return { ...base, ...nothing, void: `the prober dropped ${run.lost} records` };\n\n let voided: string | null = null;\n if (cell.probe.mode !== \"idle\" && cell.before !== true) {\n if (request === undefined) voided = \"the operation was never sent into the fault\";\n else if (run.fault !== null && run.fault.t1 > request.t) voided = \"the operation went out before the fault held\";\n }\n const keepalive = records.find((record) => record.ev === \"probe\" && record.what === \"keepalive\");\n if (voided === null && run.fault !== null && keepalive !== undefined && keepalive.mono < run.fault.t1)\n voided = \"a keepalive probe went out before the fault held\";\n if (voided === null && cell.hold !== undefined && run.fault !== null) {\n const heal = run.heal ?? null;\n if (heal === null) voided = \"the fault never lifted\";\n else {\n const late = (heal.t0 - run.fault.t0) / 1e9 - valueIn(cell.hold, \"s\");\n if (late > valueIn(NOISE, \"s\")) voided = `the lift came ${Math.round(late * 1e4) / 10} ms after its hold: the injector was late, not the client`;\n }\n }\n const assumed = prediction.rtt;\n if (voided === null && assumed !== undefined && rtt !== null && valueIn(rtt, \"ms\") > valueIn(assumed.high, \"ms\"))\n voided = `rtt-over-assumption: its first round trip was ${rtt.value} ms, above the ${valueIn(assumed.high, \"ms\")} ms that the prediction assumes`;\n\n const findings: string[] = [];\n const noise = valueIn(NOISE, \"s\");\n // the end of the window: the window of a silent prediction, or the end of the run\n const until = prediction.detects ? Infinity : after + valueIn(prediction.window, \"s\") * 1e9;\n for (const match of driver.quiet ?? []) {\n const line = events.find((event) => event.t > after && event.t <= until && matches(event, match));\n if (line !== undefined) findings.push(`${wordsOf(match)} came ${between(after, line.t).value} s after the fault, where none should`);\n }\n if (!prediction.detects && run.middlebox !== undefined) {\n // a router forgets a flow of a socket that is still open: the flow of a live connection is gone\n for (const socket of socketsOf(events)) {\n const forgot = run.middlebox.find(\n (line) =>\n line.ev === \"ct\" &&\n line[\"type\"] === \"DESTROY\" &&\n String(line[\"orig\"] ?? \"\").startsWith(`${socket.local}>`) &&\n line.t > socket.opened &&\n (socket.ended === null || line.t < socket.ended) &&\n line.t <= until,\n );\n if (forgot !== undefined) findings.push(`a router forgot the flow of the ${socket.role} socket ${between(after, forgot.t).value} s after the fault, where it should keep it`);\n }\n }\n const stepTimes: DriverStepTime[] = (driver.then ?? []).map((step) => {\n const line = lineAfter(events, step.ends, after);\n const from = referenceOf(run, step.from, step.socket ?? driver.socket, true);\n if (line === undefined || from === undefined) {\n findings.push(line === undefined ? `${step.what}: ${wordsOf(step.ends)} never came` : `${step.what}: its reference (${step.from}) never came`);\n return { what: step.what, name: line === undefined ? null : nameOf(line, step.ends), elapsed: null };\n }\n const elapsed = between(from, line.t);\n const name = nameOf(line, step.ends);\n if (step.name !== \"OK\" && name !== step.name) findings.push(`${step.what}: it ended with ${name}, not ${step.name}`);\n const [low, high] = [round(valueIn(step.low, \"s\") - noise), round(valueIn(step.high, \"s\") + noise)];\n if (elapsed.value < low || elapsed.value > high) findings.push(`${step.what}: ${elapsed.value} s is outside [${low}, ${high}] s`);\n return { what: step.what, name, elapsed };\n });\n const stepped = stepTimes.length === 0 ? {} : { driver: { steps: stepTimes } };\n\n if (!prediction.detects) {\n if (driver.reply === undefined || prediction.replies === undefined)\n return { ...base, ...nothing, ...stepped, void: voided, finding: findings[0] ?? null };\n const line = lineAfter(events, driver.reply.ends, after);\n const from = referenceOf(run, driver.reply.from, driver.socket, true);\n if (line === undefined || from === undefined)\n return { ...base, ...nothing, ...stepped, void: voided, finding: findings[0] ?? `the reply never came: ${wordsOf(driver.reply.ends)}` };\n return {\n ...base,\n ...nothing,\n ...stepped,\n outcome: { kind: \"answered\" },\n elapsed: between(from, line.t),\n void: voided,\n finding: findings[0] ?? null,\n };\n }\n\n // the band of the cell: its line, or the report of the kernel on its socket\n const line = driver.ends === undefined ? undefined : lineAfter(events, driver.ends, after);\n const report = driver.ends === undefined && place !== undefined ? reportOf(place, after) : undefined;\n if (driver.ends === undefined && place === undefined)\n return { ...base, ...nothing, ...stepped, void: voided ?? `the prober saw nothing of the ${driver.socket} socket`, finding: findings[0] ?? null };\n const ended = line?.t ?? report?.at;\n const outcome: Outcome =\n line !== undefined && driver.ends !== undefined ? { kind: \"error\", errno: nameOf(line, driver.ends) } : (report?.outcome ?? { kind: \"silent\" });\n if (ended === undefined)\n return { ...base, ...nothing, ...stepped, void: voided, finding: findings[0] ?? `nothing detected it: ${prediction.timer} never fired` };\n const reference = referenceOf(run, prediction.from, driver.socket, driver.ends !== undefined);\n if (reference === undefined)\n return { ...base, ...nothing, ...stepped, outcome, void: voided, finding: `it ended without its reference event (${prediction.from} on the ${driver.socket} socket)` };\n if (driver.ends === undefined && prediction.from === \"last-received\" && place !== undefined) voided ??= delayed(place, reference, ended);\n const name = outcome.kind === \"error\" ? outcome.errno : outcome.kind === \"eof\" ? \"EOF\" : \"silent\";\n if (prediction.errno !== \"OK\" && name !== prediction.errno) findings.unshift(`it ended with ${name}, not ${prediction.errno}`);\n // the client's own view: its first error line after the report of the kernel\n const seen = report === undefined ? undefined : events.find((event) => event.t >= report.at && (event.ev === \"error\" || event.ev === \"socket-error\"));\n return {\n ...base,\n ...stepped,\n outcome,\n elapsed: between(reference, ended),\n socket: seen === undefined ? null : between(reference, seen.t),\n noticed: report === undefined || seen === undefined ? null : between(report.at, seen.t),\n steps: (prediction.from === \"last-received\" ? records.filter((record) => record.ev === \"probe\" && record.what === \"keepalive\" && record.mono > reference) : []).map(\n (record) => between(reference, record.mono),\n ),\n void: voided,\n finding: findings[0] ?? null,\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, KernelRecord, ProbeEvent, ReplicaSetStage, Run, SentinelEvent, SentinelStage, Stage } from \"./cell.ts\";\nimport { socketsOf } from \"./driver.ts\";\nimport { NOISE } from \"./verdict.ts\";\n\n/**\n * This module measures a run of a group cell. It gives the first write that a primary accepted\n * after the fault, and the timeline of the group. The Sentinels of a group announce its timeline.\n * The logs of the members of a replica set and its client tell it. The module also gives what\n * became of each write.\n */\n\n/** These are the stages of a timeline under Sentinels, in their sequence after a fault. */\nexport const STAGES: readonly SentinelStage[] = [\n \"+sdown\",\n \"+odown\",\n \"+try-failover\",\n \"+elected-leader\",\n \"+selected-slave\",\n \"+promoted-slave\",\n \"+switch-master\",\n \"+convert-to-slave\",\n];\n\n/** An error on the way is one kind of error that the client met after the fault. It has its first time from the fault, and its count. */\nexport interface ErrorOnTheWay {\n readonly name: string;\n readonly first: Quantity;\n readonly count: number;\n}\n\n/**\n * A writes record tells what became of the writes of a run, by their sequence numbers:\n * - `old` counts the writes that the primary of the start of the run accepted after the fault.\n * - `lost` are the writes that a primary accepted and the group did not keep. They are the cost\n * of a split brain. It is null when the group did not converge after the run.\n * - `unknown` are the writes that the client sent and did not see accepted: an error, or no answer.\n * `held` tells if the group kept each one, or null when it did not converge.\n */\nexport interface Writes {\n readonly sent: number;\n readonly acked: number;\n readonly old: number;\n readonly lost: readonly number[] | null;\n readonly unknown: ReadonlyArray<{ readonly n: number; readonly held: boolean | null }>;\n}\n\n/** A group measurement is what one run of a group cell measured, beside its time. */\nexport interface GroupMeasured {\n /** These are the member that was the primary at the start, and the member of the first accepted write after the fault. */\n readonly from: string;\n readonly to: string | null;\n /** These are the times of each stage from the fault: the first announcement by any Sentinel. A stage that no Sentinel announced is absent. */\n readonly stages: Partial<Record<Stage, Quantity>>;\n readonly writes: Writes;\n readonly errors: readonly ErrorOnTheWay[];\n /** These are the delays of the stamps of the observer after the writes of the Sentinels, for a calibration. */\n readonly lags?: readonly Quantity[];\n}\n\nconst between = (from: number, to: number): Quantity => quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\n\n/** This function gives the member whose address and port is `at`, or null. */\nconst memberAt = (run: Run, at: unknown): string | null => {\n if (typeof at !== \"string\") return null;\n const ip = at.split(\":\")[0];\n return Object.entries(run.group?.members ?? {}).find(([, part]) => part.ip === ip)?.[0] ?? null;\n};\n\n/** This function gives the time of each stage of a timeline from an instant: the first announcement of each, after the instant. */\nexport const stagesOf = (timeline: readonly SentinelEvent[], from: number): Partial<Record<Stage, Quantity>> => {\n const out: Partial<Record<Stage, Quantity>> = {};\n for (const line of timeline) {\n if (line.ev !== \"sentinel\" || line.t < from) continue;\n const stage = STAGES.find((each) => each === line.event);\n if (stage === undefined || out[stage] !== undefined) continue;\n // the down states count for the primary only, not for a replica or a Sentinel\n if ((stage === \"+sdown\" || stage === \"+odown\") && !String(line.detail ?? \"\").startsWith(\"master \")) continue;\n out[stage] = between(from, line.t);\n }\n return out;\n};\n\n/** These are the stages of the timeline of a replica set, in their usual sequence after a fault. */\nexport const REPLICA_SET_STAGES: readonly ReplicaSetStage[] = [\n \"member-down\",\n \"election-start\",\n \"elected\",\n \"caught-up\",\n \"writable\",\n \"old-primary-steps-down\",\n \"discovered\",\n \"written-off\",\n \"first-write\",\n];\n\n/**\n * A member mark is how a log line of mongod marks a stage: by its id, or by its message. The\n * message is the fallback for a version whose ids differ. `at` tells where the line comes from:\n * the old primary, or another member.\n */\ninterface MemberMark {\n readonly ids: readonly number[];\n readonly message: RegExp;\n readonly at: \"old\" | \"other\";\n}\n\n/** These are the log lines of mongod that mark each stage of a member. */\nexport const MEMBER_MARKS: Readonly<Record<Exclude<ReplicaSetStage, \"discovered\" | \"written-off\" | \"first-write\">, MemberMark>> = {\n \"member-down\": { ids: [21216], message: /now in state DOWN/, at: \"other\" },\n \"election-start\": { ids: [4615652, 21437], message: /^Starting an election|Skipping dry run|Conducting a dry run/, at: \"other\" },\n elected: { ids: [21450], message: /^Election succeeded/, at: \"other\" },\n \"caught-up\": { ids: [21363], message: /^Exited primary catch-up mode/, at: \"other\" },\n writable: { ids: [21331], message: /^Transition to primary complete/, at: \"other\" },\n \"old-primary-steps-down\": { ids: [21809, 21475, 962660], message: /relinquishing primary|Stepping down from primary/, at: \"old\" },\n};\n\n/** This function tells if a log line of a member marks a stage, with the old primary of the run. */\nconst marks = (line: SentinelEvent, stage: keyof typeof MEMBER_MARKS, old: string, oldAddress: string): boolean => {\n const mark = MEMBER_MARKS[stage];\n const member = String(line[\"member\"] ?? \"\");\n if ((mark.at === \"old\") !== (member === old)) return false;\n const attr = (line[\"attr\"] ?? {}) as Record<string, unknown>;\n // the DOWN state counts for the old primary only, as another member sees it\n if (stage === \"member-down\" && !JSON.stringify(attr).includes(oldAddress)) return false;\n // the generic state change of the old primary: PRIMARY to another state\n if (stage === \"old-primary-steps-down\" && Number(line[\"id\"]) === 21358 && attr[\"oldState\"] === \"PRIMARY\") return true;\n return mark.ids.includes(Number(line[\"id\"])) || mark.message.test(String(line[\"msg\"] ?? \"\"));\n};\n\n/**\n * This function gives the time of each stage of a replica set from an instant, in a run. The stages\n * of the members come from their log lines (`ev` \"mongod\"), whose times the lab puts on the\n * monotonic clock. The stages of the client come from its lines:\n * - `discovered`: a member other than the old primary becomes RSPrimary in the view of the client.\n * - `written-off`: the first heartbeat of the client to the old primary that fails.\n * - `first-write`: the first write that a primary accepts, of a write that the client sent after\n * the commit of the fault (`sentAfter`). It is on a new primary when `lands` is `new`.\n */\nexport const replicaSetStagesOf = (\n run: Run,\n from: number,\n sentAfter: number,\n lands: \"new\" | \"same\",\n): Partial<Record<Stage, Quantity>> => {\n const out: Partial<Record<Stage, Quantity>> = {};\n const old = run.group?.primary ?? \"\";\n const oldIp = run.group?.members[old]?.ip ?? \"\";\n const oldAddress = `${oldIp}:`;\n for (const line of run.group?.timeline ?? []) {\n if (line.ev !== \"mongod\" || line.t < from) continue;\n for (const stage of Object.keys(MEMBER_MARKS) as Array<keyof typeof MEMBER_MARKS>)\n if (out[stage] === undefined && marks(line, stage, old, oldAddress)) out[stage] = between(from, line.t);\n }\n const isOld = (address: unknown): boolean => typeof address === \"string\" && address.startsWith(oldAddress);\n for (const event of run.events) {\n if (event.t < from) continue;\n if (out.discovered === undefined && event.ev === \"sdam\" && event[\"event\"] === \"server-changed\" && !isOld(event[\"address\"])) {\n const next = event[\"next\"] as { readonly type?: string } | null;\n if (next?.type === \"RSPrimary\") out.discovered = between(from, event.t);\n }\n if (out[\"written-off\"] === undefined && event.ev === \"sdam\" && event[\"event\"] === \"heartbeat-failed\" && isOld(event[\"address\"]))\n out[\"written-off\"] = between(from, event.t);\n }\n const accepted = acceptedAfter(run, sentAfter, lands);\n if (accepted !== undefined) out[\"first-write\"] = between(from, accepted.t);\n return out;\n};\n\n/** This function gives the first write that a primary accepted, of a write sent after an instant. It is on a new primary for `new`. */\nconst acceptedAfter = (run: Run, sentAfter: number, lands: \"new\" | \"same\"): ProbeEvent | undefined => {\n const sent = new Map<number, number>();\n for (const event of run.events) if (event.ev === \"sent\") sent.set(Number(event[\"n\"]), event.t);\n const primary = run.group?.primary ?? \"\";\n return run.events.find((event) => {\n if (event.ev !== \"ack\" || (sent.get(Number(event[\"n\"])) ?? -Infinity) <= sentAfter) return false;\n const member = memberAt(run, event[\"at\"]);\n return member !== null && (lands === \"new\" ? member !== primary : true);\n });\n};\n\n/** This function gives what became of each write of a run, from the lines of the client and the keys that the group kept. */\nexport const writesOf = (run: Run, after: number, primary: string): Writes => {\n const { events } = run;\n const sent = events.filter((event) => event.ev === \"sent\");\n // without its done line, the stop ended the last write that the client sent\n const done = events.find((event) => event.ev === \"done\")?.t ?? sent.at(-1)?.t ?? Infinity;\n const acks = new Map<number, ProbeEvent>();\n for (const event of events) if (event.ev === \"ack\") acks.set(Number(event[\"n\"]), event);\n const holds = run.group?.holds ?? null;\n const kept = holds === null ? null : new Set(holds);\n const old = [...acks.values()].filter((event) => event.t > after && memberAt(run, event[\"at\"]) === primary).length;\n const lost = kept === null ? null : [...acks.keys()].filter((n) => !kept.has(n)).sort((a, b) => a - b);\n // a write in flight when the client stopped is not on the way: the stop ended it\n const unknown = sent\n .filter((event) => event.t < done && !acks.has(Number(event[\"n\"])))\n .map((event) => {\n const n = Number(event[\"n\"]);\n return { n, held: kept === null ? null : kept.has(n) };\n });\n return { sent: sent.length, acked: acks.size, old, lost, unknown };\n};\n\n/** This function gives the errors that the client met after an instant: each kind, its first time, and its count. */\nexport const errorsOf = (events: readonly ProbeEvent[], after: number): ErrorOnTheWay[] => {\n const done = events.find((event) => event.ev === \"done\")?.t ?? Infinity;\n const kinds = new Map<string, { first: number; count: number }>();\n for (const event of events) {\n if (event.t <= after || event.t >= done) continue;\n const name =\n event.ev === \"error\"\n ? String(event[\"errno\"] ?? event[\"name\"] ?? \"error\")\n : event.ev === \"fail\"\n ? String(event[\"name\"] ?? \"fail\")\n : event.ev === \"sentinel-error\"\n ? \"sentinel-error\"\n : null;\n if (name === null) continue;\n const seen = kinds.get(name);\n if (seen === undefined) kinds.set(name, { first: event.t, count: 1 });\n else seen.count += 1;\n }\n return [...kinds].map(([name, { first, count }]) => ({ name, first: between(after, first), count }));\n};\n\n/**\n * This function gives the delay of each stamp of the observer after the write of its Sentinel that\n * the observer read. That write is the latest write of the Sentinel's socket toward the observer,\n * at the stamp or before it. The prober records each write (`tx`) on the clock of the stamps.\n */\nexport const lagsOf = (run: Run, kernel: readonly KernelRecord[]): Quantity[] => {\n const timeline = run.group?.timeline ?? [];\n const locals = new Map<string, string>();\n for (const line of timeline) if (line.ev === \"subscribed\") locals.set(String(line.sentinel), String(line[\"local\"]));\n const out: Quantity[] = [];\n for (const line of timeline) {\n if (line.ev !== \"sentinel\") continue;\n const sentinel = String(line.sentinel);\n const local = locals.get(sentinel);\n if (local === undefined) continue;\n const write = kernel\n .filter((record) => record.ev === \"tx\" && record.src === sentinel && record.dst === local && record.mono <= line.t)\n .at(-1);\n if (write !== undefined) out.push(between(write.mono, line.t));\n }\n return out;\n};\n\n/** This type is the measurement of observe.ts, which a group run fills. */\nexport interface GroupReading {\n readonly outcome: { readonly kind: \"answered\" } | { readonly kind: \"silent\" };\n readonly elapsed: Quantity | null;\n readonly socket: Quantity | null;\n readonly noticed: Quantity | null;\n readonly void: string | null;\n readonly finding: string | null;\n readonly group: GroupMeasured;\n}\n\n/**\n * This function reads one run of a group cell.\n * - The reference is the start of the commit of the fault, as the injector stamped it. For a\n * stall, it is the first write into the fault, as for the stall of a direct pair.\n * - The outcome is the first write that a primary accepted after the fault. The prediction says\n * which primary: a new one, or the same one after the lift. In a replica set, it is the first\n * accepted write of a write that the client sent after the commit of the fault.\n * - The stages come from the Sentinels, or from the logs of the members of a replica set and the\n * lines of its client (`replicaSetStagesOf`).\n * - Its time is the last segment with data that the client's socket to that primary received\n * before the client saw the acceptance. The stamp of the client is the fallback.\n * - A run is void when the prober lost records, or when the client did not become ready. It is\n * also void when a Sentinel entered its TILT mode, or when the election split its votes. A band\n * from the fault whose commit took longer than δ_abs is void too. So is a band whose hold lifted\n * more than δ_abs late.\n */\nexport const measureGroup = (cell: Cell, run: Run): GroupReading => {\n const { events, kernel } = run;\n const { prediction } = cell;\n const expected = prediction.group;\n const primary = run.group?.primary ?? \"\";\n const ready = events.find((event) => event.ev === \"ready\");\n const t0 = run.fault?.t0;\n const empty: GroupMeasured = {\n from: primary,\n to: null,\n stages: {},\n writes: { sent: 0, acked: 0, old: 0, lost: null, unknown: [] },\n errors: [],\n };\n const silent = { outcome: { kind: \"silent\" } as const, elapsed: null, socket: null, noticed: null, finding: null };\n if (ready === undefined) return { ...silent, void: \"the client never became ready\", group: empty };\n if (t0 === undefined || run.fault === null) return { ...silent, void: \"the fault never committed\", group: empty };\n if (run.lost === null)\n return { ...silent, void: \"the prober wrote no clock line after the run, so nothing says what it dropped\", group: empty };\n if (run.lost > 0) return { ...silent, void: `the prober dropped ${run.lost} records`, group: empty };\n const timeline = run.group?.timeline ?? [];\n const heal = run.heal ?? null;\n const lands = expected?.lands ?? \"new\";\n // a replica set has no authority: its members and the client tell its stages\n const replica = cell.replicaSet !== undefined;\n const stall = prediction.detects === false && prediction.recovers !== undefined;\n const stages = replica ? replicaSetStagesOf(run, t0, run.fault.t1, lands) : stagesOf(timeline, t0);\n // the first write that a primary accepted after the fault: on a new primary, or on the same one\n // after the lift. In a replica set, it is the first write sent after the commit of the fault\n // that a primary accepts: a partial cut leaves the old primary its writes\n const after = lands === \"same\" && heal !== null && (!replica || stall) ? heal.t0 : t0;\n const sentAt = new Map<number, number>();\n for (const event of events) if (event.ev === \"sent\") sentAt.set(Number(event[\"n\"]), event.t);\n const accepted = events.find((event) => {\n if (event.ev !== \"ack\" || event.t <= after) return false;\n if (replica && (sentAt.get(Number(event[\"n\"])) ?? -Infinity) <= run.fault!.t1) return false;\n const member = memberAt(run, event[\"at\"]);\n return member !== null && (lands === \"new\" ? member !== primary : true);\n });\n const to = accepted === undefined ? null : memberAt(run, accepted[\"at\"]);\n // the first write of a replica set is the outcome itself: a stall's first write is the write into it\n if (replica) {\n delete stages[\"first-write\"];\n if (accepted !== undefined) stages[\"first-write\"] = between(t0, accepted.t);\n }\n const group: GroupMeasured = {\n from: primary,\n to,\n stages,\n writes: writesOf(run, t0, primary),\n errors: errorsOf(events, t0),\n ...(cell.calibrates === \"sentinels\" ? { lags: lagsOf(run, kernel) } : {}),\n };\n let voided: string | null = null;\n const tilt = timeline.find((line) => line.ev === \"sentinel\" && line.event === \"+tilt\" && line.t >= t0);\n if (tilt !== undefined) voided = `a Sentinel entered TILT ${between(t0, tilt.t).value} s after the fault: the host delayed it, not its timers`;\n const split = timeline.find((line) => line.ev === \"sentinel\" && line.event === \"-failover-abort-not-elected\" && line.t >= t0);\n const elected = timeline.find((line) => line.ev === \"sentinel\" && line.event === \"+elected-leader\" && line.t >= t0);\n if (voided === null && split !== undefined && (elected === undefined || elected.t > split.t))\n voided = \"the election split its votes and runs again after 2 × failover-timeout, which the band does not bound\";\n // a Sentinel starts a failover in the tick of its +odown, unless an attempt of its own came less\n // than 2 × failover-timeout before: then the run measured an earlier run, not its own fault\n const odown = stages[\"+odown\"];\n if (voided === null && odown !== undefined && stages[\"+try-failover\"] === undefined)\n voided = \"no Sentinel started a failover after +odown: each was in the hold-off of 2 × failover-timeout after an earlier attempt\";\n // a hold lifts at its instant in the VM: a band assumes that instant, and a lift later than the\n // noise moved each stage after it\n if (voided === null && cell.hold !== undefined && prediction.detects !== null) {\n if (heal === null) voided = \"the fault never lifted\";\n else {\n const late = (heal.t0 - t0) / 1e9 - valueIn(cell.hold, \"s\");\n if (late > valueIn(NOISE, \"s\"))\n voided = `the lift came ${Math.round(late * 1e4) / 10} ms after its hold: the injector was late, not the group`;\n }\n }\n // a band holds for the round trips that it assumes: the first one of the client's socket to the old primary\n const assumed = prediction.detects === null ? undefined : prediction.rtt;\n const first = kernel.find(\n (record) =>\n record.netns === run.netns &&\n record.dst === `${run.group?.members[primary]?.ip}:${(cell.group ?? cell.replicaSet)?.port}` &&\n record.tcp?.srtt !== undefined,\n );\n const rtt = first?.tcp?.srtt === undefined ? null : valueIn(first.tcp.srtt, \"us\") / 1000;\n if (voided === null && assumed !== undefined && rtt !== null && rtt > valueIn(assumed.high, \"ms\"))\n voided = `rtt-over-assumption: its first round trip was ${rtt} ms, above the ${valueIn(assumed.high, \"ms\")} ms that the prediction assumes`;\n if (voided === null && prediction.detects === true && prediction.from === \"fault\" && (run.fault.t1 - run.fault.t0) / 1e6 > valueIn(NOISE, \"ms\"))\n voided = `the commit of the fault took ${Math.round((run.fault.t1 - run.fault.t0) / 1e5) / 10} ms, longer than the noise of the pipeline: its start is not known`;\n\n // the reference: the fault, or for a stall the first write into it\n let reference = t0;\n if (prediction.detects === false && prediction.recovers !== undefined) {\n const into = events.find((event) => event.ev === \"sent\" && event.t > run.fault!.t1);\n if (into === undefined || (heal !== null && into.t > heal.t0)) {\n voided ??= \"the write in flight went out before the fault held\";\n } else {\n reference = into.t;\n // the stall must end where the prediction assumed: after the firing before, and before its own\n const { heal: bounds } = prediction.recovers;\n if (heal === null) voided ??= \"the stall never ended\";\n else if (\n valueIn(between(reference, heal.t0), \"s\") <= valueIn(bounds.low, \"s\") ||\n valueIn(between(reference, heal.t1), \"s\") >= valueIn(bounds.high, \"s\")\n )\n voided ??= `the stall ended ${between(reference, heal.t0).value}–${between(reference, heal.t1).value} s after the write, outside the ${bounds.low.value}–${bounds.high.value} s its prediction assumed`;\n }\n }\n\n // findings: a failover where none should be, a client on the wrong primary, an error out of its band\n const findings: string[] = [];\n // a failover: a Sentinel switches the primary, or another member of a replica set wins an election\n const switched = replica ? stages[\"elected\"] : stages[\"+switch-master\"];\n if (expected?.failover === false && switched !== undefined)\n findings.push(`the group failed over ${switched.value} s after the fault, where it should not`);\n for (const stage of expected?.quiet ?? []) {\n const at = stages[stage];\n if (at !== undefined) findings.push(`${stage} came ${at.value} s after the fault, where it should not`);\n }\n if (accepted !== undefined && lands === \"same\" && to !== primary)\n findings.push(`the client wrote to ${to ?? String(accepted[\"at\"])}, where it should stay on ${primary}`);\n for (const error of expected?.errors ?? []) {\n const met = group.errors.find((each) => each.name === error.name);\n const noise = valueIn(NOISE, \"s\");\n if (met === undefined) findings.push(`the client never met ${error.name}`);\n else if (met.first.value < valueIn(error.low, \"s\") - noise || met.first.value > valueIn(error.high, \"s\") + noise)\n findings.push(`the client met ${error.name} ${met.first.value} s after the fault, outside [${valueIn(error.low, \"s\")}, ${valueIn(error.high, \"s\")}] s`);\n }\n if (accepted === undefined) {\n if (prediction.detects !== null)\n findings.push(lands === \"new\" ? \"the client never wrote to a new primary in the window\" : \"the client never wrote again in the window\");\n return { ...silent, void: voided, finding: findings[0] ?? null, group };\n }\n // the kernel's time of the acceptance: the last segment with data from that primary at the\n // client's stamp or before it. The monitor and the pinger of a driver have their own sockets to\n // each member, thus only the sockets of the pool count\n const address = String(accepted[\"at\"]);\n const others = new Set(socketsOf(events).filter((socket) => socket.role !== \"pool\").map((socket) => socket.local));\n const segment = kernel\n .filter(\n (record) =>\n record.netns === run.netns &&\n record.ev === \"rx\" &&\n record.dst === address &&\n !others.has(String(record.src)) &&\n (record.seg?.len.value ?? 0) > 0 &&\n record.mono <= accepted.t &&\n record.mono > after,\n )\n .at(-1);\n const at = segment?.mono ?? accepted.t;\n return {\n outcome: { kind: \"answered\" },\n elapsed: between(reference, at),\n socket: between(reference, accepted.t),\n noticed: segment === undefined ? null : between(segment.mono, accepted.t),\n void: voided,\n finding: findings[0] ?? null,\n group,\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, KernelRecord, ProbeEvent, Reference, Run } from \"./cell.ts\";\nimport type { DriverStepTime } from \"./driver.ts\";\nimport { measureDriver } from \"./driver.ts\";\nimport type { GroupMeasured } from \"./timeline.ts\";\nimport { measureGroup } from \"./timeline.ts\";\nimport { NOISE, requestWords } from \"./verdict.ts\";\n\n/**\n * An outcome tells how the wait of a run ended. The socket failed with an errno, or it read the\n * end of the file. Or nothing came, or the reply came back after a stall.\n */\nexport type Outcome =\n | { readonly kind: \"error\"; readonly errno: string }\n | { readonly kind: \"eof\" }\n | { readonly kind: \"silent\" }\n | { readonly kind: \"answered\" }\n | { readonly kind: \"closed\" };\n\n/**\n * A request reading is what the second request of an HTTP client met, from the lines of the\n * client. It holds the connection of its first try, a new try, and its outcome. `gap` is the time\n * from the end of the first reply to the send of the second request.\n */\nexport interface RequestReading {\n readonly connection: \"reused\" | \"new\";\n readonly retried: boolean;\n readonly error: string | null;\n readonly pending: boolean;\n readonly gap: Quantity | null;\n}\n\n/** A measurement is what one run measured, against the prediction of its cell. */\nexport interface Measured {\n readonly environment: number;\n readonly index: number;\n readonly outcome: Outcome;\n /**\n * This time is from the reference event of the prediction to the detection, both as the kernel\n * stamped them in the records of the prober. It is null when nothing detected the fault.\n */\n readonly elapsed: Quantity | null;\n /** This time is the same interval from the TCP_INFO samples of the client, the second vantage. */\n readonly socket: Quantity | null;\n /** This time is how long the client took to see the error after the kernel reported it. */\n readonly noticed: Quantity | null;\n /**\n * These are the steps of the kernel after the reference: each keepalive probe, or each firing of\n * the RTO timer. After a stall, they are each retransmission from the send, the loss probe first.\n */\n readonly steps: readonly Quantity[];\n /**\n * These are the reply times of a measurement-only cell. For each request after the first, a\n * sample is the time from its send to the first data after it.\n */\n readonly samples?: readonly Quantity[];\n /** This count is the number of keepalive probes that the kernel sent. */\n readonly probes: number;\n /** This count is the number of segments that the kernel sent again. */\n readonly retransmissions: number;\n /** This count is the number of segments that the socket received after the request. */\n readonly acks: number;\n /** This count is the number of bytes that the client read after the request. */\n readonly bytes?: number;\n /**\n * This time is when a router of the topology forgot the flow of the client. It counts from the\n * last segment that the client received before the fault. It is null when no router forgot the flow.\n */\n readonly forgot?: Quantity | null;\n /**\n * This time is the first round trip of the socket of the observer. It is the smoothed round trip\n * in its first record, which is the sample of the handshake. It is null when no record has it.\n */\n readonly rtt?: Quantity | null;\n /** This record tells what reordering did in a run of a measurement-only cell, from the records of its two ends. */\n readonly reordering?: Reordering;\n /** These are the times of the proxy of a calibration cell, one for each request and its reply. */\n readonly crossings?: Crossings;\n /** This record is what a run of a group cell measured: the stages of its timeline, and what became of each write. */\n readonly group?: GroupMeasured;\n /** This record is what the second request of an HTTP client met, for a cell whose prediction says it. */\n readonly request?: RequestReading | null;\n /** This record is the time of each later band of a driver cell (`DriverBand.then`). */\n readonly driver?: { readonly steps: readonly DriverStepTime[] };\n /** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */\n readonly void: string | null;\n /** This text tells what in the run disagrees with the prediction, other than its timing, or null. */\n readonly finding: string | null;\n}\n\n/**\n * A reordering record is what reordering, duplicates and losses did to the data of a run. The\n * receiver is the client, and the sender is the socket of its peer.\n * - `outOfOrder` counts the arrivals at the client beyond a hole, and `duplicates` counts the\n * arrivals whose data the client had. The prober reads the two from each arrival, against `rcv_nxt`.\n * - An arrival can hold more than one segment. The kernel joins the segments that wait in the\n * backlog of a socket while its owner holds it.\n * - `ooo` is the count of the kernel of the client in segments (`rcv_ooopack`).\n * - `sacked` and `lost` are the most segments that the sender held as SACKed and as lost.\n * - `dsacks` is the DSACK blocks that the sender received, and `fast` its fast retransmissions.\n * - `degree` is the highest reordering degree of the sender (`tp->reordering`), and `seen` the\n * reorderings that it detected (`reord_seen`).\n *\n * The counts of the kernel are null for a record of the prober before version 0.2.\n */\nexport interface Reordering {\n readonly outOfOrder: number;\n readonly duplicates: number;\n readonly ooo: number | null;\n readonly sacked: number | null;\n readonly lost: number | null;\n readonly dsacks: number | null;\n readonly fast: number;\n readonly degree: number | null;\n readonly seen: number | null;\n}\n\n/**\n * A crossings record is the time of a proxy for each request (`up`) and for each reply (`down`).\n * The time of a request starts at its arrival on the socket toward the client. It stops at the\n * write on the socket toward the target. The time of a reply goes the other way. A write is a\n * `tx` record of the prober.\n */\nexport interface Crossings {\n readonly hop: string;\n readonly up: readonly Quantity[];\n readonly down: readonly Quantity[];\n}\n\n/** These are the errno numbers that the kernel reports to a socket, by the names that predictions use. */\nconst ERRNO: Readonly<Record<number, string>> = {\n 32: \"EPIPE\",\n 101: \"ENETUNREACH\",\n 103: \"ECONNABORTED\",\n 104: \"ECONNRESET\",\n 110: \"ETIMEDOUT\",\n 111: \"ECONNREFUSED\",\n 113: \"EHOSTUNREACH\",\n};\n\n/** This function gives the interval between two instants on the one clock, in seconds to the microsecond. */\nconst between = (from: number, to: number): Quantity =>\n quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\n\n/** These functions give the states of a state record: before the change, and after it. TCP_ESTABLISHED is 1, TCP_CLOSE_WAIT 8. */\nconst oldState = (record: KernelRecord): number | undefined => (record as { readonly old?: number }).old;\nconst newState = (record: KernelRecord): number | undefined => (record as { readonly new?: number }).new;\n\nconst count = (event: ProbeEvent | undefined, field: string): number =>\n event === undefined ? 0 : Number(event[field] ?? 0);\n\n/** This function gives the view of the client on the same interval: TCP_INFO, with a sample each 2 ms. */\nconst socketVantage = (\n cell: Cell,\n events: readonly ProbeEvent[],\n ready: ProbeEvent,\n request: ProbeEvent,\n): Quantity | null => {\n if (!cell.prediction.detects || cell.prediction.from === \"peer-closed\" || cell.prediction.from === \"fault\")\n return null;\n const ended = events.find((event) => event.ev === \"error\" || event.ev === \"eof\");\n const reference =\n cell.prediction.from === \"last-received\"\n ? ready\n : cell.prediction.from === \"request-sent\"\n ? request\n : events.find(\n (event) =>\n event.ev === \"kernel\" && event.t >= request.t && count(event, \"retransmits\") > 0,\n );\n return ended === undefined || reference === undefined ? null : between(reference.t, ended.t);\n};\n\n/** A socket place is a socket of a run: its namespace and its cookie. */\nexport interface SocketPlace {\n readonly netns: number;\n readonly cookie: number;\n}\n\nconst ipOf = (address: string): string => address.split(\":\")[0]!;\n\n/**\n * This function finds the socket that became ESTABLISHED in a run, in a namespace, whose peer\n * matches. In a topology, an address of a hop serves each run, thus only the socket that the\n * run opened is the socket of the run.\n */\nconst opened = (\n run: Run,\n netns: number,\n peer: (dst: string) => boolean,\n): SocketPlace | undefined => {\n const record = run.kernel.find(\n (each) =>\n each.netns === netns &&\n each.ev === \"state\" &&\n newState(each) === 1 &&\n each.dst !== undefined &&\n peer(each.dst),\n );\n return record?.cookie === undefined ? undefined : { netns, cookie: record.cookie };\n};\n\n/** This function finds the socket of the target for the connection of a run. In a topology, its peer is the last hop. */\nconst targetSocket = (run: Run, local: unknown): SocketPlace | undefined => {\n if (run.topology === undefined) {\n const cookie = run.kernel.find(\n (record) => record.netns === run.targetNetns && record.dst === local,\n )?.cookie;\n return cookie === undefined ? undefined : { netns: run.targetNetns, cookie };\n }\n const peer = run.topology.targetPeer;\n return opened(run, run.targetNetns, (dst) => ipOf(dst) === peer);\n};\n\n/**\n * This function finds the socket of the observer of a cell in a run:\n * - the client socket, by the address that it connected from, in its namespace.\n * - the socket of the target for the same connection, in its namespace.\n * - a socket of a proxy of the topology, toward the client or toward the target.\n */\nexport const socketOf = (cell: Cell, run: Run): SocketPlace | undefined => {\n const local = run.events.find((event) => event.ev === \"connected\")?.[\"local\"];\n const observer = cell.observer ?? \"client\";\n if (observer === \"client\") {\n const cookie = run.kernel.find((record) => record.netns === run.netns && record.src === local)?.cookie;\n return cookie === undefined ? undefined : { netns: run.netns, cookie };\n }\n if (observer === \"server\") return targetSocket(run, local);\n const hop = run.topology?.hops[observer.hop];\n if (hop === undefined) return undefined;\n return observer.toward === \"client\"\n ? opened(run, hop.netns, (dst) => ipOf(dst) === hop.client)\n : opened(run, hop.netns, (dst) => dst === hop.target);\n};\n\n/**\n * This function finds the socket of the peer of the observer: its close is the reference of a FIN.\n * In a direct pair, it is the other end of the connection. In a topology, it is the socket of the\n * target, for the client.\n */\nconst peerOf = (cell: Cell, run: Run): SocketPlace | undefined => {\n const local = run.events.find((event) => event.ev === \"connected\")?.[\"local\"];\n const server = cell.observer === \"server\";\n if (run.topology !== undefined) return server ? undefined : targetSocket(run, local);\n const netns = server ? run.netns : run.targetNetns;\n const cookie = run.kernel.find(\n (record) => record.netns === netns && (server ? record.src : record.dst) === local,\n )?.cookie;\n return cookie === undefined ? undefined : { netns, cookie };\n};\n\n/** This function tells if sequence number a is after b, in the 32-bit space of TCP (`after` in include/net/tcp.h). */\nconst after32 = (a: number, b: number): boolean => ((a - b) | 0) > 0;\n\n/** This function gives the largest value of a count of the kernel in records, or null when no record has it. */\nconst most = (records: readonly KernelRecord[], count: (record: KernelRecord) => number | undefined): number | null =>\n records.reduce<number | null>((top, record) => {\n const value = count(record);\n return value === undefined ? top : Math.max(top ?? 0, value);\n }, null);\n\n/** This function gives what reordering did in a run, from the records of the receiver and of the sender. */\nexport const reorderingOf = (\n receiver: readonly KernelRecord[],\n sender: readonly KernelRecord[],\n): Reordering => {\n const arrivals = receiver.filter(\n (record) => record.ev === \"rx\" && (record.seg?.len.value ?? 0) > 0 && record.tcp?.rcv_nxt !== undefined,\n );\n const outOfOrder = arrivals.filter((record) => after32(record.seg!.seq, record.tcp!.rcv_nxt!)).length;\n const duplicates = arrivals.filter(\n (record) => !after32(record.seg!.seq + record.seg!.len.value, record.tcp!.rcv_nxt!),\n ).length;\n return {\n outOfOrder,\n duplicates,\n ooo: most(receiver, (record) => record.tcp?.rcv_ooopack),\n sacked: most(sender, (record) => record.tcp?.sacked),\n lost: most(sender, (record) => record.tcp?.lost),\n dsacks: most(sender, (record) => record.tcp?.dsack_dups),\n fast: sender.filter((record) => record.ev === \"retrans\" && record.why === \"fast\").length,\n degree: most(sender, (record) => record.tcp?.reordering),\n seen: most(sender, (record) => record.tcp?.reord_seen),\n };\n};\n\n/**\n * This function gives the times of a proxy in a run, for each request after the first. The time\n * of a request starts at its arrival on the socket of the proxy toward the client. It stops at the\n * next write on the socket toward the target. The time of its reply starts at its arrival on the\n * socket toward the target, and it stops at the next write toward the client. A request without\n * each record gives no time.\n */\nexport const crossingsOf = (\n run: Run,\n hop: string,\n toClient: SocketPlace | undefined,\n toTarget: SocketPlace | undefined,\n): Crossings => {\n const up: Quantity[] = [];\n const down: Quantity[] = [];\n if (toClient === undefined || toTarget === undefined) return { hop, up, down };\n const of = (place: SocketPlace) =>\n run.kernel.filter((record) => record.netns === place.netns && record.cookie === place.cookie);\n const near = of(toClient);\n const far = of(toTarget);\n const arrival = (records: readonly KernelRecord[], from: number, to: number) =>\n records.find((record) => record.ev === \"rx\" && (record.seg?.len.value ?? 0) > 0 && record.mono > from && record.mono < to);\n const write = (records: readonly KernelRecord[], from: number, to: number) =>\n records.find((record) => record.ev === \"tx\" && record.mono >= from && record.mono < to);\n const sends = run.events.filter((event) => event.ev === \"sent\").slice(1);\n sends.forEach((send, n) => {\n const next = sends[n + 1]?.t ?? Infinity;\n const request = arrival(near, send.t, next);\n const relayed = request === undefined ? undefined : write(far, request.mono, next);\n if (request === undefined || relayed === undefined) return;\n up.push(between(request.mono, relayed.mono));\n const reply = arrival(far, relayed.mono, next);\n const answered = reply === undefined ? undefined : write(near, reply.mono, next);\n if (reply !== undefined && answered !== undefined) down.push(between(reply.mono, answered.mono));\n });\n return { hop, up, down };\n};\n\n/**\n * This function reads one run against its cell, from the records of the kernel first.\n * - The socket of the run is the socket of its observer. It is the socket that the client connected\n * from, in its namespace, or the socket of the target or of a proxy for that connection.\n * - The fault must hold before the event that it must meet. That is before the request goes into\n * it, and before the first keepalive probe. A live peer answers that probe, and the answer\n * starts the idle time again. A run where the fault was late is void. A run whose records the\n * prober lost is also void.\n * - For an HTTP client, it also reads what the second request met (`requestOf`). An outcome other\n * than the one that the prediction gives is a finding.\n */\nexport const measure = (cell: Cell, run: Run): Measured => {\n const read = measureSocket(cell, run);\n const expected = cell.prediction.request;\n if (cell.probe.http === undefined && expected === undefined) return read;\n const request = requestOf(run.events);\n if (expected === undefined || read.void !== null) return { ...read, request };\n const differs = requestFinding(expected, request);\n const finding = read.finding === null ? differs : differs === null ? read.finding : `${read.finding}. ${differs}`;\n return { ...read, request, finding };\n};\n\n/** This function tells how a request reading differs from its prediction, or gives null. */\nconst requestFinding = (expected: NonNullable<Cell[\"prediction\"][\"request\"]>, read: RequestReading | null): string | null => {\n if (read === null) return `the second request never went out, where it should meet: ${requestWords(expected)}`;\n const same =\n read.connection === expected.connection &&\n read.retried === expected.retried &&\n read.error === expected.error &&\n read.pending === (expected.pending === true);\n return same ? null : `the second request met: ${requestWords(read)}, not ${requestWords(expected)}`;\n};\n\n/**\n * This function reads the second request of an HTTP client from its lines. Its first try is its\n * first `request` line, and each `retry` line is a new try. Its end is its `error` line, or its\n * `replied` line. A request with neither is pending. It gives null when the client wrote no try.\n *\n * A new try that gets no connection writes no `retry` line. Its dial writes a `dial-error` line.\n * Thus a `dial-error` line after the first try and before the end is also a new try.\n */\nexport const requestOf = (events: readonly ProbeEvent[]): RequestReading | null => {\n const of = (event: ProbeEvent) => Number(event[\"i\"]) === 2;\n const tries = events.filter((event) => (event.ev === \"request\" || event.ev === \"retry\") && of(event));\n const first = tries[0];\n if (first === undefined) return null;\n const failed = events.find((event) => event.ev === \"error\" && of(event));\n const replied = events.some((event) => event.ev === \"replied\" && of(event));\n const ready = events.find((event) => event.ev === \"ready\");\n const sent = events.find((event) => event.ev === \"sent\" && of(event));\n const end = failed ?? events.find((event) => event.ev === \"done\" && of(event));\n const dialed = events.some(\n (event) => event.ev === \"dial-error\" && event.t > first.t && (end === undefined || event.t <= end.t),\n );\n return {\n connection: first[\"reused\"] === true ? \"reused\" : \"new\",\n retried: dialed || tries.some((each) => each.ev === \"retry\"),\n error: failed === undefined ? null : String(failed[\"errno\"]),\n pending: failed === undefined && !replied,\n gap: ready === undefined || sent === undefined ? null : between(ready.t, sent.t),\n };\n};\n\n/** This function reads the socket of one run against its cell, as `measure` does without the second request of an HTTP client. */\nconst measureSocket = (cell: Cell, run: Run): Measured => {\n if (cell.group !== undefined || cell.replicaSet !== undefined) {\n const read = measureGroup(cell, run);\n return {\n environment: run.environment,\n index: run.index,\n outcome: read.outcome,\n elapsed: read.elapsed,\n socket: read.socket,\n noticed: read.noticed,\n steps: [],\n probes: 0,\n retransmissions: run.kernel.filter((record) => record.netns === run.netns && record.ev === \"retrans\").length,\n acks: read.group.writes.acked,\n void: read.void,\n finding: read.finding,\n group: read.group,\n };\n }\n // a driver with more than one socket: its bands name their socket and their line\n if (cell.prediction.detects !== null && cell.prediction.driver !== undefined) return measureDriver(cell, run);\n const { events, kernel } = run;\n const ready = events.find((event) => event.ev === \"ready\");\n const local = events.find((event) => event.ev === \"connected\")?.[\"local\"];\n // a client in a gap sends its second request by itself, and its idle connection counts from its first reply\n const inflight = cell.probe.mode !== \"idle\" && cell.probe.mode !== \"gap\";\n const request = inflight ? events.filter((event) => event.ev === \"sent\")[1] : ready;\n const server = cell.observer === \"server\";\n const place = socketOf(cell, run);\n const netns = place?.netns;\n const cookie = place?.cookie;\n const socket = kernel.filter((record) => record.netns === netns && record.cookie === cookie);\n // the socket of the peer for the same connection: a FIN counts from the change of state that its close makes\n const peerPlace = peerOf(cell, run);\n const peerNetns = peerPlace?.netns;\n const peerCookie = peerPlace?.cookie;\n // the bytes that the client read after its request, and the first and last time that data came\n const after = request === undefined ? [] : events.filter((event) => event.ev === \"data\" && event.t > request.t);\n const bytes = after.reduce((sum, event) => sum + Number(event[\"n\"] ?? 0), 0);\n // the end of the flow in a router of the topology: its DESTROY line, by the address of the client\n const forgotten = (run.middlebox ?? []).find(\n (line) =>\n line.ev === \"ct\" &&\n line[\"type\"] === \"DESTROY\" &&\n typeof line[\"orig\"] === \"string\" &&\n (line[\"orig\"] as string).startsWith(`${String(local)}>`),\n );\n const keepalives = socket.filter(\n (record) => record.ev === \"probe\" && record.what === \"keepalive\",\n );\n const retransmitted = socket.filter((record) => record.ev === \"retrans\");\n /**\n * This function examines the timer that drives the measurement: keepalive or retransmit. It\n * finds the first firing in an interval that was two jiffies or more behind its wheel slot. The\n * wheel does a slot in the tick when it is due, thus such a delay comes from the CPU: the host\n * stopped a vCPU. The prediction is about the timing of the kernel, and that firing measured\n * the timing of the host. One jiffy is a tick that occurred while the wheel worked, and δ_abs\n * includes it. The function ignores the other timer: the retransmit timer fires one time, idle,\n * after a reply, and it does not move a keepalive.\n */\n const delayed = (from: number, to: number, timer: \"keepalive\" | \"retransmit\"): string | null => {\n const firings = socket.filter(\n (record) =>\n record.ev === \"timer\" && record.what === timer && record.mono >= from && record.mono <= to,\n );\n const late = firings.find((record) => (record.behind?.value ?? 0) >= 2);\n if (late !== undefined)\n return `its ${timer} timer ran ${late.behind!.value} jiffies behind its wheel slot: the VM delayed it, not the kernel`;\n // a keepalive timer that finds its socket held by its owner sends nothing, and it tries again\n // HZ/20 later (tcp_keepalive_timer). The retransmit timer only gives its work to the release\n // by the owner, some microseconds later\n const held = firings.find((record) => timer === \"keepalive\" && record.owned === true);\n return held === undefined\n ? null\n : \"its keepalive timer found the socket held by its owner and put itself off by HZ/20: the run measured the owner, not the timer\";\n };\n const acks =\n request === undefined\n ? 0\n : socket.filter((record) => record.ev === \"rx\" && record.mono > request.t).length;\n // the first round trip of the observer: the smoothed round trip in its first record, the sample of the handshake\n const srtt = socket.find((record) => record.tcp?.srtt !== undefined)?.tcp?.srtt;\n const rtt = srtt === undefined ? null : quantity(valueIn(srtt, \"us\") / 1000, \"ms\");\n const base = {\n environment: run.environment,\n index: run.index,\n probes: keepalives.length,\n retransmissions: retransmitted.length,\n acks,\n rtt,\n };\n const nothing = {\n outcome: { kind: \"silent\" } as const,\n elapsed: null,\n socket: null,\n noticed: null,\n steps: [],\n finding: null,\n };\n\n if (ready === undefined) return { ...base, ...nothing, void: \"the client never had its reply\" };\n if (request === undefined)\n return { ...base, ...nothing, void: \"the request was never sent into the fault\" };\n if (run.lost === null)\n return {\n ...base,\n ...nothing,\n void: \"the prober wrote no clock line after the run, so nothing says what it dropped\",\n };\n if (run.lost > 0) return { ...base, ...nothing, void: `the prober dropped ${run.lost} records` };\n if (cookie === undefined)\n return { ...base, ...nothing, void: \"the prober saw nothing of this connection\" };\n let voided: string | null = null;\n if (run.fault !== null) {\n // the request of a client must go into the fault. The request of a cell on the server side must\n // reach the server first, and its reply must not reach the client before the fault holds. An\n // mtu-blackhole drops only the long reply, thus its request goes after the fault, as a client's does\n const replyFirst = server && inflight && cell.fault !== \"mtu-blackhole\";\n const replied = events.some(\n (event) => event.ev === \"data\" && event.t > request.t && event.t < run.fault!.t1,\n );\n if (inflight && !replyFirst && run.fault.t1 > request.t)\n voided = \"the request went out before the fault held\";\n else if (replyFirst && run.fault.t1 < request.t)\n voided = \"the request went out into the fault, so the server never had it\";\n else if (replyFirst && replied)\n voided = \"the server's reply arrived before the fault held\";\n else if (keepalives[0] !== undefined && keepalives[0].mono < run.fault.t1)\n voided = \"a keepalive probe went out before the fault held\";\n }\n // a band holds for the first round trips that its prediction assumes. Above them, the run measured another path\n const assumed = cell.prediction.detects === null ? undefined : cell.prediction.rtt;\n if (voided === null && assumed !== undefined && rtt !== null && valueIn(rtt, \"ms\") > valueIn(assumed.high, \"ms\"))\n voided = `rtt-over-assumption: its first round trip was ${rtt.value} ms, above the ${valueIn(assumed.high, \"ms\")} ms that the prediction assumes`;\n\n // the detection as the kernel reported it to the owner of the socket, and as the client saw it.\n // The process of a server or of a proxy tells nothing that the harness reads, thus its kernel tells all\n const remote = cell.observer !== undefined && cell.observer !== \"client\";\n const reported = socket.find((record) => record.ev === \"error\" && (record.err ?? 0) !== 0);\n const seen = remote\n ? undefined\n : events.find((event) => event.ev === \"error\" || event.ev === \"eof\");\n // an end of file without an error is the change of state that the FIN of the peer makes, from\n // ESTABLISHED to CLOSE_WAIT, as the kernel stamps it\n const closeWait = socket.find((record) => record.ev === \"state\" && newState(record) === 8);\n // a close of the observer itself, for a prediction that says it: from ESTABLISHED to FIN_WAIT1,\n // the FIN that its pool sends when it closes an idle connection\n const own =\n cell.prediction.detects === true && cell.prediction.errno === \"CLOSED\"\n ? socket.find((record) => record.ev === \"state\" && oldState(record) === 1 && newState(record) === 4)\n : undefined;\n const outcome: Outcome =\n reported !== undefined\n ? { kind: \"error\", errno: ERRNO[reported.err!] ?? `E${reported.err}` }\n : closeWait !== undefined || seen?.ev === \"eof\"\n ? { kind: \"eof\" }\n : own !== undefined\n ? { kind: \"closed\" }\n : seen !== undefined\n ? { kind: \"error\", errno: String(seen[\"errno\"]) }\n : { kind: \"silent\" };\n const ended = reported?.mono ?? closeWait?.mono ?? own?.mono ?? seen?.t;\n const noticed =\n reported !== undefined && seen !== undefined ? between(reported.mono, seen.t) : null;\n const { prediction } = cell;\n // keepalive counts from the last segment received before the fault. The budget of the give-up\n // counts from the first retransmission of the RTO timer, not from the tail loss probe before it.\n // A reset counts from the send of the client. The kernel has no record of that send, thus the\n // stamp is the stamp of the client. A toxic counts from the start of its commit\n const before = run.fault?.t1 ?? ready.t;\n const referenceAt = (from: Reference): number | undefined => {\n if (from === \"request-sent\") return request.t;\n if (from === \"fault\") return run.fault?.t0;\n if (from === \"client-start\") return (events.find((event) => event.ev === \"connecting\") ?? events[0])?.t;\n const record: KernelRecord | undefined =\n from === \"last-received\"\n ? socket.filter((each) => each.ev === \"rx\" && each.mono <= before).at(-1)\n : from === \"first-retransmission\"\n ? retransmitted.find((each) => each.why === \"rto\")\n : kernel.find(\n (each) =>\n each.netns === peerNetns &&\n each.cookie === peerCookie &&\n each.ev === \"state\" &&\n oldState(each) === 1,\n );\n return record?.mono;\n };\n // in a topology, the bytes after the request and the instant that a router forgot the flow\n const lastRx =\n kernel\n .filter((each) => each.netns === run.netns && each.src === local && each.ev === \"rx\" && each.mono <= before)\n .at(-1)?.mono ?? ready.t;\n const chain =\n run.topology === undefined\n ? {}\n : { bytes, forgot: forgotten === undefined ? null : between(lastRx, forgotten.t) };\n\n // a measurement-only cell: each request of the run and the time to its reply, and the time from\n // its reference to the end of the connection, or to the last data\n if (prediction.detects === null) {\n const sends = events.filter((event) => event.ev === \"sent\").slice(1);\n // a reply ends at its last byte when the client says so (`replied`), and at its first data otherwise\n const answers = events.some((event) => event.ev === \"replied\") ? \"replied\" : \"data\";\n const samples = sends.flatMap((send, n) => {\n const next = sends[n + 1]?.t ?? Infinity;\n const reply = events.find((event) => event.ev === answers && event.t > send.t && event.t < next);\n return reply === undefined ? [] : [between(send.t, reply.t)];\n });\n const from = prediction.from === undefined ? undefined : referenceAt(prediction.from);\n const until = ended ?? after.at(-1)?.t;\n // the data of the client comes from its peer: the receiver and the sender of each reply\n const peerRecords =\n peerCookie === undefined\n ? []\n : kernel.filter((record) => record.netns === peerNetns && record.cookie === peerCookie);\n const proxy = cell.calibrates === undefined ? undefined : run.topology?.hops[cell.calibrates];\n const crossings =\n cell.calibrates === undefined\n ? {}\n : {\n crossings: crossingsOf(\n run,\n cell.calibrates,\n proxy === undefined ? undefined : socketOf({ ...cell, observer: { hop: cell.calibrates, toward: \"client\" } }, run),\n proxy === undefined ? undefined : socketOf({ ...cell, observer: { hop: cell.calibrates, toward: \"target\" } }, run),\n ),\n };\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome: ended !== undefined ? outcome : { kind: \"answered\" },\n ...(from === undefined || until === undefined ? {} : { elapsed: between(from, until) }),\n noticed: prediction.from === undefined ? null : noticed,\n steps: retransmitted\n .filter((record) => record.mono > request.t)\n .map((record) => between(request.t, record.mono)),\n samples,\n reordering: reorderingOf(\n socket.filter((record) => record.mono > request.t),\n peerRecords.filter((record) => record.mono > request.t),\n ),\n ...crossings,\n void: voided,\n };\n }\n\n // a stall: nothing must detect it, and the connection must move again at the first\n // retransmission after it stops, timed from the send of the request\n if (!prediction.detects && prediction.recovers !== undefined) {\n const { recovers } = prediction;\n const heal = run.heal ?? null;\n if (voided === null && heal === null) voided = \"the stall never ended\";\n else if (voided === null && heal !== null) {\n const from = between(request.t, heal.t0);\n const to = between(request.t, heal.t1);\n if (\n valueIn(from, \"s\") <= valueIn(recovers.heal.low, \"s\") ||\n valueIn(to, \"s\") >= valueIn(recovers.heal.high, \"s\")\n )\n voided = `the stall ended ${from.value}–${to.value} s after the send, outside the ${recovers.heal.low.value}–${recovers.heal.high.value} s its prediction assumed`;\n }\n const steps = retransmitted\n .filter((record) => record.mono > request.t)\n .map((record) => between(request.t, record.mono));\n if (ended !== undefined)\n return {\n ...base,\n ...nothing,\n outcome,\n steps,\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where it should recover`,\n };\n // the first segment back after the stall stopped: the answer of the server to the\n // retransmission that got through, and the read of the reply by the client\n const answer =\n heal === null\n ? undefined\n : socket.find((record) => record.ev === \"rx\" && record.mono > heal.t0);\n if (answer === undefined)\n return {\n ...base,\n ...nothing,\n steps,\n void: voided,\n finding: \"it never moved again: nothing came back after the stall ended\",\n };\n const reply = events.find((event) => event.ev === \"data\" && event.t > request.t);\n voided ??= delayed(request.t, answer.mono, \"retransmit\");\n return {\n ...base,\n outcome: { kind: \"answered\" },\n elapsed: between(request.t, answer.mono),\n socket: reply === undefined ? null : between(request.t, reply.t),\n noticed: reply === undefined ? null : between(answer.mono, reply.t),\n steps,\n void: voided,\n finding: null,\n };\n }\n\n // a reply in a band: nothing must detect the fault, and the reply must come back in its time,\n // from the send of the request to the first segment that the socket receives after it\n if (!prediction.detects && prediction.replies !== undefined) {\n const window = valueIn(prediction.window, \"s\") * 1e9;\n if (ended !== undefined && ended - request.t <= window)\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n elapsed: between(request.t, ended),\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where the reply should come`,\n };\n // the first segment with data: a proxy can acknowledge the request first, with no data\n const answer = socket.find(\n (record) => record.ev === \"rx\" && record.mono > request.t && (record.seg?.len.value ?? 0) > 0,\n );\n const reply = after[0];\n if (answer === undefined)\n return { ...base, ...nothing, ...chain, void: voided, finding: \"the reply never came\" };\n return {\n ...base,\n ...chain,\n outcome: { kind: \"answered\" },\n elapsed: between(request.t, answer.mono),\n socket: reply === undefined ? null : between(request.t, reply.t),\n noticed: reply === undefined ? null : between(answer.mono, reply.t),\n steps: [],\n void: voided,\n finding: null,\n };\n }\n\n if (!prediction.detects) {\n // the test of silence covers the window from the send of the request\n const window = valueIn(prediction.window, \"s\") * 1e9;\n if (ended === undefined || ended - request.t > window) {\n // the router keeps the flow in the window, unless the prediction says that it forgets it,\n // and when. Either way, nothing detects it\n const forgetIn = forgotten !== undefined && forgotten.t - request.t <= window ? forgotten : undefined;\n const expected = prediction.middlebox?.forgets;\n const after = forgetIn === undefined ? null : between(lastRx, forgetIn.t);\n let finding: string | null = null;\n if (expected === undefined) {\n if (after !== null) finding = `a router forgot the flow ${after.value} s after the last segment, where it should keep it`;\n } else if (after === null) {\n finding = \"no router forgot the flow in the window, where it should forget it\";\n } else {\n const noise = valueIn(NOISE, \"s\");\n const low = valueIn(expected.low, \"s\") - noise;\n const high = valueIn(expected.high, \"s\") + noise;\n if (after.value < low || after.value > high)\n finding = `a router forgot the flow ${after.value} s after the last segment, outside [${Math.round(low * 1e6) / 1e6}, ${Math.round(high * 1e6) / 1e6}] s`;\n }\n return { ...base, ...nothing, ...chain, void: voided, finding };\n }\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n elapsed: between(request.t, ended),\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where nothing should`,\n };\n }\n\n if (ended === undefined)\n return {\n ...base,\n ...nothing,\n ...chain,\n void: voided,\n finding: `nothing detected it: ${prediction.timer} never fired`,\n };\n const reference = referenceAt(prediction.from);\n if (reference === undefined) {\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n noticed,\n void: voided,\n finding: `it ended without its reference event (${prediction.from})`,\n };\n }\n const steps = (\n prediction.from === \"last-received\"\n ? keepalives\n : prediction.from === \"first-retransmission\"\n ? retransmitted.filter((record) => record.why === \"rto\" && record.mono > reference)\n : []\n ).map((record) => between(reference, record.mono));\n const matches =\n prediction.errno === \"EOF\"\n ? outcome.kind === \"eof\"\n : prediction.errno === \"CLOSED\"\n ? outcome.kind === \"closed\"\n : outcome.kind === \"error\" && outcome.errno === prediction.errno;\n const finding = matches\n ? null\n : `it ended with ${outcome.kind === \"error\" ? outcome.errno : outcome.kind === \"closed\" ? \"its own close\" : \"end of file\"}, not ${prediction.errno}`;\n // the timer of an HTTP client or server is a timer of the program, not of the kernel: the kernel\n // timers of its socket do not drive the measurement\n const programTimer = cell.probe.http !== undefined && prediction.from === \"last-received\" && prediction.timer !== \"keepalive\";\n // a firing at the reference itself only moves the start of the interval. A reset meets the send\n // with no timer, a FIN arrives with none, and a toxic has its own clock\n if ((prediction.from === \"last-received\" && !programTimer) || prediction.from === \"first-retransmission\")\n voided ??= delayed(\n reference,\n ended,\n prediction.from === \"last-received\" ? \"keepalive\" : \"retransmit\",\n );\n // a timer of conntrack (expire_…) reaches its DESTROY through the listing of the router. A slow\n // listing between the expiry and the detection breaks the bound of its lag: the run measured the\n // pump, not the router. The expiry is at most the width of the band before the detection\n const lag = (valueIn(prediction.high, \"s\") - valueIn(prediction.low, \"s\")) * 1e9;\n const slow = prediction.timer.startsWith(\"expire_\")\n ? (run.middlebox ?? []).find(\n (line) =>\n line.ev === \"pump-slow\" &&\n line.t <= ended &&\n line.t + Number(line[\"took\"]) * 1e9 >= ended - lag,\n )\n : undefined;\n if (slow !== undefined)\n voided ??= `a listing of the router took ${Math.round(Number(slow[\"took\"]) * 1e4) / 10} ms, longer than the bound of its prediction`;\n // a toxic starts somewhere in the commit of its request. A commit longer than the noise of the\n // pipeline leaves its start unknown within δ_abs\n const commit = run.fault === null ? 0 : (run.fault.t1 - run.fault.t0) / 1e6;\n if (prediction.from === \"fault\" && commit > valueIn(NOISE, \"ms\"))\n voided ??= `the commit of the fault took ${Math.round(commit * 10) / 10} ms, longer than the noise of the pipeline: its start is not known`;\n return {\n ...base,\n ...chain,\n outcome,\n elapsed: between(reference, ended),\n socket: remote ? null : socketVantage(cell, events, ready, request),\n noticed,\n steps,\n void: voided,\n finding,\n };\n};\n","import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n realpathSync,\n rmSync,\n statSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { StartedNetwork, StartedTestContainer } from \"testcontainers\";\nimport type * as Testcontainers from \"testcontainers\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport { isHopPlace } from \"./cell.ts\";\nimport type {\n Client,\n ClientImage,\n FaultPlace,\n Hop,\n HopPlace,\n KernelRecord,\n MiddleboxEvent,\n ProbeEvent,\n ProxyHop,\n RunTopology,\n Target,\n Topology,\n Toxic,\n} from \"./cell.ts\";\n\n/**\n * These are the roots of the two packages that the images read: this package, and the prober\n * that Node resolves for it. In a workspace, they are two folders side by side. From npm, the\n * prober is a link, and a Docker build context does not follow a link. Thus each image builds\n * from a context that the harness copies from the two roots.\n */\nconst ROOTS: Readonly<Record<string, string>> = {\n harness: fileURLToPath(new URL(\"..\", import.meta.url)),\n prober: dirname(\n realpathSync(createRequire(import.meta.url).resolve(\"@mark1russell7/prober/package.json\")),\n ),\n};\n\n/** This function gives the path of a part of an image, which is `<package>/<path>` in the context. */\nconst pathOf = (part: string): string => {\n const [root, ...rest] = part.split(\"/\");\n const base = ROOTS[root!];\n if (base === undefined) throw new Error(`${part} is in no package that the images read`);\n return join(base, ...rest);\n};\n\n/** This function loads Testcontainers when a lab first opens. Code that only measures and judges does not load it. */\nexport const containers = (): Promise<typeof Testcontainers> => import(\"testcontainers\");\n\n/** An image is an image that the harness builds: its Dockerfile, and each file or folder that the Dockerfile reads. */\ninterface Image {\n readonly name: string;\n readonly dockerfile: string;\n readonly from: readonly string[];\n}\n\n/** This image holds the prober, the TCP client, the injector and the namespace admin. */\nconst HARNESS: Image = {\n name: \"loom-harness\",\n dockerfile: \"harness/image/Dockerfile\",\n from: [\n \"harness/image/Dockerfile\",\n \"harness/image/native\",\n \"prober/bpf\",\n \"prober/native\",\n \"prober/fixtures\",\n ],\n};\n/** This image holds the Node client: ioredis 6.0.0 on Node 24.21.0. */\nconst NODE: Image = {\n name: \"loom-harness-node\",\n dockerfile: \"harness/image/node/Dockerfile\",\n from: [\"harness/image/node\"],\n};\n/** This image holds the Go HTTP client: the http.Transport of Go 1.27.2. */\nconst GO_HTTP: Image = {\n name: \"loom-harness-go-http\",\n dockerfile: \"harness/image/go-http/Dockerfile\",\n from: [\"harness/image/go-http\"],\n};\n/** This image holds the Node HTTP clients: the http.Agent of Node 24.21.0, and undici 8.12.2. */\nconst NODE_HTTP: Image = {\n name: \"loom-harness-node-http\",\n dockerfile: \"harness/image/node-http/Dockerfile\",\n from: [\"harness/image/node-http\"],\n};\n/** This image holds the MongoDB client: the driver mongodb 7.7.0 on Node 24.21.0, and its step-down script. */\nconst MONGO: Image = {\n name: \"loom-harness-mongo\",\n dockerfile: \"harness/image/mongo/Dockerfile\",\n from: [\"harness/image/mongo\"],\n};\n/** This image is the router of a middlebox chain: NAT and conntrack, with a form for a forgotten flow. */\nconst ROUTER: Image = {\n name: \"loom-harness-router\",\n dockerfile: \"harness/image/router/Dockerfile\",\n from: [\"harness/image/router\"],\n};\n\n/** This function gives each file under a path, in a stable sequence, but not the output of a local build. */\nconst sources = (path: string): string[] => {\n if (statSync(path).isFile()) return [path];\n const entries = readdirSync(path, { withFileTypes: true });\n return entries\n .filter((entry) => entry.name !== \"target\" && entry.name !== \"node_modules\")\n .flatMap((entry) => sources(join(path, entry.name)))\n .sort();\n};\n\n/** This function gives each file of an image, with its path in the build context. */\nconst filesOf = (image: Image): Array<{ readonly file: string; readonly at: string }> =>\n image.from.flatMap((part) => {\n const base = pathOf(part);\n return sources(base).map((file) => ({\n file,\n at: join(part, relative(base, file)).replaceAll(\"\\\\\", \"/\"),\n }));\n });\n\n/** This function gives the tag of an image from its content. Thus a changed probe or client cannot operate as an old one. */\nconst imageTag = (image: Image): string => {\n const hash = createHash(\"sha256\");\n for (const { file, at } of filesOf(image)) {\n hash.update(at);\n hash.update(readFileSync(file));\n }\n return `${image.name}:${hash.digest(\"hex\").slice(0, 12)}`;\n};\n\n/** This function copies the files of an image into a new build context, and gives its path. */\nconst stage = (image: Image): string => {\n const context = mkdtempSync(join(tmpdir(), `${image.name}-`));\n for (const { file, at } of filesOf(image)) {\n mkdirSync(dirname(join(context, at)), { recursive: true });\n copyFileSync(file, join(context, at));\n }\n return context;\n};\n\nconst built = new Map<string, Promise<string>>();\n/** This function builds an image one time for each process. Docker keeps its layers between runs. */\nconst build = (image: Image): Promise<string> => {\n let tag = built.get(image.name);\n if (tag === undefined) {\n tag = (async () => {\n const name = imageTag(image);\n const { GenericContainer } = await containers();\n const context = stage(image);\n try {\n await GenericContainer.fromDockerfile(context, image.dockerfile).build(name, {\n deleteOnExit: false,\n });\n } finally {\n rmSync(context, { recursive: true, force: true });\n }\n return name;\n })();\n built.set(image.name, tag);\n }\n return tag;\n};\n\n/** This function gives the image of the harness. */\nexport const harnessImage = (): Promise<string> => build(HARNESS);\n/** This function gives the image of the Node client. */\nexport const nodeClientImage = (): Promise<string> => build(NODE);\n/** This function gives the image of the router. */\nexport const routerImage = (): Promise<string> => build(ROUTER);\n/** This function gives the image of the Go HTTP client. */\nexport const goHttpImage = (): Promise<string> => build(GO_HTTP);\n/** This function gives the image of the Node HTTP clients. */\nexport const nodeHttpImage = (): Promise<string> => build(NODE_HTTP);\n/** This function gives the image of the MongoDB client. */\nexport const mongoClientImage = (): Promise<string> => build(MONGO);\n\n/**\n * This time is the interval of the listing of a router, when its hop does not set one. A listing\n * evicts each expired flow that it walks past, and the DESTROY event of that flow follows.\n */\nexport const PUMP: Quantity = { value: 0.1, unit: \"s\" };\n/**\n * This time is the longest listing of the conntrack table of a router that a prediction assumes.\n * A DESTROY follows an expiry within the interval of the pump and two listings. On 2026-10-15,\n * the slowest of 500 listings in an idle router took 19.4 ms, and the median took 5.5 ms. A\n * longer listing writes a `pump-slow` line, and a run with one near its detection is void.\n */\nexport const LISTING: Quantity = { value: 20, unit: \"ms\" };\n\n/**\n * A network fault is a fault that the injector applies as a filter rule. It is a black hole (both\n * directions, or one), or a reset on the next segment. It can also be an ICMP unreachable for each\n * segment, or a drop of each segment that is longer than a length.\n */\nexport type NetworkFault = \"blackhole\" | \"rst\" | \"reject\" | \"mtu\";\n\n/** These are the options of a network fault: the direction of a black hole, the code of a reject, the length of an MTU drop. */\nexport interface FaultOptions {\n /** This direction is relative to the namespace of the rule: `in` drops what comes from the peer, `out` what goes to it. */\n readonly direction?: \"in\" | \"out\";\n readonly code?: \"host\" | \"net\" | \"port\" | \"admin\";\n /** This length is the longest segment in bytes that an MTU drop lets through. */\n readonly above?: number;\n}\n\n/** This function gives the arguments of the injector for the options of a fault. */\nexport const optionArgs = (options: FaultOptions): string[] => [\n ...(options.direction === undefined ? [] : [\"--direction\", options.direction]),\n ...(options.code === undefined ? [] : [\"--code\", options.code]),\n ...(options.above === undefined ? [] : [\"--above\", String(options.above)]),\n];\n\n/** These are the actions of the injector that lift each network fault. */\nexport const LIFT: Readonly<Record<NetworkFault, string>> = {\n blackhole: \"clear\",\n rst: \"clear-rst\",\n reject: \"clear-reject\",\n mtu: \"clear-mtu\",\n};\n\n/**\n * A fault side is the namespace that gets the rules of a fault: the namespace of the peer of the\n * observer. For the client, it is the namespace of the target (the injector). For the server, it\n * is the namespace of the client (its privileged admin). Rules in the namespace of the observer\n * make its sends fail locally, with EPERM, and the network does not lose them. Thus its\n * retransmissions do not go out. The retransmission tracepoint of the kernel then records none,\n * because it fires only for a retransmission that went out.\n */\nexport type FaultSide = \"target\" | \"client\";\n\n/** A hop of a manifest is one hop of a topology, as the lab started it. */\nexport interface ManifestHop {\n readonly name: string;\n readonly kind: \"router\" | \"proxy\";\n readonly image: string;\n readonly id: string;\n /** These are the settings of the hop: the form and the idle timeout of a router, the files and settings of a proxy. */\n readonly settings: Readonly<Record<string, string>>;\n /** These are the sysctls of the namespace of the hop, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n}\n\n/** A manifest is what a lab records about its machine, its images and its settings. */\nexport interface Manifest {\n readonly kernel: string;\n /** This text is the architecture of the machine, as uname -m gives it: x86_64. */\n readonly architecture: string;\n readonly hz: string | null;\n readonly boot_id: string;\n readonly docker: string;\n /** This record is the target, and its settings as it read them back. */\n readonly target: {\n readonly image: string;\n readonly id: string;\n readonly settings: Readonly<Record<string, string>>;\n /** These are the sysctls of its namespace, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n /** These are the files that went into the target, by their path, each with the sha256 of its content. */\n readonly files?: Readonly<Record<string, string>>;\n /** This line is the first line of a target that writes them: a server that tells its settings as it reads them. */\n readonly first?: ProbeEvent;\n };\n readonly harness: { readonly image: string; readonly id: string; readonly packages: string[] };\n /** This record is the client of each run: the TCP client of the harness, a driver, or an image that the cell gives. */\n readonly client: { readonly kind: Client | \"image\"; readonly image: string };\n /** These are the namespaces that the prober watched, by inode. */\n readonly netns: {\n readonly client: number;\n readonly target: number;\n readonly hops?: Readonly<Record<string, number>>;\n };\n /** These are the sysctls of the client namespace, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n /** These are the hops of a topology, in sequence from the client. */\n readonly hops?: readonly ManifestHop[];\n /** These are the parts of a group, with their settings as each part read them back. */\n readonly group?: {\n readonly members: ReadonlyArray<{ readonly name: string; readonly ip: string; readonly id: string; readonly settings: Readonly<Record<string, string>> }>;\n readonly sentinels: ReadonlyArray<{ readonly name: string; readonly ip: string; readonly id: string; readonly settings: Readonly<Record<string, string>> }>;\n readonly observer: { readonly image: string };\n };\n}\n\n/** These are the JSON lines of a program, as they arrive. */\nexport interface Lines<T extends { readonly ev: string }> {\n readonly seen: T[];\n /** These are the other lines that it wrote, for example an error. They explain a program that stopped its output. */\n readonly other: string[];\n consume: (stream: NodeJS.ReadableStream) => void;\n /** This method gives the first line of one of these kinds, or it rejects after `within` ms. */\n until(kinds: readonly string[], within: number, who: string): Promise<T>;\n}\n\n/** This function collects the JSON lines of a program. It gives each line to `also` too, when the caller gives it. */\nexport const lines = <T extends { readonly ev: string }>(also?: (line: T) => void): Lines<T> => {\n const seen: T[] = [];\n const other: string[] = [];\n const waiting: Array<{ kinds: readonly string[]; resolve: (line: T) => void }> = [];\n let partial = \"\";\n const take = (text: string) => {\n if (!text.startsWith(\"{\")) {\n if (text.length > 0) other.push(text);\n return;\n }\n const line = JSON.parse(text) as T;\n seen.push(line);\n also?.(line);\n for (const wait of waiting.splice(0)) {\n if (wait.kinds.includes(line.ev)) wait.resolve(line);\n else waiting.push(wait);\n }\n };\n return {\n seen,\n other,\n consume: (stream) =>\n stream.on(\"data\", (chunk: Buffer | string) => {\n partial += chunk.toString();\n const split = partial.split(\"\\n\");\n partial = split.pop() ?? \"\";\n for (const text of split) take(text.trim());\n }),\n until(kinds, within, who) {\n const found = seen.find((line) => kinds.includes(line.ev));\n if (found !== undefined) return Promise.resolve(found);\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const said = other.length === 0 ? \"\" : `; it wrote: ${other.slice(-3).join(\" | \")}`;\n reject(new Error(`no ${kinds.join(\" or \")} from the ${who} in ${within} ms${said}`));\n }, within);\n waiting.push({\n kinds,\n resolve: (line) => {\n clearTimeout(timer);\n resolve(line);\n },\n });\n });\n },\n };\n};\n\n/** A probe is the client container of one run. */\nexport interface Probe {\n /** This method gives the first event of one of these kinds, or it rejects after `within` ms. */\n until(kinds: readonly string[], within: number): Promise<ProbeEvent>;\n /** This method lets an in-flight probe send its request. */\n go(): Promise<void>;\n /** This method freezes the client with the cgroup freezer: its process stops, and its kernel continues to answer. */\n freeze(): Promise<void>;\n thaw(): Promise<void>;\n /** This method stops the process of the client with SIGKILL. Its kernel then closes its socket with a FIN, or a reset. */\n kill(): Promise<void>;\n readonly events: readonly ProbeEvent[];\n stop(): Promise<void>;\n}\n\n/** An agent is a container that a lab starts in the namespaces of the target, for example a Node process that signals the server. */\nexport interface Agent {\n /** These are the JSON lines that it wrote, as they arrive. */\n readonly lines: Lines<{ readonly ev: string }>;\n /** This method starts a command in the agent, and gives its standard output. */\n exec(argv: readonly string[]): Promise<string>;\n stop(): Promise<void>;\n}\n\n/** A lab is one environment: the target, the client, the hops of a topology, the injectors and the prober. */\nexport interface Lab {\n /** This address is the address that the client connects to: the target, or the first proxy of a topology. */\n readonly targetIp: string;\n /** This port is the port that the client connects to. */\n readonly targetPort: number;\n readonly clientIp: string;\n readonly netns: {\n readonly client: number;\n readonly target: number;\n readonly hops?: Readonly<Record<string, number>>;\n };\n /** This record is where each socket of a topology is, or null for a direct pair. */\n readonly topology: RunTopology | null;\n /** These are the lines of the routers of a topology, in sequence, each with the name of its hop. A direct pair has none. */\n readonly middlebox: readonly MiddleboxEvent[];\n /** These are the lines of a target that writes them (`Target.lines`), in sequence. Another target has none. */\n readonly server: readonly ProbeEvent[];\n /** This method adds a toxic to the Toxiproxy hop of a topology, or removes it with null. The stamps of the injector bracket it. */\n toxic(toxic: Toxic | null): Promise<{ t0: number; t1: number }>;\n /** This method makes the target close the connection of each client, with the command of the target. */\n serverClose(): Promise<void>;\n /**\n * This method starts an agent in the network namespace of the target, and in its process\n * namespace with `pid`. Its command keeps it up, for example `sleep infinity`.\n */\n agent(image: string, command: readonly string[], options?: { readonly pid?: boolean }): Promise<Agent>;\n /** These are all the records that the prober wrote, in sequence. */\n readonly kernel: readonly KernelRecord[];\n /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */\n sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method sets the settings of the target in it before a client connects, and reads them back. */\n configure(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method sets the sysctls of the target namespace through a privileged sidecar there, and reads them back. */\n targetSysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /**\n * This method applies a fault between the client and the target, by default from the namespace\n * of the target. The fault is a black hole, or a reset that answers the next segment of the\n * client. At a proxy of a topology, the rules face the peer of the proxy on one side. The stamps\n * of the injector bracket it.\n */\n fault(\n kind: NetworkFault,\n at?: FaultPlace,\n options?: FaultOptions,\n ): Promise<{ t0: number; t1: number }>;\n /** This method removes a fault. */\n lift(kind: NetworkFault, at?: FaultPlace, options?: FaultOptions): Promise<void>;\n /**\n * This method puts tc netem on the egress of a namespace, for example \"delay 20ms\" or \"loss 5%\",\n * or removes it with null. At a hop, it is the egress of the device of the hop toward one side.\n * The stamps of the injector bracket it.\n */\n netem(spec: string | null, at?: FaultPlace): Promise<{ t0: number; t1: number }>;\n /**\n * This method sets the TSO, GSO and GRO of a namespace. With them off, netem and a length match\n * act on segments, not on GSO super-packets of up to 64 KB. At a hop, it sets the two devices of the hop.\n */\n offload(on: boolean, at?: FaultPlace): Promise<void>;\n /** This method stops the server process in the target with SIGKILL, with the command of the target. */\n kill(): Promise<void>;\n /**\n * This method resolves the neighbors of the path in the two directions, with a ping from each\n * namespace. Without it, the first connection of an environment waits for ARP. Under a netem\n * delay, that wait is one more delayed round, and its round trip and RTO are longer.\n */\n warm(): Promise<void>;\n /** This method runs commands in the target before a client connects, each as argv. */\n setup(commands: ReadonlyArray<readonly string[]>): Promise<void>;\n /**\n * This method stops a black hole at an instant on the monotonic clock of the VM. The injector\n * waits for that instant in the VM, thus the length of a stall does not include the latency of\n * a docker exec. Stamps bracket it, as for a fault.\n */\n heal(at: FaultPlace, when: number): Promise<{ t0: number; t1: number }>;\n /**\n * This method removes what the two kernels learned from their earlier connections. These are\n * the TCP metrics that each kernel keeps for a peer address when a connection closes. These metrics set the\n * first round trip of the next connection, and thus its RTO. A run with these metrics is not\n * independent of the run before it.\n */\n forget(): Promise<void>;\n /** This method freezes the target with the cgroup freezer: its process stops, and its kernel continues to answer. */\n freeze(): Promise<void>;\n thaw(): Promise<void>;\n /**\n * This method sends a signal to the server process of the target (`Target.program`), at once or\n * at an instant of the VM. A sidecar in the network and process namespaces of the target sends\n * it, and the injector stamps it. The lab starts that sidecar at the first signal.\n */\n signal(signal: \"STOP\" | \"CONT\" | \"KILL\", at?: number): Promise<{ t0: number; t1: number }>;\n probe(args: readonly string[]): Promise<Probe>;\n /**\n * This method waits for the next clock line of the prober, which gives the count of lost\n * records. It gives null when no clock line comes in 5 s, because then nothing tells the count.\n */\n settle(): Promise<number | null>;\n manifest(\n sysctls: Readonly<Record<string, string>>,\n settings?: Readonly<Record<string, string>>,\n targetSysctls?: Readonly<Record<string, string>>,\n ): Promise<Manifest>;\n close(): Promise<void>;\n}\n\n/** This function makes a container share the process namespace of another container, for example `container:<id>`. */\nexport const withPid = <T extends Testcontainers.GenericContainer>(container: T, mode: string): T => {\n (container as unknown as { hostConfig: { PidMode?: string } }).hostConfig.PidMode = mode;\n return container;\n};\n\n/** This container class can share the process namespace of another container. */\nconst shared = (Base: typeof Testcontainers.GenericContainer) =>\n class extends Base {\n withPidMode(mode: string): this {\n (this as unknown as { hostConfig: { PidMode?: string } }).hostConfig.PidMode = mode;\n return this;\n }\n };\n\n/** This function gives the content of a file of a proxy, with the address of the next hop for each {{next}}. */\nexport const templated = (content: string, next: string): string => content.replaceAll(\"{{next}}\", next);\n\n/** This function gives the JSON body of a toxic for the API of Toxiproxy. Each toxic of a cell has the name `fault`. */\nexport const toxicBody = (toxic: Toxic): string =>\n JSON.stringify({\n name: \"fault\",\n type: toxic.type,\n stream: toxic.stream,\n toxicity: 1,\n attributes: toxic.attributes,\n });\n\n/**\n * This function gives the routes of a chain: for each router, the networks after it, which the\n * element before it reaches through it. Element 0 is the client, and element k is hop k - 1. Network\n * j is the network between element j and element j + 1.\n */\nexport const routesOf = (\n hops: readonly Pick<Hop, \"kind\">[],\n): Array<{ readonly from: number; readonly via: number; readonly networks: readonly number[] }> =>\n hops.flatMap((hop, at) =>\n hop.kind === \"router\"\n ? [\n {\n from: at,\n via: at + 1,\n networks: Array.from({ length: hops.length - at }, (_, k) => at + 1 + k),\n },\n ]\n : [],\n );\n\n/**\n * This function gives the endpoint that an element connects to: the first element after it that\n * is not a router. Element 0 is the client, element k is hop k - 1, and the last element is the target.\n */\nexport const endpointAfter = (hops: readonly Pick<Hop, \"kind\">[], element: number): number => {\n let at = element + 1;\n while (at <= hops.length && hops[at - 1]!.kind === \"router\") at += 1;\n return at;\n};\n\n// each container here is a holder, a probe or the prober: nothing drains, thus the stop is immediate\nexport const STOP = { timeout: 0 };\n\nexport const output = async (container: StartedTestContainer, argv: string[]): Promise<string> => {\n const result = await container.exec(argv);\n if (result.exitCode !== 0)\n throw new Error(`${argv.join(\" \")} exited ${result.exitCode}: ${result.output.trim()}`);\n return result.stdout.trim();\n};\n\n/** This function gives the inode of the network namespace of a container: net:[4026532583]. */\nexport const netnsOf = async (container: StartedTestContainer): Promise<number> => {\n const link = await output(container, [\"readlink\", \"/proc/self/ns/net\"]);\n const inode = /^net:\\[(\\d+)\\]$/.exec(link)?.[1];\n if (inode === undefined) throw new Error(`not a network namespace: ${link}`);\n return Number(inode);\n};\n\n/** This function gives the image and the command of the client of each run, and its kind for the manifest. */\nexport const clientOf = async (\n kind: Client | ClientImage,\n image: string,\n): Promise<{ clientImage: string; command: readonly string[]; clientKind: Client | \"image\" }> => {\n if (typeof kind === \"object\") return { clientImage: kind.image, command: kind.command, clientKind: \"image\" };\n if (kind === \"ioredis\")\n return {\n clientImage: await nodeClientImage(),\n command: [\"node\", \"/opt/harness/node/client-ioredis.mjs\"],\n clientKind: kind,\n };\n if (kind === \"go-http\") return { clientImage: await goHttpImage(), command: [\"/opt/harness/client-go\"], clientKind: kind };\n if (kind === \"http-agent\" || kind === \"undici\")\n return {\n clientImage: await nodeHttpImage(),\n command: [\"node\", `/opt/harness/node-http/client-${kind === \"undici\" ? \"undici\" : \"agent\"}.mjs`],\n clientKind: kind,\n };\n if (kind === \"mongodb\")\n return {\n clientImage: await mongoClientImage(),\n command: [\"node\", \"/opt/harness/mongo/client-mongo.mjs\"],\n clientKind: kind,\n };\n return { clientImage: image, command: [\"/opt/harness/client\"], clientKind: kind };\n};\n\n/** These are the containers of one hop of a lab, and the addresses that its rules and the measurement read. */\ninterface HopIn {\n readonly hop: Hop;\n /** This container is the hop itself: the router, or the stock proxy. */\n readonly box: StartedTestContainer;\n /** This sidecar shares the namespace of the hop: the injector of its faults and its toxics. */\n readonly injector: StartedTestContainer;\n /** This record is the address of the hop on the network of each of its sides. */\n readonly ip: { readonly client: string; readonly target: string };\n /** These are the peers of the hop: an address on the side of the client, and an address with its port toward the target. */\n readonly peer: { readonly client: string; readonly target: string };\n /** These are the devices of the hop toward each side, as its routes give them. The rules of a router and netem at a hop face one. */\n readonly devices: { readonly client: string; readonly target: string };\n readonly netns: number;\n sysctls: Record<string, string>;\n}\n\n/** This function gives the device of a route from its line in `ip -o route get`: \"10.0.0.2 dev eth0 src …\". */\nexport const deviceOf = (route: string): string => {\n const device = /\\bdev (\\S+)/.exec(route)?.[1];\n if (device === undefined) throw new Error(`no device in the route: ${route}`);\n return device;\n};\n\n/**\n * This function opens one environment:\n * - a chain of new networks, and the stock target on the last one. A direct pair has one network.\n * - a client that holds its own network namespace, with a privileged sidecar in it for its\n * sysctls and its routes. Docker Desktop keeps /proc/sys read-only in a container that operates\n * without privileges.\n * - each hop of the topology, on the network of each of its sides. A hop is a router, or a stock\n * proxy with an injector sidecar in its namespace.\n * - an injector sidecar in the namespace of the target.\n * - the prober, a privileged container that loads the kernel program and watches the namespace of\n * the client, of the target and of each hop.\n *\n * The client of each run is its own container in the namespace of the client. It is the TCP\n * client of the harness, a driver from its own image (ioredis on Node), or an image that the\n * caller gives. Each prints the same lines.\n *\n * The client reaches the networks after a router through that router, by a route that its\n * privileged sidecar adds. A router translates the address of the side of the client, thus the\n * target and each later hop see the router as the peer.\n */\nexport const openLab = async (\n target: Target,\n kind: Client | ClientImage = \"tcp\",\n topology?: Topology,\n): Promise<Lab> => {\n const image = await harnessImage();\n const { clientImage, command, clientKind } = await clientOf(kind, image);\n const hops = topology?.hops ?? [];\n const { GenericContainer, Network, Wait, getContainerRuntimeClient } = await containers();\n const runtime = await getContainerRuntimeClient();\n const opened: StartedTestContainer[] = [];\n const networks: StartedNetwork[] = [];\n // the lines of a target that writes them, on the clock of the VM\n const serverLines = lines<ProbeEvent>();\n const close = async () => {\n for (const container of opened.reverse()) await container.stop(STOP).catch(() => undefined);\n for (const network of networks.reverse()) await network.stop().catch(() => undefined);\n };\n /** This function gives the address of a container on a network, as Docker has it at this time. A container can join a network after its start. */\n const ipOn = async (container: StartedTestContainer, network: StartedNetwork): Promise<string> => {\n const info = await runtime.container.inspect(runtime.container.getById(container.getId()));\n const ip = info.NetworkSettings.Networks[network.getName()]?.IPAddress;\n if (ip === undefined || ip === \"\") throw new Error(`${container.getName()} has no address on ${network.getName()}`);\n return ip;\n };\n const join = (container: StartedTestContainer, network: StartedNetwork) =>\n runtime.container.connectToNetwork(\n runtime.container.getById(container.getId()),\n runtime.network.getById(network.getId()),\n [],\n );\n const subnetOf = async (network: StartedNetwork): Promise<string> => {\n const info = await runtime.network.getById(network.getId()).inspect();\n const subnet = (info as { IPAM?: { Config?: Array<{ Subnet?: string }> } }).IPAM?.Config?.[0]?.Subnet;\n if (subnet === undefined) throw new Error(`${network.getName()} has no subnet`);\n return subnet;\n };\n try {\n // network j joins element j and element j + 1: 0 is the client, i + 1 is hop i, the last is the target\n for (let at = 0; at <= hops.length; at += 1) networks.push(await new Network().start());\n const last = networks[hops.length]!;\n let stock = new GenericContainer(target.image)\n .withNetwork(last)\n .withWaitStrategy(Wait.forLogMessage(target.ready));\n // the files of the target go in before it starts, for example the script of a server on a stock image\n if (target.files !== undefined)\n stock = stock.withCopyContentToContainer(\n Object.entries(target.files).map(([path, content]) => ({ content, target: path })),\n );\n if (target.lines === true) stock = stock.withLogConsumer(serverLines.consume);\n // a holder command keeps the container up when a fin fault kills the server process\n const server = await (\n target.command === undefined ? stock : stock.withCommand([...target.command])\n ).start();\n opened.push(server);\n const serverIp = await ipOn(server, last);\n\n // the hops, from the target to the client: each proxy gets the address of the endpoint after it\n const middlebox: MiddleboxEvent[] = [];\n const inChain: HopIn[] = [];\n const endpointOf = (element: number): string => {\n const at = endpointAfter(hops, element);\n if (at === hops.length + 1) return `${serverIp}:${target.port}`;\n const proxy = inChain[at - 1]!;\n return `${proxy.ip.client}:${(proxy.hop as ProxyHop).port}`;\n };\n for (let at = hops.length - 1; at >= 0; at -= 1) {\n const hop = hops[at]!;\n const [clientSide, targetSide] = [networks[at]!, networks[at + 1]!];\n if (hop.kind === \"router\") {\n const seen = lines<MiddleboxEvent>((line) => middlebox.push({ ...line, hop: hop.name }));\n // the router is privileged: it sets the sysctls of its own namespace, conntrack's among them\n const box = await new GenericContainer(await routerImage())\n .withNetwork(clientSide)\n .withPrivilegedMode()\n .withEnvironment({\n FORM: hop.form,\n IDLE: String(valueIn(hop.idle, \"s\")),\n PUMP: String(valueIn(hop.pump ?? PUMP, \"s\")),\n SLOW: String(valueIn(LISTING, \"s\")),\n })\n .withLogConsumer(seen.consume)\n .start();\n opened.push(box);\n // the router waits for the network of the side of the target, its eth1\n await join(box, targetSide);\n const ready = await seen.until([\"mbx-ready\"], 30_000, `router ${hop.name}`);\n await seen.until(hop.form === \"reset-both\" ? [\"resetter\"] : [\"ct-watch\"], 30_000, `router ${hop.name}`);\n // the injector of a fault at the router: FORWARD rules and netem in its namespace\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${box.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n inChain[at] = {\n hop,\n box,\n injector,\n ip: { client: await ipOn(box, clientSide), target: await ipOn(box, targetSide) },\n peer: { client: \"\", target: endpointOf(at + 1) },\n devices: { client: \"\", target: \"\" },\n netns: await netnsOf(box),\n sysctls: { \"net.netfilter.nf_conntrack_tcp_timeout_established\": String(ready[\"idle\"]) },\n };\n continue;\n }\n const next = endpointOf(at + 1);\n let proxy = new GenericContainer(hop.image).withNetwork(targetSide);\n if (hop.command !== undefined) proxy = proxy.withCommand([...hop.command]);\n if (hop.files !== undefined)\n proxy = proxy.withCopyContentToContainer(\n Object.entries(hop.files).map(([path, content]) => ({ content: templated(content, next), target: path })),\n );\n if (hop.ready !== undefined) proxy = proxy.withWaitStrategy(Wait.forLogMessage(hop.ready));\n const box = await proxy.start();\n opened.push(box);\n await join(box, clientSide);\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${box.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n inChain[at] = {\n hop,\n box,\n injector,\n ip: { client: await ipOn(box, clientSide), target: await ipOn(box, targetSide) },\n peer: { client: \"\", target: next },\n devices: { client: \"\", target: \"\" },\n netns: await netnsOf(injector),\n sysctls: {},\n };\n }\n\n const client = await new GenericContainer(image).withNetwork(networks[0]!).start();\n opened.push(client);\n const admin = await new GenericContainer(image)\n .withNetworkMode(`container:${client.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(admin);\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${server.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n const clientIp = await ipOn(client, networks[0]!);\n // the peer of each hop on the side of the client: the element before it, on the network between them\n for (let at = 0; at < inChain.length; at += 1) {\n const before = at === 0 ? clientIp : inChain[at - 1]!.ip.target;\n inChain[at] = { ...inChain[at]!, peer: { ...inChain[at]!.peer, client: before } };\n }\n\n // a privileged sidecar for each proxy that needs one, for its sysctls and its routes\n const admins = new Map<number, StartedTestContainer>();\n const adminOf = async (element: number): Promise<StartedTestContainer> => {\n if (element === 0) return admin;\n const hop = inChain[element - 1]!;\n if (hop.hop.kind === \"router\") return hop.box;\n let found = admins.get(element);\n if (found === undefined) {\n found = await new GenericContainer(image)\n .withNetworkMode(`container:${hop.box.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(found);\n admins.set(element, found);\n }\n return found;\n };\n for (const route of routesOf(hops)) {\n const where = await adminOf(route.from);\n const via = inChain[route.via - 1]!.ip.client;\n for (const network of route.networks)\n await output(where, [\"ip\", \"route\", \"replace\", await subnetOf(networks[network]!), \"via\", via]);\n }\n for (let at = 0; at < inChain.length; at += 1) {\n const hop = inChain[at]!;\n // the device toward each side, as the routes of the hop give it\n const toward = async (address: string) =>\n deviceOf(await output(hop.injector, [\"ip\", \"-o\", \"route\", \"get\", address.split(\":\")[0]!]));\n inChain[at] = { ...hop, devices: { client: await toward(hop.peer.client), target: await toward(hop.peer.target) } };\n if (hop.hop.sysctls === undefined) continue;\n // a router is privileged and sets its own sysctls. A proxy has a privileged sidecar for them\n const where = await adminOf(at + 1);\n const settings = Object.entries(hop.hop.sysctls).map(([key, value]) => `${key}=${value}`);\n await output(where, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n for (const key of Object.keys(hop.hop.sysctls)) hop.sysctls[key] = await output(where, [\"sysctl\", \"-n\", key]);\n }\n\n const netns = {\n client: await netnsOf(admin),\n target: await netnsOf(injector),\n ...(inChain.length === 0\n ? {}\n : { hops: Object.fromEntries(inChain.map((hop) => [hop.hop.name, hop.netns])) }),\n };\n const records = lines<KernelRecord>();\n const prober = await new GenericContainer(image)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\n \"/opt/harness/prober\",\n String(netns.client),\n String(netns.target),\n ...inChain.map((hop) => String(hop.netns)),\n ])\n .withLogConsumer(records.consume)\n .start();\n opened.push(prober);\n const begun = await records.until([\"begin\", \"fail\"], 30_000, \"prober\");\n if (begun.ev === \"fail\") throw new Error(`the prober failed: ${String(begun[\"why\"])}`);\n const first = endpointAfter(hops, 0);\n const targetIp = first === hops.length + 1 ? serverIp : inChain[first - 1]!.ip.client;\n const targetPort = first === hops.length + 1 ? target.port : (inChain[first - 1]!.hop as ProxyHop).port;\n const targetPeer = inChain.length === 0 ? clientIp : inChain.at(-1)!.ip.target;\n const placed: RunTopology | null =\n inChain.length === 0\n ? null\n : {\n targetPeer,\n hops: Object.fromEntries(\n inChain.map((hop) => [hop.hop.name, { netns: hop.netns, client: hop.peer.client, target: hop.peer.target }]),\n ),\n };\n const paused = runtime.container.getById(server.getId());\n let targetAdmin: StartedTestContainer | undefined;\n let signaller: StartedTestContainer | undefined;\n const hopNamed = (name: string): HopIn => {\n const found = inChain.find((hop) => hop.hop.name === name);\n if (found === undefined) throw new Error(`the topology has no hop ${name}`);\n return found;\n };\n /**\n * This function gives the namespace and the peer of the rules of a fault at a place, and the\n * other arguments of the injector. At a proxy, the rules are INPUT and OUTPUT rules, which act\n * on its sockets. A router forwards past INPUT and OUTPUT, thus its rules are FORWARD rules on\n * its device toward the side (`--via`).\n */\n const hopPlace = (at: FaultPlace): HopPlace => {\n if (!isHopPlace(at)) throw new Error(`a lab of a topology has no part ${JSON.stringify(at)}: a group cell opens a group lab`);\n return at;\n };\n const placeOf = (place: FaultPlace): [StartedTestContainer, string, string[]] => {\n if (place === \"client\") return [admin, targetIp, []];\n if (place === \"target\") return [injector, targetPeer, []];\n const at = hopPlace(place);\n const hop = hopNamed(at.hop);\n const peer = at.toward === \"client\" ? hop.peer.client : hop.peer.target.split(\":\")[0]!;\n const via = hop.hop.kind === \"router\" ? [\"--via\", hop.devices[at.toward]] : [];\n return [hop.injector, peer, via];\n };\n /** This function gives the namespace and the device of netem at a place: an end, or the device of a hop toward a side. */\n const deviceAt = (place: FaultPlace): [StartedTestContainer, string] => {\n if (place === \"client\") return [admin, \"eth0\"];\n if (place === \"target\") return [injector, \"eth0\"];\n const at = hopPlace(place);\n const hop = hopNamed(at.hop);\n return [hop.injector, hop.devices[at.toward]];\n };\n const toxiproxy = (): HopIn & { hop: ProxyHop & { api: { port: number; proxy: string } } } => {\n const found = inChain.find((hop) => hop.hop.kind === \"proxy\" && hop.hop.api !== undefined);\n if (found === undefined) throw new Error(\"the topology has no hop with the API of Toxiproxy\");\n return found as HopIn & { hop: ProxyHop & { api: { port: number; proxy: string } } };\n };\n const stamped = async (where: StartedTestContainer, argv: string[]) => {\n const stamp = JSON.parse((await output(where, argv)).split(\"\\n\").at(-1)!) as { t0: number; t1: number };\n return { t0: stamp.t0, t1: stamp.t1 };\n };\n const proxies = inChain.filter((hop) => hop.hop.kind === \"proxy\");\n\n return {\n targetIp,\n targetPort,\n clientIp,\n netns,\n topology: placed,\n middlebox,\n server: serverLines.seen,\n kernel: records.seen,\n async sysctl(values) {\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n if (settings.length > 0) await output(admin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n const read: Record<string, string> = {};\n for (const key of Object.keys(values))\n read[key] = await output(admin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async targetSysctl(values) {\n const read: Record<string, string> = {};\n if (Object.keys(values).length === 0) return read;\n // privileged, and only when a cell asks for it: otherwise the namespace of the target stays as it is\n targetAdmin ??= await new GenericContainer(image)\n .withNetworkMode(`container:${server.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(targetAdmin);\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n await output(targetAdmin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n for (const key of Object.keys(values))\n read[key] = await output(targetAdmin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async configure(values) {\n const read: Record<string, string> = {};\n if (Object.keys(values).length === 0) return read;\n const how = target.settings;\n if (how === undefined) throw new Error(`${target.image} names no way to set its settings`);\n for (const [name, value] of Object.entries(values)) {\n await output(server, [...how.set, name, value]);\n read[name] = (await output(server, [...how.get, name])).split(\"\\n\").at(-1) ?? \"\";\n }\n return read;\n },\n async fault(kind, at = \"target\", options = {}) {\n const [where, peer, via] = placeOf(at);\n return stamped(where, [\"/opt/harness/inject\", kind, peer, ...optionArgs(options), ...via]);\n },\n async lift(kind, at = \"target\", options = {}) {\n const [where, peer, via] = placeOf(at);\n await output(where, [\"/opt/harness/inject\", LIFT[kind], peer, ...optionArgs(options), ...via]);\n },\n async netem(spec, at = \"target\") {\n const [where, device] = deviceAt(at);\n const args =\n spec === null\n ? [\"/opt/harness/inject\", \"clear-netem\", device]\n : [\"/opt/harness/inject\", \"netem\", device, ...spec.split(/\\s+/)];\n return stamped(where, args);\n },\n async offload(on, at = \"target\") {\n // at a hop, the two devices: a router merges what it receives (GRO) and forwards it whole\n const devices = isHopPlace(at) ? Object.values(hopNamed(at.hop).devices) : [deviceAt(at)[1]];\n const [where] = deviceAt(at);\n for (const device of devices)\n await output(where, [\"/opt/harness/inject\", \"offload\", device, on ? \"on\" : \"off\"]);\n },\n async toxic(toxic) {\n const hop = toxiproxy();\n const api = `127.0.0.1:${hop.hop.api.port}`;\n const path = `/proxies/${hop.hop.api.proxy}/toxics`;\n return stamped(\n hop.injector,\n toxic === null\n ? [\"/opt/harness/inject\", \"http\", \"DELETE\", api, `${path}/fault`]\n : [\"/opt/harness/inject\", \"http\", \"POST\", api, path, toxicBody(toxic)],\n );\n },\n async serverClose() {\n if (target.close === undefined) throw new Error(`${target.image} names no way to close its clients`);\n await output(server, [...target.close]);\n },\n async agent(agentImage, argv, options = {}) {\n const Shared = shared(GenericContainer);\n const seen = lines<{ readonly ev: string }>();\n let started = new Shared(agentImage)\n .withNetworkMode(`container:${server.getId()}`)\n .withCommand([...argv])\n .withLogConsumer(seen.consume);\n if (options.pid === true) started = started.withPidMode(`container:${server.getId()}`);\n const container = await started.start();\n opened.push(container);\n return {\n lines: seen,\n exec: (args) => output(container, [...args]),\n stop: () =>\n container\n .stop(STOP)\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n async kill() {\n if (target.kill === undefined) throw new Error(`${target.image} names no way to kill its server`);\n await output(server, [...target.kill]);\n },\n async warm() {\n await output(admin, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", targetIp]);\n await output(injector, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", targetPeer]);\n for (const hop of proxies)\n await output(hop.injector, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", hop.peer.target.split(\":\")[0]!]);\n },\n async setup(commands) {\n for (const argv of commands) await output(server, [...argv]);\n },\n async heal(at, when) {\n const [where, peer, via] = placeOf(at);\n return stamped(where, [\"/opt/harness/inject\", \"clear\", peer, \"--at\", String(Math.round(when)), ...via]);\n },\n async forget() {\n await output(admin, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n await output(injector, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n for (const hop of proxies) await output(hop.injector, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n },\n freeze: () => paused.pause().then(() => undefined),\n thaw: () => paused.unpause().then(() => undefined),\n async signal(signal, at) {\n if (target.program === undefined) throw new Error(`${target.image} names no server process to signal`);\n // only when a cell asks for it: otherwise no sidecar shares the process namespace of the target\n signaller ??= await withPid(\n new GenericContainer(image).withNetworkMode(`container:${server.getId()}`).withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\"),\n `container:${server.getId()}`,\n ).start();\n if (!opened.includes(signaller)) opened.push(signaller);\n const when = at === undefined ? [] : [\"--at\", String(Math.round(at))];\n return stamped(signaller, [\"/opt/harness/inject\", \"signal\", target.program, signal, ...when]);\n },\n async probe(args) {\n const events = lines<ProbeEvent>();\n const container = await new GenericContainer(clientImage)\n .withNetworkMode(`container:${client.getId()}`)\n .withCommand([...command, ...args])\n .withLogConsumer(events.consume)\n .start();\n return {\n events: events.seen,\n until: (kinds, within) => events.until(kinds, within, \"client\"),\n go: () => output(container, [\"touch\", \"/tmp/go\"]).then(() => undefined),\n freeze: () =>\n runtime.container\n .getById(container.getId())\n .pause()\n .then(() => undefined),\n thaw: () =>\n runtime.container\n .getById(container.getId())\n .unpause()\n .then(() => undefined),\n kill: () =>\n runtime.container\n .getById(container.getId())\n .kill({ signal: \"SIGKILL\" })\n .then(() => undefined),\n // a killed client has no container to stop\n stop: () =>\n container\n .stop(STOP)\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n async settle() {\n // the prober drains its ring each 20 ms, and it writes a clock line each second\n const after = records.seen.length;\n const clock = await new Promise<KernelRecord | null>((resolve) => {\n const started = Date.now();\n const look = () => {\n const line = records.seen.slice(after).find((record) => record.ev === \"clock\");\n if (line !== undefined) resolve(line);\n else if (Date.now() - started > 5_000) resolve(null);\n else setTimeout(look, 50);\n };\n look();\n });\n return clock === null ? null : Number(clock[\"lost\"] ?? 0);\n },\n async manifest(sysctls, settings = {}, targetSysctls = {}) {\n const config = await output(admin, [\"zcat\", \"/proc/config.gz\"]).catch(() => \"\");\n const inspect = async (container: StartedTestContainer) =>\n runtime.container.inspect(runtime.container.getById(container.getId()));\n const hopsOf = async (): Promise<ManifestHop[]> => {\n const out: ManifestHop[] = [];\n for (const hop of inChain) {\n const box = hop.hop;\n out.push({\n name: box.name,\n kind: box.kind,\n image: box.kind === \"router\" ? await routerImage() : box.image,\n id: (await inspect(hop.box)).Image,\n settings:\n box.kind === \"router\"\n ? {\n form: box.form,\n idle: String(valueIn(box.idle, \"s\")),\n pump: String(valueIn(box.pump ?? PUMP, \"s\")),\n slow: String(valueIn(LISTING, \"s\")),\n packages: (await output(hop.box, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort().join(\" \"),\n }\n : { ...box.settings, ...box.files },\n sysctls: hop.sysctls,\n });\n }\n return out;\n };\n return {\n kernel: await output(client, [\"uname\", \"-r\"]),\n architecture: await output(client, [\"uname\", \"-m\"]),\n hz: /^CONFIG_HZ=(\\d+)$/m.exec(config)?.[1] ?? null,\n boot_id: await output(client, [\"cat\", \"/proc/sys/kernel/random/boot_id\"]),\n docker: `${runtime.info.containerRuntime.serverVersion} on ${runtime.info.containerRuntime.operatingSystem}`,\n target: {\n image: target.image,\n id: (await inspect(server)).Image,\n settings,\n sysctls: targetSysctls,\n ...(target.files === undefined\n ? {}\n : {\n files: Object.fromEntries(\n Object.entries(target.files).map(([path, content]) => [path, createHash(\"sha256\").update(content).digest(\"hex\")]),\n ),\n }),\n ...(serverLines.seen[0] === undefined ? {} : { first: serverLines.seen[0] }),\n },\n harness: {\n image,\n id: (await inspect(client)).Image,\n packages: (await output(client, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort(),\n },\n client: { kind: clientKind, image: clientImage },\n netns,\n sysctls,\n ...(inChain.length === 0 ? {} : { hops: await hopsOf() }),\n };\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n};\n","import type { StartedNetwork, StartedTestContainer } from \"testcontainers\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type {\n Cell,\n Group,\n GroupPlace,\n GroupRole,\n KernelRecord,\n ProbeEvent,\n Run,\n RunPart,\n SentinelEvent,\n} from \"./cell.ts\";\nimport type { Manifest } from \"./lab.ts\";\nimport { STOP, containers, harnessImage, lines, netnsOf, nodeClientImage, output, withPid } from \"./lab.ts\";\n\n/**\n * This module is the lab of a replicated group: a primary and its replicas, and the Sentinels\n * that decide which member is the primary. It opens one environment for a group cell, and it does\n * the runs of the cell in it. Each member and each Sentinel is a container from the stock image of\n * the group, in its own network namespace. An injector sidecar shares its network and process\n * namespaces. The client of each run is ioredis in Sentinel mode. An observer subscribes to the\n * events of each Sentinel, and the prober watches the namespace of the client.\n */\n\n/** A stamp is the time of the injector around one commit, on CLOCK_MONOTONIC in nanoseconds. */\nexport interface Stamp {\n readonly t0: number;\n readonly t1: number;\n}\n\n/** A group network fault is a black hole between a part and its peers, or a reset of each segment from them. */\nexport type GroupNetworkFault = \"blackhole\" | \"rst\";\n\n/** A group client is the client container of one run of a group cell. */\nexport interface GroupClient {\n readonly events: readonly ProbeEvent[];\n until(kinds: readonly string[], within: number): Promise<ProbeEvent>;\n stop(): Promise<void>;\n}\n\n/** A group lab is one environment of a group cell. */\nexport interface GroupLab {\n readonly group: Group;\n readonly clientIp: string;\n readonly observerIp: string;\n readonly members: Readonly<Record<string, RunPart>>;\n readonly sentinels: Readonly<Record<string, RunPart>>;\n /** These are the namespaces that the prober watches: the client, and the Sentinels for a calibration. */\n readonly netns: { readonly client: number; readonly watched: readonly number[] };\n /** These are all the records that the prober wrote, in sequence. */\n readonly kernel: readonly KernelRecord[];\n /** These are all the lines of the observer of the Sentinels, in sequence. */\n readonly timeline: readonly SentinelEvent[];\n /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */\n sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method waits until the group is whole and calm, and gives the name of its primary. */\n converge(within?: number): Promise<string>;\n /** This method removes each key from the group, and waits until each replica has the removal. */\n flush(primary: string): Promise<void>;\n /** This method makes each Sentinel forget the state of the group (`SENTINEL RESET`): an aborted failover then holds off no later failover. */\n reset(): Promise<void>;\n /** This method gives the sequence numbers that the group holds, from its primary. */\n holds(primary: string): Promise<number[]>;\n /** This method gives the part that has a role in the group, when `primary` is the primary. */\n partOf(role: GroupRole, primary: string): string;\n /** This method starts the client of a run, with the arguments of `client-sentinel.mjs`. */\n client(args: readonly string[]): Promise<GroupClient>;\n /** This method applies a network fault between a part and some of its peers, at once or at an instant of the VM. */\n fault(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace[\"cuts\"]>, at?: number): Promise<Stamp>;\n /** This method removes a network fault, at once or at an instant of the VM. */\n lift(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace[\"cuts\"]>, at?: number): Promise<Stamp>;\n /** This method sends a signal to the server process of a part, at once or at an instant of the VM. */\n signal(part: string, signal: \"KILL\" | \"STOP\" | \"CONT\", at?: number): Promise<Stamp>;\n /** This method starts the server of a member again, after a fin. */\n restart(member: string): Promise<void>;\n /** This method asks the first Sentinel for a failover of the group. */\n failover(coordinated: boolean): Promise<Stamp>;\n /** This method removes the TCP metrics of the client namespace: each run starts from a kernel that keeps nothing. */\n forget(): Promise<void>;\n /** This method waits for the next clock line of the prober, which gives the count of lost records. */\n settle(): Promise<number | null>;\n manifest(sysctls: Readonly<Record<string, string>>): Promise<Manifest>;\n close(): Promise<void>;\n}\n\n/** This function gives the configuration file of a member: its port, its settings, and the primary that a replica follows. */\nexport const memberConfig = (group: Group, primary: string | null): string =>\n [\n `port ${group.port}`,\n ...Object.entries(group.memberSettings).map(([key, value]) => `${key} ${value}`),\n ...(primary === null ? [] : [`replicaof ${primary} ${group.port}`]),\n \"\",\n ].join(\"\\n\");\n\n/** This function gives the configuration file of a Sentinel: its port, the group that it monitors, and the settings of the group. */\nexport const sentinelConfig = (group: Group, primary: string): string =>\n [\n `port ${group.sentinelPort}`,\n `sentinel monitor ${group.name} ${primary} ${group.port} ${group.quorum}`,\n ...Object.entries(group.sentinelSettings).map(([key, value]) => `sentinel ${key} ${group.name} ${value}`),\n \"\",\n ].join(\"\\n\");\n\n/**\n * This function gives the peers that a fault on a part cuts it from, by their addresses. `all` is\n * each other part and the client, `group` is the other parts, and `clients` is the client. The\n * observer is not a peer, thus the timeline of the group stays whole.\n */\nexport const peersOf = (\n parts: { readonly members: Readonly<Record<string, RunPart>>; readonly sentinels: Readonly<Record<string, RunPart>> },\n client: string,\n part: string,\n cuts: NonNullable<GroupPlace[\"cuts\"]>,\n): string[] => {\n const others = [...Object.entries(parts.members), ...Object.entries(parts.sentinels)]\n .filter(([name]) => name !== part)\n .map(([, at]) => at.ip);\n if (cuts === \"clients\") return [client];\n if (cuts === \"group\") return others;\n return [...others, client];\n};\n\n/** This function reads the flat reply of a command line client into its fields: name, value, name, value. */\nexport const fieldsOf = (text: string): Array<Record<string, string>> => {\n const out: Array<Record<string, string>> = [];\n const words = text.split(\"\\n\").map((line) => line.replace(/\\r$/, \"\"));\n let current: Record<string, string> | null = null;\n for (let at = 0; at + 1 < words.length; at += 2) {\n const [key, value] = [words[at]!, words[at + 1]!];\n // each entry of a list starts again at its name\n if (key === \"name\" || current === null) {\n current = {};\n out.push(current);\n }\n current[key] = value;\n }\n return out;\n};\n\n/** This function reads the INFO of a server into its fields. */\nexport const infoOf = (text: string): Record<string, string> => {\n const out: Record<string, string> = {};\n for (const line of text.split(\"\\n\")) {\n const at = line.indexOf(\":\");\n if (line.startsWith(\"#\") || at === -1) continue;\n out[line.slice(0, at)] = line.slice(at + 1).trim();\n }\n return out;\n};\n\n/**\n * A view of the group is what its parts tell at one time. `healthy` tells if each Sentinel agrees\n * on one primary with no flag but `master`, and knows each replica and each other Sentinel. Each\n * replica must follow that primary with its link up, and the primary must have each replica online,\n * with no failover in progress.\n */\nexport interface GroupView {\n readonly primary: string | null;\n readonly healthy: boolean;\n readonly why: string;\n}\n\n/** This function judges the views of the Sentinels and of the members: the primary that they agree on, if the group is healthy. */\nexport const viewOf = (\n group: Group,\n ips: Readonly<Record<string, string>>,\n sentinels: ReadonlyArray<{ readonly master: Record<string, string>; readonly replicas: ReadonlyArray<Record<string, string>> }>,\n members: Readonly<Record<string, Record<string, string>>>,\n): GroupView => {\n const others = String(group.sentinels.length - 1);\n const replicas = group.members.length - 1;\n const addresses = new Set(sentinels.map((each) => each.master[\"ip\"]));\n if (addresses.size !== 1) return { primary: null, healthy: false, why: `the Sentinels name ${addresses.size} primaries` };\n const ip = [...addresses][0]!;\n const primary = Object.entries(ips).find(([, at]) => at === ip)?.[0] ?? null;\n if (primary === null) return { primary: null, healthy: false, why: `no member has the address ${ip}` };\n for (const [at, each] of sentinels.entries()) {\n if (each.master[\"flags\"] !== \"master\") return { primary, healthy: false, why: `Sentinel ${at} flags the primary ${each.master[\"flags\"]}` };\n if (each.master[\"num-other-sentinels\"] !== others) return { primary, healthy: false, why: `Sentinel ${at} knows ${each.master[\"num-other-sentinels\"]} other Sentinels` };\n const good = each.replicas.filter(\n (replica) => replica[\"flags\"] === \"slave\" && replica[\"master-link-status\"] === \"ok\" && replica[\"master-host\"] === ip,\n );\n if (good.length !== replicas || each.replicas.length !== replicas)\n return { primary, healthy: false, why: `Sentinel ${at} has ${good.length} healthy replicas of ${each.replicas.length}` };\n }\n const info = members[primary] ?? {};\n if (info[\"role\"] !== \"master\") return { primary, healthy: false, why: `${primary} reports the role ${info[\"role\"]}` };\n if (info[\"master_failover_state\"] !== undefined && info[\"master_failover_state\"] !== \"no-failover\")\n return { primary, healthy: false, why: `${primary} is in the failover state ${info[\"master_failover_state\"]}` };\n const online = Object.entries(info).filter(([key, value]) => /^slave\\d+$/.test(key) && value.includes(\"state=online\")).length;\n if (online !== replicas) return { primary, healthy: false, why: `${primary} has ${online} replicas online` };\n for (const name of group.members) {\n if (name === primary) continue;\n const each = members[name] ?? {};\n if (each[\"role\"] !== \"slave\" || each[\"master_host\"] !== ip || each[\"master_link_status\"] !== \"up\")\n return { primary, healthy: false, why: `${name} is not a replica of ${primary} with its link up` };\n }\n return { primary, healthy: true, why: \"healthy\" };\n};\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** This time is the longest that a group takes to converge before a run, or after it. */\nexport const CONVERGE_MS = 240_000;\n\n/**\n * This function opens one environment of a group cell:\n * - one new network, with each member, each Sentinel, the client and the observer on it.\n * - each member: the stock image, its server as the child of a shell, thus a fin can stop the\n * server and the container stays up. The first member is the primary, and the others follow it.\n * - each Sentinel: the stock image, its Sentinel as the child of a shell, which monitors the first member.\n * - an injector sidecar for each part, in its network and process namespaces.\n * - a client that holds its own network namespace, with a privileged sidecar for its sysctls.\n * - the observer of the Sentinels, in a namespace of its own.\n * - the prober, which watches the namespace of the client, and of each Sentinel for a calibration.\n */\nexport const openGroupLab = async (group: Group, options: { readonly watchSentinels?: boolean } = {}): Promise<GroupLab> => {\n const image = await harnessImage();\n const node = await nodeClientImage();\n const { GenericContainer, Network, Wait, getContainerRuntimeClient } = await containers();\n const runtime = await getContainerRuntimeClient();\n const opened: StartedTestContainer[] = [];\n let network: StartedNetwork | undefined;\n const close = async () => {\n for (const container of opened.reverse()) await container.stop(STOP).catch(() => undefined);\n await network?.stop().catch(() => undefined);\n };\n const ipOf = async (container: StartedTestContainer): Promise<string> => {\n const info = await runtime.container.inspect(runtime.container.getById(container.getId()));\n const ip = info.NetworkSettings.Networks[network!.getName()]?.IPAddress;\n if (ip === undefined || ip === \"\") throw new Error(`${container.getName()} has no address on ${network!.getName()}`);\n return ip;\n };\n try {\n network = await new Network().start();\n const boxes = new Map<string, StartedTestContainer>();\n const injectors = new Map<string, StartedTestContainer>();\n const parts = { members: {} as Record<string, RunPart>, sentinels: {} as Record<string, RunPart> };\n /** This function starts one part: the stock image with its configuration, its program as the child of a shell, and its injector. */\n const start = async (name: string, program: string, file: string, content: string, ready: string) => {\n const box = await new GenericContainer(group.image)\n .withNetwork(network!)\n .withNetworkAliases(name)\n .withCopyContentToContainer([{ content, target: file }])\n .withCommand([\"sh\", \"-c\", `${program} ${file} & while :; do sleep 3600; done`])\n .withWaitStrategy(Wait.forLogMessage(ready))\n .start();\n opened.push(box);\n boxes.set(name, box);\n const injector = await withPid(\n new GenericContainer(image).withNetworkMode(`container:${box.getId()}`).withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\"),\n `container:${box.getId()}`,\n ).start();\n opened.push(injector);\n injectors.set(name, injector);\n return { ip: await ipOf(box), netns: await netnsOf(injector) };\n };\n const [first, ...rest] = group.members;\n if (first === undefined) throw new Error(\"a group has no member\");\n parts.members[first] = await start(first, group.programs.server, \"/data/server.conf\", memberConfig(group, null), group.ready.member);\n const primaryIp = parts.members[first].ip;\n for (const name of rest)\n parts.members[name] = await start(name, group.programs.server, \"/data/server.conf\", memberConfig(group, primaryIp), group.ready.member);\n for (const name of group.sentinels)\n parts.sentinels[name] = await start(name, group.programs.sentinel, \"/data/sentinel.conf\", sentinelConfig(group, primaryIp), group.ready.sentinel);\n\n const holder = await new GenericContainer(image).withNetwork(network).start();\n opened.push(holder);\n const admin = await new GenericContainer(image)\n .withNetworkMode(`container:${holder.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(admin);\n const clientIp = await ipOf(holder);\n\n const sentinelAddresses = group.sentinels.map((name) => `${parts.sentinels[name]!.ip}:${group.sentinelPort}`);\n const timeline = lines<SentinelEvent>();\n const observer = await new GenericContainer(node)\n .withNetwork(network)\n .withCommand([\"node\", \"/opt/harness/node/observer.mjs\", sentinelAddresses.join(\",\")])\n .withLogConsumer(timeline.consume)\n .start();\n opened.push(observer);\n const observerIp = await ipOf(observer);\n for (const address of sentinelAddresses)\n await new Promise<void>((resolve, reject) => {\n const started = Date.now();\n const look = () => {\n if (timeline.seen.some((line) => line.ev === \"subscribed\" && line[\"sentinel\"] === address)) resolve();\n else if (Date.now() - started > 30_000) reject(new Error(`the observer never subscribed to ${address}`));\n else setTimeout(look, 50);\n };\n look();\n });\n\n const clientNetns = await netnsOf(admin);\n const watched = [clientNetns, ...(options.watchSentinels === true ? group.sentinels.map((name) => parts.sentinels[name]!.netns) : [])];\n const records = lines<KernelRecord>();\n const prober = await new GenericContainer(image)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\"/opt/harness/prober\", ...watched.map(String)])\n .withLogConsumer(records.consume)\n .start();\n opened.push(prober);\n const begun = await records.until([\"begin\", \"fail\"], 30_000, \"prober\");\n if (begun.ev === \"fail\") throw new Error(`the prober failed: ${String(begun[\"why\"])}`);\n\n const box = (name: string): StartedTestContainer => {\n const found = boxes.get(name);\n if (found === undefined) throw new Error(`the group has no part ${name}`);\n return found;\n };\n const injectorOf = (name: string): StartedTestContainer => {\n const found = injectors.get(name);\n if (found === undefined) throw new Error(`the group has no part ${name}`);\n return found;\n };\n const cli = (name: string, args: readonly string[]): Promise<string> => {\n const port = group.sentinels.includes(name) ? group.sentinelPort : group.port;\n return output(box(name), [group.programs.cli, \"-p\", String(port), ...args]);\n };\n const stamped = async (where: StartedTestContainer, argv: string[]): Promise<Stamp> => {\n const stamp = JSON.parse((await output(where, argv)).split(\"\\n\").at(-1)!) as Stamp;\n return { t0: stamp.t0, t1: stamp.t1 };\n };\n const ips: Record<string, string> = Object.fromEntries(Object.entries(parts.members).map(([name, at]) => [name, at.ip]));\n const view = async (): Promise<GroupView> => {\n const sentinels = [];\n for (const name of group.sentinels) {\n const master = fieldsOf(await cli(name, [\"SENTINEL\", \"MASTER\", group.name]))[0] ?? {};\n const replicas = fieldsOf(await cli(name, [\"SENTINEL\", \"REPLICAS\", group.name]));\n sentinels.push({ master, replicas });\n }\n const members: Record<string, Record<string, string>> = {};\n for (const name of group.members) members[name] = infoOf(await cli(name, [\"INFO\", \"replication\"]));\n return viewOf(group, ips, sentinels, members);\n };\n const after = (at: number | undefined): string[] => (at === undefined ? [] : [\"--at\", String(Math.round(at))]);\n const roles = (role: GroupRole, primary: string): string => {\n if (role === \"primary\") return primary;\n if (role === \"sentinel\") return group.sentinels[0]!;\n const replica = group.members.find((name) => name !== primary);\n if (replica === undefined) throw new Error(\"the group has no replica\");\n return replica;\n };\n\n return {\n group,\n clientIp,\n observerIp,\n members: parts.members,\n sentinels: parts.sentinels,\n netns: { client: clientNetns, watched },\n kernel: records.seen,\n timeline: timeline.seen,\n async sysctl(values) {\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n if (settings.length > 0) await output(admin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n const read: Record<string, string> = {};\n for (const key of Object.keys(values)) read[key] = await output(admin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async converge(within = CONVERGE_MS) {\n const started = Date.now();\n let calm = 0;\n let last: GroupView = { primary: null, healthy: false, why: \"no view yet\" };\n while (Date.now() - started < within) {\n last = await view().catch((error: unknown) => ({ primary: null, healthy: false, why: (error as Error).message }));\n // two healthy views in a row: one view can come in the middle of a change\n calm = last.healthy ? calm + 1 : 0;\n if (calm >= 2 && last.primary !== null) return last.primary;\n await sleep(1_000);\n }\n throw new Error(`the group did not converge in ${within} ms: ${last.why}`);\n },\n async flush(primary) {\n await cli(primary, [\"FLUSHALL\"]);\n const acked = Number(await cli(primary, [\"WAIT\", String(group.members.length - 1), \"5000\"]));\n if (acked !== group.members.length - 1) throw new Error(`${acked} replicas acknowledged the flush`);\n },\n async reset() {\n for (const name of group.sentinels) await cli(name, [\"SENTINEL\", \"RESET\", group.name]);\n },\n async holds(primary) {\n const keys = await cli(primary, [\"--scan\", \"--pattern\", \"w:*\"]);\n return keys\n .split(\"\\n\")\n .map((key) => Number(key.trim().slice(2)))\n .filter((n) => Number.isInteger(n) && n > 0)\n .sort((a, b) => a - b);\n },\n partOf: roles,\n async client(args) {\n const events = lines<ProbeEvent>();\n const container = await new GenericContainer(node)\n .withNetworkMode(`container:${holder.getId()}`)\n .withCommand([\"node\", \"/opt/harness/node/client-sentinel.mjs\", ...args])\n .withLogConsumer(events.consume)\n .start();\n return {\n events: events.seen,\n until: (kinds, within) => events.until(kinds, within, \"client\"),\n // a TERM first: the client writes its done line, then stops\n stop: () =>\n container\n .stop({ timeout: 2 })\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n fault(kind, part, cuts, at) {\n const peers = peersOf(parts, clientIp, part, cuts).join(\",\");\n return stamped(injectorOf(part), [\"/opt/harness/inject\", kind, peers, ...after(at)]);\n },\n lift(kind, part, cuts, at) {\n const peers = peersOf(parts, clientIp, part, cuts).join(\",\");\n return stamped(injectorOf(part), [\"/opt/harness/inject\", kind === \"rst\" ? \"clear-rst\" : \"clear\", peers, ...after(at)]);\n },\n signal(part, signal, at) {\n const program = group.sentinels.includes(part) ? group.programs.sentinel : group.programs.server;\n return stamped(injectorOf(part), [\"/opt/harness/inject\", \"signal\", program, signal, ...after(at)]);\n },\n async restart(member) {\n await output(box(member), [\"sh\", \"-c\", `${group.programs.server} /data/server.conf --daemonize yes`]);\n const started = Date.now();\n while ((await cli(member, [\"PING\"]).catch(() => \"\")) !== \"PONG\") {\n if (Date.now() - started > 30_000) throw new Error(`${member} did not start again`);\n await sleep(200);\n }\n },\n failover(coordinated) {\n const sentinel = group.sentinels[0]!;\n return stamped(injectorOf(sentinel), [\n \"/opt/harness/inject\",\n \"resp\",\n `127.0.0.1:${group.sentinelPort}`,\n \"SENTINEL\",\n \"FAILOVER\",\n group.name,\n ...(coordinated ? [\"COORDINATED\"] : []),\n ]);\n },\n async forget() {\n await output(admin, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n },\n async settle() {\n const from = records.seen.length;\n const clock = await new Promise<KernelRecord | null>((resolve) => {\n const started = Date.now();\n const look = () => {\n const line = records.seen.slice(from).find((record) => record.ev === \"clock\");\n if (line !== undefined) resolve(line);\n else if (Date.now() - started > 5_000) resolve(null);\n else setTimeout(look, 50);\n };\n look();\n });\n return clock === null ? null : Number(clock[\"lost\"] ?? 0);\n },\n async manifest(sysctls) {\n const config = await output(admin, [\"zcat\", \"/proc/config.gz\"]).catch(() => \"\");\n const inspect = async (container: StartedTestContainer) =>\n runtime.container.inspect(runtime.container.getById(container.getId()));\n const memberSettings = async (name: string) => {\n const read: Record<string, string> = {};\n // the reply is the name, then the value: an empty value is the end of the reply\n for (const key of Object.keys(group.memberSettings)) read[key] = (await cli(name, [\"CONFIG\", \"GET\", key])).split(\"\\n\")[1] ?? \"\";\n return read;\n };\n const sentinelSettings = async (name: string) => {\n const master = fieldsOf(await cli(name, [\"SENTINEL\", \"MASTER\", group.name]))[0] ?? {};\n const read: Record<string, string> = { quorum: master[\"quorum\"] ?? \"\" };\n for (const key of Object.keys(group.sentinelSettings)) read[key] = master[key] ?? \"\";\n return read;\n };\n const members = [];\n for (const name of group.members)\n members.push({ name, ip: parts.members[name]!.ip, id: (await inspect(box(name))).Image, settings: await memberSettings(name) });\n const sentinels = [];\n for (const name of group.sentinels)\n sentinels.push({ name, ip: parts.sentinels[name]!.ip, id: (await inspect(box(name))).Image, settings: await sentinelSettings(name) });\n return {\n kernel: await output(holder, [\"uname\", \"-r\"]),\n architecture: await output(holder, [\"uname\", \"-m\"]),\n hz: /^CONFIG_HZ=(\\d+)$/m.exec(config)?.[1] ?? null,\n boot_id: await output(holder, [\"cat\", \"/proc/sys/kernel/random/boot_id\"]),\n docker: `${runtime.info.containerRuntime.serverVersion} on ${runtime.info.containerRuntime.operatingSystem}`,\n target: { image: group.image, id: members[0]!.id, settings: members[0]!.settings, sysctls: {} },\n harness: {\n image,\n id: (await inspect(holder)).Image,\n packages: (await output(holder, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort(),\n },\n client: { kind: \"ioredis\", image: node },\n netns: { client: clientNetns, target: parts.members[first]!.netns },\n sysctls,\n group: { members, sentinels, observer: { image: node } },\n };\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n};\n\n// ---------------------------------------------------------------------------------------------\n// the runs of a group cell\n// ---------------------------------------------------------------------------------------------\n\n/** This count is the number of accepted writes that a run waits for before its fault: the stream is steady then. */\nconst STEADY = 20;\n/** This time is how long a run continues after its outcome, so that the record shows what came next. */\nconst TAIL_MS = 2_000;\n\n/** This function gives the address of a member, with the port of the group. */\nconst addressOf = (lab: GroupLab, member: string): string => `${lab.members[member]!.ip}:${lab.group.port}`;\n\n/** This function gives the latest time that a group cell waits for its outcome, in ms from the fault. */\nexport const deadlineOf = (cell: Cell): number => {\n const { prediction } = cell;\n const seconds =\n prediction.detects === true\n ? valueIn(prediction.high, \"s\")\n : prediction.detects === false && prediction.recovers !== undefined\n ? valueIn(prediction.recovers.high, \"s\")\n : valueIn(prediction.window, \"s\");\n return seconds * 1_000 + 10_000;\n};\n\n/** This function gives the arguments of the client of a group cell. */\nconst clientArgs = (cell: Cell, lab: GroupLab): string[] => {\n const sentinels = cell.group!.sentinels.map((name) => `${lab.sentinels[name]!.ip}:${cell.group!.sentinelPort}`);\n return [\n \"--sentinels\",\n sentinels.join(\",\"),\n \"--name\",\n cell.group!.name,\n \"--interval-ms\",\n String(cell.probe.interval === undefined ? 50 : valueIn(cell.probe.interval, \"ms\")),\n \"--max-s\",\n String(Math.ceil(deadlineOf(cell) / 1_000 + 120)),\n ...(cell.driverOptions === undefined ? [] : [\"--options\", JSON.stringify(cell.driverOptions)]),\n ];\n};\n\n/** This function waits until a condition on the lines of the client holds, or for `within` ms. It gives the first line that matches. */\nconst waitFor = (\n client: GroupClient,\n match: (event: ProbeEvent) => boolean,\n within: number,\n): Promise<ProbeEvent | undefined> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const found = client.events.find(match);\n if (found !== undefined || Date.now() - started > within) resolve(found);\n else setTimeout(look, 20);\n };\n look();\n });\n\n/** This function gives the latest subscription of the observer to each Sentinel, in some lines of the observer. */\nexport const subscriptionsOf = (timeline: readonly SentinelEvent[]): SentinelEvent[] => {\n const latest = new Map<string, SentinelEvent>();\n for (const line of timeline) if (line.ev === \"subscribed\") latest.set(String(line.sentinel), line);\n return [...latest.values()];\n};\n\n/** This function gives the count of lost records in the latest clock line of the prober. */\nconst droppedSoFar = (records: readonly KernelRecord[]): number => {\n for (let at = records.length - 1; at >= 0; at -= 1)\n if (records[at]!.ev === \"clock\") return Number(records[at]![\"lost\"] ?? 0);\n return 0;\n};\n\n/**\n * This function does one run of a group cell:\n * - The group converges, and its keys go. The client connects through the Sentinels, and its\n * stream of writes gets steady.\n * - The fault occurs on its part, as the primary of this run makes the roles. A fault with a\n * hold lifts at its instant in the VM. A stall lifts at its length after the first write into it.\n * - The run waits for its outcome: the first write that a new primary accepts, or the first one\n * after the lift on the same primary. Then it continues for a moment.\n * - Each fault goes, the client stops, and the group converges again. The keys that it then holds\n * tell which writes it kept.\n */\nexport const groupOnce = async (lab: GroupLab, cell: Cell, environment: number, index: number): Promise<Run> => {\n const group = cell.group!;\n const primary = await lab.converge();\n await lab.flush(primary);\n await lab.forget();\n const from = lab.kernel.length;\n const timelineFrom = lab.timeline.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const client = await lab.client(clientArgs(cell, lab));\n const lands = cell.prediction.group?.lands ?? \"new\";\n const place = (cell.faultAt ?? { role: \"primary\" }) as GroupPlace;\n let fault: Run[\"fault\"] = null;\n let heal: Run[\"heal\"] = null;\n let at: string | null = null;\n const undo: Array<() => Promise<unknown>> = [];\n try {\n await client.until([\"ready\"], 60_000);\n const steady = await waitFor(client, (event) => event.ev === \"ack\" && Number(event[\"n\"]) >= STEADY, 30_000);\n if (steady === undefined) throw new Error(`the client did not write ${STEADY} times in 30 s`);\n let healing: Promise<Run[\"heal\"]> | null = null;\n switch (cell.fault) {\n case \"blackhole\":\n case \"partition\":\n case \"rst\": {\n const kind = cell.fault === \"rst\" ? \"rst\" : \"blackhole\";\n const cuts = place.cuts ?? (cell.fault === \"partition\" ? \"group\" : \"all\");\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.fault(kind, part, cuts);\n undo.push(() => lab.lift(kind, part, cuts));\n if (cell.stall !== undefined) {\n // the first write into the fault arms the retransmissions: the stall counts from it\n const t1 = fault.t1;\n const sent = await waitFor(client, (event) => event.ev === \"sent\" && event.t > t1, 5_000);\n if (sent !== undefined) healing = lab.lift(kind, part, cuts, sent.t + valueIn(cell.stall, \"s\") * 1e9);\n } else if (cell.hold !== undefined) {\n healing = lab.lift(kind, part, cuts, fault.t0 + valueIn(cell.hold, \"s\") * 1e9);\n }\n break;\n }\n case \"freeze-process\": {\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.signal(part, \"STOP\");\n undo.push(() => lab.signal(part, \"CONT\"));\n if (cell.hold !== undefined) healing = lab.signal(part, \"CONT\", fault.t0 + valueIn(cell.hold, \"s\") * 1e9);\n break;\n }\n case \"fin\": {\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.signal(part, \"KILL\");\n if (group.members.includes(part)) undo.push(() => lab.restart(part));\n break;\n }\n case \"planned-failover\":\n fault = await lab.failover(cell.coordinated === true);\n break;\n default:\n throw new Error(`a group cell has no fault ${cell.fault}`);\n }\n // the injector holds the lift in the VM: its exec returns at the instant, and the stamps come with it\n const healed =\n healing === null\n ? Promise.resolve(null)\n : healing.then((stamp) => {\n heal = stamp;\n // the lift came: nothing is left to undo\n undo.length = 0;\n return stamp;\n });\n // a run that stops early lifts the fault itself, and the late lift then has nothing to remove\n healed.catch(() => undefined);\n const t0 = fault.t0;\n const old = addressOf(lab, primary);\n const deadline = deadlineOf(cell);\n const outcome =\n lands === \"new\"\n ? await waitFor(\n client,\n (event) => event.ev === \"ack\" && event.t > t0 && typeof event[\"at\"] === \"string\" && event[\"at\"] !== old,\n deadline,\n )\n : await (async () => {\n await healed;\n const after = (heal as Run[\"heal\"])?.t0 ?? t0;\n return waitFor(client, (event) => event.ev === \"ack\" && event.t > after, deadline);\n })();\n if (outcome !== undefined) await sleep(TAIL_MS);\n await healed;\n } finally {\n // each fault that is still on goes, and a killed server starts again\n for (const step of undo.reverse()) await step().catch(() => undefined);\n await client.stop();\n }\n const settled = await lab.settle();\n const lost = settled === null ? null : settled - lostBefore;\n const after = await lab.converge().catch(() => null);\n const holds = after === null ? null : await lab.holds(after);\n // a failover that a Sentinel aborted holds off its next attempt for 2 × failover-timeout. The\n // next run must not inherit that hold-off: each Sentinel forgets the state of the group, and the\n // next converge waits until each one knows the group again\n if (lab.timeline.slice(timelineFrom).some((line) => line.ev === \"sentinel\" && String(line.event).startsWith(\"-failover-abort\")))\n await lab.reset();\n return {\n environment,\n index,\n events: [...client.events],\n fault,\n ...(heal === null ? {} : { heal }),\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.members[primary]!.netns,\n lost,\n group: {\n members: lab.members,\n sentinels: lab.sentinels,\n primary,\n at,\n // the subscription of the observer to each Sentinel came before the run: its address names\n // the socket of each Sentinel's writes, which a calibration reads\n timeline: [...subscriptionsOf(lab.timeline.slice(0, timelineFrom)), ...lab.timeline.slice(timelineFrom)],\n after,\n holds,\n },\n };\n};\n","import type { StartedNetwork, StartedTestContainer } from \"testcontainers\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, GroupPlace, GroupRole, KernelRecord, ProbeEvent, ReplicaSet, Run, RunPart, SentinelEvent } from \"./cell.ts\";\nimport type { GroupClient, GroupNetworkFault, GroupView, Stamp } from \"./group.ts\";\nimport { CONVERGE_MS, deadlineOf } from \"./group.ts\";\nimport type { Manifest } from \"./lab.ts\";\nimport { STOP, containers, harnessImage, lines, mongoClientImage, netnsOf, output, withPid } from \"./lab.ts\";\n\n/**\n * This module is the lab of a replica set: a replicated group with no external authority, whose\n * members elect their own primary. It opens one environment for a replica set cell, for the runs\n * of the cell. Each member is a container from the stock image of the set, in its own network\n * namespace. An injector sidecar shares its network and process namespaces.\n *\n * The client of each run is the MongoDB driver, which finds the primary with its own monitors. The\n * timeline comes from the log of each member and from the lines of the client. The prober watches\n * the namespace of the client.\n */\n\n/**\n * A member line is one log line of a member, as mongod writes it in JSON. `real` is its time on\n * the wall clock in ms, and `t` is its time on the monotonic clock. The run gives `t` from the\n * clock lines of its client.\n */\nexport interface MemberLine extends SentinelEvent {\n readonly ev: \"mongod\";\n readonly member: string;\n readonly real: number;\n readonly id: number;\n readonly c: string;\n readonly msg: string;\n readonly attr: Readonly<Record<string, unknown>>;\n}\n\n/** These are the components of the log of mongod that a run keeps: replication, its heartbeats, the elections, the rollbacks and the commands. */\nexport const KEPT: ReadonlySet<string> = new Set([\"REPL\", \"REPL_HB\", \"ELECTION\", \"ROLLBACK\", \"COMMAND\"]);\n\n/** This function reads one log line of a member, or gives null for a line of another component or a line that is not JSON. */\nexport const memberLineOf = (member: string, text: string): MemberLine | null => {\n if (!text.startsWith(\"{\")) return null;\n let line: { t?: { $date?: string }; c?: string; id?: number; msg?: string; attr?: Record<string, unknown> };\n try {\n line = JSON.parse(text) as typeof line;\n } catch {\n return null;\n }\n const real = Date.parse(line.t?.$date ?? \"\");\n if (!Number.isFinite(real) || !KEPT.has(line.c ?? \"\")) return null;\n return { ev: \"mongod\", t: 0, member, real, id: Number(line.id ?? 0), c: line.c ?? \"\", msg: line.msg ?? \"\", attr: line.attr ?? {} };\n};\n\n/**\n * This function gives the offset of the wall clock from the monotonic clock, in ns, from the clock\n * lines of a client. Each line pairs its stamp with the wall clock in whole ms, which truncates.\n * Thus each pair is the offset or less, and the largest pair is the nearest. It is null without a clock line.\n */\nexport const clockOffsetOf = (events: readonly ProbeEvent[]): number | null => {\n const offsets = events.filter((event) => event.ev === \"clocks\").map((event) => Number(event[\"real\"]) * 1e6 - event.t);\n return offsets.length === 0 ? null : Math.max(...offsets);\n};\n\n/** This function puts the log lines of the members on the monotonic clock, with the offset of a client. */\nexport const onMonotonic = (log: readonly MemberLine[], offset: number | null): MemberLine[] =>\n log.map((line) => ({ ...line, t: offset === null ? 0 : Math.round(line.real * 1e6 - offset) }));\n\n/** A cut is the peers that a network fault on a part cuts it from, by their addresses, and the direction of the rules. */\nexport interface Cut {\n readonly peers: readonly string[];\n readonly direction?: \"out\";\n}\n\n/**\n * This function gives the peers that a fault on a member of a replica set cuts it from. `all` is\n * each other member and the client, `group` is the other members, and `clients` is the client.\n * `one` is the first other member in the sequence of the set, and `one-way` is the same member for\n * the segments from the part only.\n */\nexport const cutOf = (\n set: ReplicaSet,\n members: Readonly<Record<string, RunPart>>,\n client: string,\n part: string,\n cuts: NonNullable<GroupPlace[\"cuts\"]>,\n): Cut => {\n const others = set.members.filter((name) => name !== part).map((name) => members[name]!.ip);\n if (cuts === \"clients\") return { peers: [client] };\n if (cuts === \"group\") return { peers: others };\n if (cuts === \"one\") return { peers: others.slice(0, 1) };\n if (cuts === \"one-way\") return { peers: others.slice(0, 1), direction: \"out\" };\n return { peers: [...others, client] };\n};\n\n/** This function gives the command of the server of each member. The server is the child of a shell, thus a fin can stop it and the container stays up. */\nexport const memberCommand = (set: ReplicaSet): string =>\n `${set.programs.server} --replSet ${set.name} --bind_ip_all --port ${set.port} ${(set.args ?? []).join(\" \")}`.trim();\n\n/** This function gives the script of the shell that starts the set: each member by its address, and the settings of the set. */\nexport const initiateScript = (set: ReplicaSet, ips: Readonly<Record<string, string>>): string => {\n const config = {\n _id: set.name,\n members: set.members.map((name, at) => ({ _id: at, host: `${ips[name]}:${set.port}` })),\n settings: set.settings,\n };\n return `print(JSON.stringify(rs.initiate(${JSON.stringify(config)})))`;\n};\n\n/** This script prints the view of a member: itself, if it accepts writes, and the state of each member as it sees them. */\nexport const STATUS_SCRIPT: string =\n \"const s = rs.status(); const h = db.hello(); print(JSON.stringify({ me: h.me, writable: h.isWritablePrimary === true, \" +\n \"members: s.members.map((m) => ({ name: m.name, state: m.stateStr, health: m.health, optime: m.optimeDate ? m.optimeDate.getTime() : null })) }))\";\n\n/** A status is the view of one member, as `STATUS_SCRIPT` prints it. */\nexport interface Status {\n readonly me: string;\n readonly writable: boolean;\n readonly members: ReadonlyArray<{ readonly name: string; readonly state: string; readonly health: number; readonly optime: number | null }>;\n}\n\n/**\n * This function judges the view of a member: the primary of the set, and if the set is healthy.\n * The set is healthy when the member is the one primary and accepts writes. Each other member must\n * be a healthy secondary at the optime of the primary.\n */\nexport const statusViewOf = (set: ReplicaSet, ips: Readonly<Record<string, string>>, status: Status): GroupView => {\n const byAddress = new Map(set.members.map((name) => [`${ips[name]}:${set.port}`, name]));\n const primaries = status.members.filter((member) => member.state === \"PRIMARY\");\n if (primaries.length !== 1) return { primary: null, healthy: false, why: `the set has ${primaries.length} primaries` };\n const primary = byAddress.get(primaries[0]!.name) ?? null;\n if (primary === null) return { primary: null, healthy: false, why: `no member has the address ${primaries[0]!.name}` };\n if (status.me !== primaries[0]!.name || !status.writable) return { primary, healthy: false, why: `${primary} does not accept writes yet` };\n if (status.members.length !== set.members.length) return { primary, healthy: false, why: `the set has ${status.members.length} members` };\n for (const member of status.members) {\n if (member.health !== 1) return { primary, healthy: false, why: `${member.name} is not healthy` };\n if (member.state !== \"PRIMARY\" && member.state !== \"SECONDARY\") return { primary, healthy: false, why: `${member.name} is ${member.state}` };\n if (member.optime !== primaries[0]!.optime) return { primary, healthy: false, why: `${member.name} is behind the primary` };\n }\n return { primary, healthy: true, why: \"healthy\" };\n};\n\n/** A replica set lab is one environment of a replica set cell. */\nexport interface ReplicaSetLab {\n readonly set: ReplicaSet;\n readonly clientIp: string;\n readonly members: Readonly<Record<string, RunPart>>;\n readonly netns: { readonly client: number };\n /** These are all the records that the prober wrote, in sequence. */\n readonly kernel: readonly KernelRecord[];\n /** These are the log lines of the members that a run keeps, in the sequence of their arrival, with `t` at 0. */\n readonly log: readonly MemberLine[];\n /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */\n sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method waits until the set is whole and calm, and gives the name of its primary. */\n converge(within?: number): Promise<string>;\n /** This method removes each write from the set, and waits until each member has the removal. */\n flush(primary: string): Promise<void>;\n /**\n * This method lets each secondary stand for election again (`rs.freeze(0)`). A step-down holds its\n * old primary out of the elections for `STEP_DOWN.seconds`. Without it, a run inherits that hold.\n */\n unfreeze(primary: string): Promise<void>;\n /** This method gives the sequence numbers that the set holds, from its primary. */\n holds(primary: string): Promise<number[]>;\n /** This method gives the member that has a role in the set, when `primary` is the primary. */\n partOf(role: GroupRole, primary: string): string;\n /** This method starts the client of a run, with the arguments of `client-mongo.mjs`. */\n client(args: readonly string[]): Promise<GroupClient>;\n /** This method applies a network fault between a member and some of its peers, at once or at an instant of the VM. */\n fault(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace[\"cuts\"]>, at?: number): Promise<Stamp>;\n /** This method removes a network fault, at once or at an instant of the VM. */\n lift(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace[\"cuts\"]>, at?: number): Promise<Stamp>;\n /** This method sends a signal to the server process of a member, at once or at an instant of the VM. */\n signal(part: string, signal: \"KILL\" | \"STOP\" | \"CONT\", at?: number): Promise<Stamp>;\n /** This method starts the server of a member again, after a fin. */\n restart(member: string): Promise<void>;\n /** This method sends replSetStepDown to the primary. */\n stepDown(primary: string): Promise<Stamp>;\n /** This method removes the TCP metrics of the client namespace: each run starts from a kernel that keeps nothing. */\n forget(): Promise<void>;\n /** This method waits for the next clock line of the prober, which gives the count of lost records. */\n settle(): Promise<number | null>;\n manifest(sysctls: Readonly<Record<string, string>>): Promise<Manifest>;\n close(): Promise<void>;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** These are the arguments of replSetStepDown: the time that the old primary cannot stand again, and the wait for a secondary to catch up. */\nexport const STEP_DOWN = { seconds: 60, catchUp: 10 } as const;\n\n/**\n * This function opens one environment of a replica set cell:\n * - one new network, with each member, the client and the sender of a step-down on it.\n * - each member: the stock image, its server as the child of a shell. A log consumer reads its\n * JSON log. The first member starts the set with the address of each member, and the lab\n * waits for a primary.\n * - an injector sidecar for each member, in its network and process namespaces.\n * - a client that holds its own network namespace, with a privileged sidecar for its sysctls.\n * - the prober, which watches the namespace of the client.\n */\nexport const openReplicaSetLab = async (set: ReplicaSet): Promise<ReplicaSetLab> => {\n const image = await harnessImage();\n const mongo = await mongoClientImage();\n const { GenericContainer, Network, Wait, getContainerRuntimeClient } = await containers();\n const runtime = await getContainerRuntimeClient();\n const opened: StartedTestContainer[] = [];\n let network: StartedNetwork | undefined;\n const close = async () => {\n for (const container of opened.reverse()) await container.stop(STOP).catch(() => undefined);\n await network?.stop().catch(() => undefined);\n };\n const ipOf = async (container: StartedTestContainer): Promise<string> => {\n const info = await runtime.container.inspect(runtime.container.getById(container.getId()));\n const ip = info.NetworkSettings.Networks[network!.getName()]?.IPAddress;\n if (ip === undefined || ip === \"\") throw new Error(`${container.getName()} has no address on ${network!.getName()}`);\n return ip;\n };\n try {\n network = await new Network().start();\n const boxes = new Map<string, StartedTestContainer>();\n const injectors = new Map<string, StartedTestContainer>();\n const members: Record<string, RunPart> = {};\n const log: MemberLine[] = [];\n for (const name of set.members) {\n let partial = \"\";\n const box = await new GenericContainer(set.image)\n .withNetwork(network)\n .withNetworkAliases(name)\n .withCommand([\"sh\", \"-c\", `${memberCommand(set)} & echo $! > /tmp/server.pid; while :; do sleep 3600; done`])\n .withWaitStrategy(Wait.forLogMessage(set.ready))\n .withLogConsumer((stream) =>\n stream.on(\"data\", (chunk: Buffer | string) => {\n partial += chunk.toString();\n const split = partial.split(\"\\n\");\n partial = split.pop() ?? \"\";\n for (const text of split) {\n const line = memberLineOf(name, text.trim());\n if (line !== null) log.push(line);\n }\n }),\n )\n .start();\n opened.push(box);\n boxes.set(name, box);\n const injector = await withPid(\n new GenericContainer(image).withNetworkMode(`container:${box.getId()}`).withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\"),\n `container:${box.getId()}`,\n ).start();\n opened.push(injector);\n injectors.set(name, injector);\n members[name] = { ip: await ipOf(box), netns: await netnsOf(injector) };\n }\n const ips: Record<string, string> = Object.fromEntries(Object.entries(members).map(([name, at]) => [name, at.ip]));\n\n const holder = await new GenericContainer(image).withNetwork(network).start();\n opened.push(holder);\n const admin = await new GenericContainer(image).withNetworkMode(`container:${holder.getId()}`).withPrivilegedMode().start();\n opened.push(admin);\n const clientIp = await ipOf(holder);\n // the sender of a step-down is a client of its own, on the network but not in the watched namespace\n const commander = await new GenericContainer(mongo)\n .withNetwork(network)\n .withCommand([\"sh\", \"-c\", \"while :; do sleep 3600; done\"])\n .start();\n opened.push(commander);\n\n const clientNetns = await netnsOf(admin);\n const records = lines<KernelRecord>();\n const prober = await new GenericContainer(image)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\"/opt/harness/prober\", String(clientNetns)])\n .withLogConsumer(records.consume)\n .start();\n opened.push(prober);\n const begun = await records.until([\"begin\", \"fail\"], 30_000, \"prober\");\n if (begun.ev === \"fail\") throw new Error(`the prober failed: ${String(begun[\"why\"])}`);\n\n const box = (name: string): StartedTestContainer => {\n const found = boxes.get(name);\n if (found === undefined) throw new Error(`the set has no member ${name}`);\n return found;\n };\n const injectorOf = (name: string): StartedTestContainer => {\n const found = injectors.get(name);\n if (found === undefined) throw new Error(`the set has no member ${name}`);\n return found;\n };\n /** This function starts a script of the shell on a member, against its own server only. */\n const shell = (name: string, script: string): Promise<string> =>\n output(box(name), [\n set.programs.shell,\n \"--quiet\",\n \"--norc\",\n `mongodb://127.0.0.1:${set.port}/?directConnection=true&serverSelectionTimeoutMS=3000`,\n \"--eval\",\n script,\n ]);\n const lastLine = (text: string): string => text.split(\"\\n\").filter((line) => line.trim() !== \"\").at(-1) ?? \"\";\n const stamped = async (where: StartedTestContainer, argv: string[]): Promise<Stamp> => {\n const stamp = JSON.parse(lastLine(await output(where, argv))) as Stamp;\n return { t0: stamp.t0, t1: stamp.t1 };\n };\n const after = (at: number | undefined): string[] => (at === undefined ? [] : [\"--at\", String(Math.round(at))]);\n let known: string = set.members[0]!;\n /** This function gives the view of the set: from the member that was the primary last, then from each other member. */\n const view = async (): Promise<GroupView> => {\n let last: GroupView = { primary: null, healthy: false, why: \"no member answered\" };\n for (const name of [known, ...set.members.filter((each) => each !== known)]) {\n const read = await shell(name, STATUS_SCRIPT)\n .then((text) => statusViewOf(set, ips, JSON.parse(lastLine(text)) as Status))\n .catch((error: unknown) => ({ primary: null, healthy: false, why: `${name}: ${(error as Error).message.slice(0, 200)}` }));\n if (read.primary !== null && read.primary === name) return read;\n last = read;\n }\n return last;\n };\n const converge = async (within = CONVERGE_MS): Promise<string> => {\n const started = Date.now();\n let calm = 0;\n let last: GroupView = { primary: null, healthy: false, why: \"no view yet\" };\n while (Date.now() - started < within) {\n last = await view();\n // two healthy views in a row: one view can come in the middle of a change\n calm = last.healthy ? calm + 1 : 0;\n if (last.primary !== null) known = last.primary;\n if (calm >= 2 && last.primary !== null) return last.primary;\n await sleep(1_000);\n }\n throw new Error(`the set did not converge in ${within} ms: ${last.why}`);\n };\n\n // the first member starts the set, with the address of each member as the client reaches it\n const initiated = await shell(set.members[0]!, initiateScript(set, ips));\n if (!lastLine(initiated).includes('\"ok\":1')) throw new Error(`rs.initiate failed: ${lastLine(initiated)}`);\n await converge();\n\n return {\n set,\n clientIp,\n members,\n netns: { client: clientNetns },\n kernel: records.seen,\n log,\n async sysctl(values) {\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n if (settings.length > 0) await output(admin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n const read: Record<string, string> = {};\n for (const key of Object.keys(values)) read[key] = await output(admin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n converge,\n async flush(primary) {\n const script =\n `const r = db.getSiblingDB(\"cells\").w.deleteMany({}, { writeConcern: { w: ${set.members.length}, wtimeout: 10000 } }); ` +\n \"print(JSON.stringify({ ok: r.acknowledged === true }))\";\n const reply = lastLine(await shell(primary, script));\n if (!reply.includes('\"ok\":true')) throw new Error(`the flush failed: ${reply}`);\n },\n async unfreeze(primary) {\n for (const name of set.members) {\n if (name === primary) continue;\n const reply = lastLine(await shell(name, \"print(JSON.stringify(rs.freeze(0)))\"));\n if (!reply.includes('\"ok\":1')) throw new Error(`${name} did not unfreeze: ${reply}`);\n }\n },\n async holds(primary) {\n const script = 'print(JSON.stringify(db.getSiblingDB(\"cells\").w.find({}, { n: 1, _id: 0 }).toArray().map((d) => d.n)))';\n const held = JSON.parse(lastLine(await shell(primary, script))) as unknown[];\n return held\n .map(Number)\n .filter((n) => Number.isInteger(n) && n > 0)\n .sort((a, b) => a - b);\n },\n partOf(role, primary) {\n if (role === \"primary\") return primary;\n if (role === \"sentinel\") throw new Error(\"a replica set has no Sentinel\");\n const secondary = set.members.find((name) => name !== primary);\n if (secondary === undefined) throw new Error(\"the set has no secondary\");\n return secondary;\n },\n async client(args) {\n const events = lines<ProbeEvent>();\n const container = await new GenericContainer(mongo)\n .withNetworkMode(`container:${holder.getId()}`)\n .withCommand([\"node\", \"/opt/harness/mongo/client-mongo.mjs\", ...args])\n .withLogConsumer(events.consume)\n .start();\n return {\n events: events.seen,\n until: (kinds, within) => events.until(kinds, within, \"client\"),\n // a TERM first: the client writes its done line, then stops\n stop: () =>\n container\n .stop({ timeout: 2 })\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n fault(kind, part, cuts, at) {\n const cut = cutOf(set, members, clientIp, part, cuts);\n const direction = cut.direction === undefined ? [] : [\"--direction\", cut.direction];\n return stamped(injectorOf(part), [\"/opt/harness/inject\", kind, cut.peers.join(\",\"), ...direction, ...after(at)]);\n },\n lift(kind, part, cuts, at) {\n const cut = cutOf(set, members, clientIp, part, cuts);\n const direction = cut.direction === undefined ? [] : [\"--direction\", cut.direction];\n return stamped(injectorOf(part), [\"/opt/harness/inject\", kind === \"rst\" ? \"clear-rst\" : \"clear\", cut.peers.join(\",\"), ...direction, ...after(at)]);\n },\n signal(part, signal, at) {\n return stamped(injectorOf(part), [\"/opt/harness/inject\", \"signal\", set.programs.server, signal, ...after(at)]);\n },\n async restart(member) {\n // the restarted server writes to the output of the container, thus its log consumer reads it\n await output(box(member), [\"sh\", \"-c\", `${memberCommand(set)} > /proc/1/fd/1 2>&1 & echo $! > /tmp/server.pid`]);\n const started = Date.now();\n while (!(await shell(member, \"print(db.hello().ok)\").catch(() => \"\")).includes(\"1\")) {\n if (Date.now() - started > 60_000) throw new Error(`${member} did not start again`);\n await sleep(500);\n }\n },\n stepDown(primary) {\n return stamped(commander, [\n \"node\",\n \"/opt/harness/mongo/stepdown-mongo.mjs\",\n `${members[primary]!.ip}:${set.port}`,\n String(STEP_DOWN.seconds),\n String(STEP_DOWN.catchUp),\n ]);\n },\n async forget() {\n await output(admin, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n },\n async settle() {\n const from = records.seen.length;\n const clock = await new Promise<KernelRecord | null>((resolve) => {\n const started = Date.now();\n const look = () => {\n const line = records.seen.slice(from).find((record) => record.ev === \"clock\");\n if (line !== undefined) resolve(line);\n else if (Date.now() - started > 5_000) resolve(null);\n else setTimeout(look, 50);\n };\n look();\n });\n return clock === null ? null : Number(clock[\"lost\"] ?? 0);\n },\n async manifest(sysctls) {\n const config = await output(admin, [\"zcat\", \"/proc/config.gz\"]).catch(() => \"\");\n const inspect = async (container: StartedTestContainer) => runtime.container.inspect(runtime.container.getById(container.getId()));\n const script = \"print(JSON.stringify({ version: db.version(), settings: rs.conf().settings }))\";\n const list = [];\n for (const name of set.members) {\n const read = JSON.parse(lastLine(await shell(name, script))) as { version: string; settings: Record<string, unknown> };\n const settings: Record<string, string> = { version: read.version };\n for (const key of Object.keys(set.settings)) settings[key] = String(read.settings[key] ?? \"\");\n list.push({ name, ip: members[name]!.ip, id: (await inspect(box(name))).Image, settings });\n }\n return {\n kernel: await output(holder, [\"uname\", \"-r\"]),\n architecture: await output(holder, [\"uname\", \"-m\"]),\n hz: /^CONFIG_HZ=(\\d+)$/m.exec(config)?.[1] ?? null,\n boot_id: await output(holder, [\"cat\", \"/proc/sys/kernel/random/boot_id\"]),\n docker: `${runtime.info.containerRuntime.serverVersion} on ${runtime.info.containerRuntime.operatingSystem}`,\n target: { image: set.image, id: list[0]!.id, settings: list[0]!.settings, sysctls: {} },\n harness: {\n image,\n id: (await inspect(holder)).Image,\n packages: (await output(holder, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort(),\n },\n client: { kind: \"mongodb\", image: mongo },\n netns: { client: clientNetns, target: members[set.members[0]!]!.netns },\n sysctls,\n group: { members: list, sentinels: [], observer: { image: mongo } },\n };\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n};\n\n// ---------------------------------------------------------------------------------------------\n// the runs of a replica set cell\n// ---------------------------------------------------------------------------------------------\n\n/** This count is the number of accepted writes that a run waits for before its fault: the stream is steady then. */\nconst STEADY = 20;\n/** This time is how long a run continues after its outcome, so that the record shows what came next. */\nconst TAIL_MS = 2_000;\n\n/** This function gives the arguments of the client of a replica set cell: each member, and the stream of writes. */\nexport const replicaSetArgs = (cell: Cell, members: Readonly<Record<string, RunPart>>): string[] => {\n const set = cell.replicaSet!;\n const hosts = set.members.map((name) => `${members[name]!.ip}:${set.port}`);\n return [\n members[set.members[0]!]!.ip,\n String(set.port),\n \"--mode\",\n \"stream\",\n \"--hosts\",\n hosts.join(\",\"),\n \"--set\",\n set.name,\n \"--interval-ms\",\n String(cell.probe.interval === undefined ? 50 : valueIn(cell.probe.interval, \"ms\")),\n \"--max-s\",\n String(Math.ceil(deadlineOf(cell) / 1_000 + (cell.hold === undefined ? 0 : valueIn(cell.hold, \"s\")) + 120)),\n \"--w\",\n String(cell.writeConcern ?? \"majority\"),\n ...(cell.driverOptions === undefined ? [] : [\"--options\", JSON.stringify(cell.driverOptions)]),\n ];\n};\n\n/** This function waits until a condition on the lines of the client holds, or for `within` ms. It gives the first line that matches. */\nconst waitFor = (client: GroupClient, match: (event: ProbeEvent) => boolean, within: number): Promise<ProbeEvent | undefined> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const found = client.events.find(match);\n if (found !== undefined || Date.now() - started > within) resolve(found);\n else setTimeout(look, 20);\n };\n look();\n });\n\n/** This function gives the count of lost records in the latest clock line of the prober. */\nconst droppedSoFar = (records: readonly KernelRecord[]): number => {\n for (let at = records.length - 1; at >= 0; at -= 1) if (records[at]!.ev === \"clock\") return Number(records[at]![\"lost\"] ?? 0);\n return 0;\n};\n\n/**\n * This function does one run of a replica set cell:\n * - The set converges, and its writes go. The client finds the primary with its monitors, and its\n * stream of writes gets steady.\n * - The fault occurs on its member, as the primary of this run makes the roles. A fault with a\n * hold lifts at its instant in the VM. A stall lifts at its length after the first write into\n * it. A step-down is replSetStepDown to the primary.\n * - The run waits for its outcome: the first accepted write of a write sent after the commit of\n * the fault. It is on a new primary, or on any primary. Then the run waits for the end of the\n * hold, thus the record shows what the set did in the whole hold.\n * - Each fault goes, the client stops, and the set converges again. The writes that it then holds\n * tell which writes it kept. The log lines of the members go onto the monotonic clock.\n */\nexport const replicaSetOnce = async (lab: ReplicaSetLab, cell: Cell, environment: number, index: number): Promise<Run> => {\n const set = cell.replicaSet!;\n const primary = await lab.converge();\n await lab.flush(primary);\n // each secondary can win an election: no hold of a step-down in an earlier run is left\n await lab.unfreeze(primary);\n await lab.forget();\n const from = lab.kernel.length;\n const logFrom = lab.log.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const client = await lab.client(replicaSetArgs(cell, lab.members));\n const lands = cell.prediction.group?.lands ?? \"new\";\n const place = (cell.faultAt ?? { role: \"primary\" }) as GroupPlace;\n let fault: Run[\"fault\"] = null;\n let heal: Run[\"heal\"] = null;\n let at: string | null = null;\n const undo: Array<() => Promise<unknown>> = [];\n try {\n await client.until([\"ready\"], 60_000);\n const steady = await waitFor(client, (event) => event.ev === \"ack\" && Number(event[\"n\"]) >= STEADY, 30_000);\n if (steady === undefined) throw new Error(`the client did not write ${STEADY} times in 30 s`);\n let healing: Promise<Run[\"heal\"]> | null = null;\n switch (cell.fault) {\n case \"blackhole\":\n case \"partition\":\n case \"rst\": {\n const kind = cell.fault === \"rst\" ? \"rst\" : \"blackhole\";\n const cuts = place.cuts ?? (cell.fault === \"partition\" ? \"group\" : \"all\");\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.fault(kind, part, cuts);\n undo.push(() => lab.lift(kind, part, cuts));\n if (cell.stall !== undefined) {\n // the first write into the fault arms the retransmissions: the stall counts from it. A\n // write that was in flight at the commit holds the stream, and then the stall counts from\n // the commit. The measurement then voids the run\n const t1 = fault.t1;\n const sent = await waitFor(client, (event) => event.ev === \"sent\" && event.t > t1, 5_000);\n healing = lab.lift(kind, part, cuts, (sent?.t ?? t1) + valueIn(cell.stall, \"s\") * 1e9);\n } else if (cell.hold !== undefined) {\n healing = lab.lift(kind, part, cuts, fault.t0 + valueIn(cell.hold, \"s\") * 1e9);\n }\n break;\n }\n case \"freeze-process\": {\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.signal(part, \"STOP\");\n undo.push(() => lab.signal(part, \"CONT\"));\n if (cell.hold !== undefined) healing = lab.signal(part, \"CONT\", fault.t0 + valueIn(cell.hold, \"s\") * 1e9);\n break;\n }\n case \"fin\": {\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.signal(part, \"KILL\");\n undo.push(() => lab.restart(part));\n break;\n }\n case \"step-down\":\n at = primary;\n fault = await lab.stepDown(primary);\n break;\n default:\n throw new Error(`a replica set cell has no fault ${cell.fault}`);\n }\n // the injector holds the lift in the VM: its exec returns at the instant, and the stamps come with it\n const healed =\n healing === null\n ? Promise.resolve(null)\n : healing.then((stamp) => {\n heal = stamp;\n // the lift came: nothing is left to undo\n undo.length = 0;\n return stamp;\n });\n // a run that stops early lifts the fault itself, and the late lift then has nothing to remove\n healed.catch(() => undefined);\n const t1 = fault.t1;\n const old = `${lab.members[primary]!.ip}:${set.port}`;\n const sentAt = (n: number): number => client.events.find((event) => event.ev === \"sent\" && Number(event[\"n\"]) === n)?.t ?? -Infinity;\n const outcome = await waitFor(\n client,\n (event) => event.ev === \"ack\" && sentAt(Number(event[\"n\"])) > t1 && (lands === \"same\" || (typeof event[\"at\"] === \"string\" && event[\"at\"] !== old)),\n deadlineOf(cell),\n );\n if (outcome !== undefined) await sleep(TAIL_MS);\n // the whole hold: a prediction that no failover occurs reads the set in each moment of it\n if (healing !== null) {\n await healed.catch(() => undefined);\n await sleep(TAIL_MS);\n }\n } finally {\n // each fault that is still on goes, and a killed server starts again\n for (const step of undo.reverse()) await step().catch(() => undefined);\n await client.stop();\n }\n const settled = await lab.settle();\n const lost = settled === null ? null : settled - lostBefore;\n const after = await lab.converge().catch(() => null);\n const holds = after === null ? null : await lab.holds(after);\n return {\n environment,\n index,\n events: [...client.events],\n fault,\n ...(heal === null ? {} : { heal }),\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.members[primary]!.netns,\n lost,\n group: {\n members: lab.members,\n sentinels: {},\n primary,\n at,\n timeline: onMonotonic(lab.log.slice(logFrom), clockOffsetOf(client.events)),\n after,\n holds,\n },\n };\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, FaultPlace, KernelRecord, ProbeEvent, Run } from \"./cell.ts\";\nimport { isHopPlace } from \"./cell.ts\";\nimport { longestOf, settled as driverSettled } from \"./driver.ts\";\nimport { groupOnce, openGroupLab } from \"./group.ts\";\nimport type { FaultOptions, Lab, Manifest, Probe } from \"./lab.ts\";\nimport { openLab } from \"./lab.ts\";\nimport type { Measured } from \"./observe.ts\";\nimport { measure, socketOf } from \"./observe.ts\";\nimport { openReplicaSetLab, replicaSetOnce } from \"./replset.ts\";\nimport type { Verdict } from \"./verdict.ts\";\nimport { STEPS, verdictOf } from \"./verdict.ts\";\n\n/** A cell result is a cell with its manifests, its runs, their measurements and its verdict. */\nexport interface CellResult {\n readonly cell: Cell;\n readonly manifests: readonly Manifest[];\n readonly runs: readonly Run[];\n /** These are the warm-up runs, one for each environment before its counted runs. The verdict does not count them. */\n readonly warmups: readonly Run[];\n readonly measured: readonly Measured[];\n readonly verdict: Verdict;\n /** These times are when its first environment opened and its last environment closed, on the wall clock. */\n readonly at: { readonly started: string; readonly ended: string };\n}\n\n/**\n * This function measures and judges the raw records of a cell, with the code of the harness at\n * this time. `runCell` uses it during a cell. A holder of the raw records of a cell can use it\n * again later. Thus the records get a new analysis after a change to observe.ts or to the verdict.\n */\nexport const judge = (\n cell: Cell,\n runs: readonly Run[],\n): { measured: Measured[]; verdict: Verdict } => {\n // a warm-up run is a record, not a sample: the verdict never counts it\n const measured = runs.filter((run) => run.warmup !== true).map((run) => measure(cell, run));\n return { measured, verdict: verdictOf(cell.prediction, measured) };\n};\n\n/** This function gives the count of lost records in the latest clock line of the prober, since it began. */\nconst droppedSoFar = (records: readonly KernelRecord[]): number => {\n for (let at = records.length - 1; at >= 0; at -= 1)\n if (records[at]!.ev === \"clock\") return Number(records[at]![\"lost\"] ?? 0);\n return 0;\n};\n\n/** This function gives the argv of the client for a cell, with a deadline far after all that it waits for. */\nconst probeArgs = (cell: Cell, lab: Lab): string[] => {\n const { prediction, probe } = cell;\n const longest =\n (prediction.detects === true && prediction.driver !== undefined\n ? longestOf(cell)\n : prediction.detects\n ? valueIn(prediction.high, \"s\")\n : valueIn(prediction.window, \"s\")) + (cell.idleBefore === undefined ? 0 : valueIn(cell.idleBefore, \"s\"));\n const args = [lab.targetIp, String(lab.targetPort), \"--mode\", probe.mode];\n if (probe.http !== undefined) {\n // an HTTP client sets its socket options from its own settings, and sends its own requests\n const { http } = probe;\n const gap = http.gap === undefined ? 0 : valueIn(http.gap, \"s\");\n args.push(\"--method\", http.method);\n if (http.gap !== undefined) args.push(\"--gap-ms\", String(valueIn(http.gap, \"ms\")));\n if (http.hold !== undefined) args.push(\"--hold-ms\", String(valueIn(http.hold, \"ms\")));\n if (http.bytes !== undefined) args.push(\"--bytes\", String(http.bytes));\n for (const [name, value] of Object.entries(http.options ?? {})) args.push(`--${name}`, String(value));\n // the client waits after its second request, thus its own close comes after the window\n args.push(\"--linger-ms\", String(LINGER_MS));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + gap + 10)));\n return args;\n }\n if (probe.request !== undefined) args.push(\"--request\", probe.request);\n if (probe.count !== undefined) args.push(\"--count\", String(probe.count));\n if (probe.interval !== undefined) args.push(\"--interval-ms\", String(valueIn(probe.interval, \"ms\")));\n if (probe.driver?.name === \"mongodb\") {\n // the MongoDB driver: its replica set, its operation, its write concern and its own options\n args.push(\"--set\", probe.driver.set);\n if (probe.driver.op !== undefined) args.push(\"--op\", probe.driver.op);\n if (cell.writeConcern !== undefined) args.push(\"--w\", String(cell.writeConcern));\n if (cell.before === true) args.push(\"--before\");\n if (cell.driverOptions !== undefined) args.push(\"--options\", JSON.stringify(cell.driverOptions));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n }\n if (probe.driver !== undefined) {\n // a driver sets its socket options from its own options: the keepAlive of ioredis\n args.push(\"--keepalive-ms\", String(valueIn(probe.driver.keepAlive, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n }\n if (probe.keepalive) args.push(\"--keepalive\");\n if (probe.userTimeout !== undefined)\n args.push(\"--user-timeout-ms\", String(valueIn(probe.userTimeout, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n};\n\n/**\n * This function waits for an error on the socket of the observer, or for its end of file\n * (CLOSE_WAIT, state 8). The prober records both. A cell whose observer is the server, or a proxy\n * of a topology, waits for that. The promise resolves after `within` ms at the latest.\n */\nconst observerError = (\n cell: Cell,\n partial: () => Run,\n within: number,\n): Promise<void> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const run = partial();\n const at = socketOf(cell, run);\n const seen =\n at !== undefined &&\n run.kernel.some(\n (record) =>\n record.netns === at.netns &&\n record.cookie === at.cookie &&\n ((record.ev === \"error\" && (record.err ?? 0) !== 0) ||\n (record.ev === \"state\" && (record as { readonly new?: number }).new === 8)),\n );\n if (seen || Date.now() - started > within) resolve();\n else setTimeout(look, 50);\n };\n look();\n });\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * This time is the pause between the client's first reply and the fault. It is more than Linux's\n * TCP_DELACK_MAX of 200 ms. Thus the delayed ACK of the reply is out before the fault.\n */\nexport const QUIET_MS = 250;\n\n/**\n * This time is how long an HTTP client waits after its second request, before it exits. The exit\n * closes its sockets, thus that close comes after the window of the cell.\n */\nexport const LINGER_MS = 1000;\n\n/** These are the lines that end the workload of an HTTP client: the end of its second request, its deadline, or its failure. */\nconst HTTP_DONE = [\"done\", \"deadline\", \"fail\"];\n\n/**\n * This function waits for the request that an in-flight client sends on the signal: its second\n * send, after the PING that made it ready. Its stamp is when the request first met the fault.\n */\nconst requestSent = (probe: Probe, within: number): Promise<ProbeEvent> =>\n new Promise((resolve, reject) => {\n const started = Date.now();\n const look = () => {\n const sent = probe.events.filter((event) => event.ev === \"sent\")[1];\n if (sent !== undefined) resolve(sent);\n else if (Date.now() - started > within)\n reject(new Error(`the client sent no request within ${within} ms of being told to`));\n else setTimeout(look, 1);\n };\n look();\n });\n\n/** This time is how long a run of a driver cell continues after its outcome, so that the record shows what came next. */\nconst TAIL_MS = 1_000;\n\n/** This function waits for the outcome of a run of a driver cell, or for `within` ms. The outcome is the end of its band and of each later band. */\nconst driverOutcome = async (cell: Cell, current: () => Run, within: number): Promise<void> => {\n const started = Date.now();\n while (Date.now() - started < within) {\n if (driverSettled(cell, current())) return;\n await sleep(250);\n }\n};\n\n/** This function gives the longest wait of a run of a driver cell for its outcome, in ms. */\nconst driverWithin = (cell: Cell): number => (longestOf(cell) * 1.2 + 10) * 1_000;\n\n/**\n * This function does one run of a cell whose fault holds before the client starts (`Cell.before`).\n * The fault is a network fault at its place. The client then starts into it, and the run waits for\n * each band of the driver. Then the fault lifts.\n */\nconst beforeOnce = async (lab: Lab, cell: Cell, environment: number, index: number): Promise<Run> => {\n if (cell.fault !== \"rst\" && cell.fault !== \"blackhole\" && cell.fault !== \"unreachable\")\n throw new Error(`${cell.name}: a fault before the client starts is a network fault, not ${cell.fault}`);\n await lab.forget();\n const from = lab.kernel.length;\n const middleboxFrom = lab.middlebox.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const faultAt: FaultPlace = cell.faultAt ?? \"target\";\n const kind = cell.fault === \"unreachable\" ? \"reject\" : cell.fault;\n const options: FaultOptions = cell.fault === \"unreachable\" ? { code: cell.icmp ?? \"host\" } : {};\n const fault = await lab.fault(kind, faultAt, options);\n let probe: Probe | undefined;\n try {\n probe = await lab.probe(probeArgs(cell, lab));\n const started = probe;\n const current = (): Run => ({\n environment,\n index,\n events: started.events,\n fault,\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost: 0,\n });\n await driverOutcome(cell, current, driverWithin(cell));\n await sleep(TAIL_MS);\n } finally {\n await lab.lift(kind, faultAt, options);\n await probe?.stop();\n }\n const settled = await lab.settle();\n return {\n environment,\n index,\n events: [...(probe?.events ?? [])],\n fault,\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost: settled === null ? null : settled - lostBefore,\n ...(lab.topology === null ? {} : { topology: lab.topology, middlebox: lab.middlebox.slice(middleboxFrom) }),\n };\n};\n\n/** This function gives the netem specification of a cell: its latency, then the netem of its fault when it has one. */\nconst netemSpec = (cell: Cell, fault: string | undefined): string | null => {\n const parts = [\n ...(cell.latency === undefined ? [] : [`delay ${valueIn(cell.latency, \"ms\")}ms`]),\n ...(fault === undefined ? [] : [fault]),\n ];\n return parts.length === 0 ? null : parts.join(\" \");\n};\n\n/**\n * This function does one run. The client connects and gets its reply, the fault occurs, and an\n * in-flight client sends into it. The harness then waits for the observer to see the fault, or for\n * the silent window to end. The observer is the client, or the socket of the target for a cell on\n * the server side. It can also be a socket of a proxy of a topology.\n *\n * A stall stops by itself, at its length after the send of the request, and the\n * run waits for the reply. Then the harness removes the fault, thus the environment can serve the\n * next run. The run keeps the records of the prober from its start to its end. The frozen process\n * is the peer of the observer: the target, or the client for the server.\n *\n * A dry run does all of this but the fault: the warm-up of a cell whose fault ends its environment.\n */\nconst once = async (\n lab: Lab,\n cell: Cell,\n environment: number,\n index: number,\n dry = false,\n): Promise<Run> => {\n if (cell.before === true) return beforeOnce(lab, cell, environment, index);\n // each run starts from kernels that keep nothing from the runs before it\n await lab.forget();\n const from = lab.kernel.length;\n const middleboxFrom = lab.middlebox.length;\n const serverFrom = lab.server.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const probe = await lab.probe(probeArgs(cell, lab));\n const server = cell.observer === \"server\";\n const peer = server\n ? { freeze: () => probe.freeze(), thaw: () => probe.thaw() }\n : { freeze: () => lab.freeze(), thaw: () => lab.thaw() };\n // a client that stopped before the signal to send tells why in its last lines\n const go = () =>\n probe.go().catch((error: unknown) => {\n const last = probe.events.slice(-3).map((event) => JSON.stringify(event));\n throw new Error(`${(error as Error).message}; the client's last lines: ${last.join(\" \")}`);\n });\n const placed = lab.topology === null ? {} : { topology: lab.topology };\n /** This function gives the run as the records show it so far: the waits read it. */\n const partial = (): Run => ({\n environment,\n index,\n events: probe.events,\n fault: null,\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost: 0,\n ...placed,\n });\n let fault: Run[\"fault\"] = null;\n let heal: { t0: number; t1: number } | null = null;\n // a target with a named process freezes by SIGSTOP, with stamps, and a hold thaws it at its instant\n const signalled = !server && cell.target.program !== undefined;\n let thawing: Promise<{ t0: number; t1: number }> | null = null;\n try {\n await probe.until([\"ready\"], 15_000);\n if (dry) {\n if (cell.probe.mode !== \"idle\") await go();\n await sleep(500);\n } else {\n // the in-flight data of a run on the server side is the reply of the server. The request goes\n // out first, and the fault occurs while the server holds it (the timeout of BLPOP). A fin with\n // a request in flight kills the server while it holds the request, so it goes first too\n const replyInFlight =\n cell.probe.mode === \"inflight\" &&\n ((server && cell.fault !== \"mtu-blackhole\") || cell.fault === \"fin\");\n if (replyInFlight) {\n // the client sends one sample after the file appears. The request reaches the server well\n // in the pause, and the reply waits for the timeout of the request\n await go();\n await sleep(200);\n } else {\n // The client acknowledges its first reply with a delayed ACK, 40 to 200 ms later\n // (TCP_DELACK_MIN to TCP_DELACK_MAX). A fault that comes first meets that ACK, not the\n // request or the idle time that the cell measures. So the fault waits until the ACK is out.\n await sleep(QUIET_MS);\n }\n const faultAt: FaultPlace = cell.faultAt ?? (server ? \"client\" : \"target\");\n // the direction of a simplex drop, relative to the namespace of the rule: in the target, or at\n // a proxy toward the client, the segments to the server come in; in the client, they go out\n const towardServerComesIn =\n faultAt === \"target\" || (isHopPlace(faultAt) && faultAt.toward === \"client\");\n const direction: FaultOptions[\"direction\"] =\n cell.drops === undefined\n ? undefined\n : (cell.drops === \"to-server\") === towardServerComesIn\n ? \"in\"\n : \"out\";\n const options: FaultOptions = {\n ...(direction === undefined ? {} : { direction }),\n ...(cell.icmp === undefined ? {} : { code: cell.icmp }),\n ...(cell.mtu === undefined ? {} : { above: cell.mtu }),\n };\n // a toxic of a cell whose fault is another one goes first: a slow close, then the close\n const toxicFirst = cell.toxic !== undefined && cell.fault !== \"toxic\";\n if (toxicFirst) await lab.toxic(cell.toxic!);\n // netem at the target shares its qdisc with the latency of the cell. At a hop, it is alone\n const netemAt: FaultPlace = cell.faultAt ?? \"target\";\n const netemOn = (fault: string | undefined) =>\n netemAt === \"target\" ? lab.netem(netemSpec(cell, fault), \"target\") : lab.netem(fault ?? null, netemAt);\n // an MTU drop goes where the long reply arrives: the namespace of the client\n const apply = async (): Promise<Run[\"fault\"]> => {\n switch (cell.fault) {\n case \"freeze-process\": {\n if (signalled) {\n const stamp = await lab.signal(\"STOP\");\n if (cell.hold !== undefined) {\n thawing = lab.signal(\"CONT\", stamp.t0 + valueIn(cell.hold, \"s\") * 1e9).then((thawed) => {\n heal = thawed;\n return thawed;\n });\n thawing.catch(() => undefined);\n }\n return stamp;\n }\n await peer.freeze();\n return null;\n }\n case \"fin\":\n await (server ? probe.kill() : lab.kill());\n return null;\n case \"unreachable\":\n return lab.fault(\"reject\", faultAt, { code: cell.icmp ?? \"host\" });\n case \"simplex\":\n return lab.fault(\"blackhole\", faultAt, options);\n case \"mtu-blackhole\":\n return lab.fault(\"mtu\", cell.faultAt ?? \"client\", { above: cell.mtu ?? 1200 });\n case \"netem\":\n return netemOn(cell.netem);\n case \"rst\":\n return lab.fault(\"rst\", faultAt);\n // the router or the proxy of the topology is the fault, or a calibration has none, or the\n // server holds the reply by itself: nothing to inject\n case \"middlebox-forget\":\n case \"proxy-idle\":\n case \"app-stall\":\n case \"none\":\n return null;\n case \"toxic\":\n if (cell.toxic === undefined) throw new Error(`${cell.name} names no toxic`);\n return lab.toxic(cell.toxic);\n case \"server-close\":\n await lab.serverClose();\n return null;\n default:\n // a stall is a black hole that stops by itself\n return lab.fault(\"blackhole\", faultAt);\n }\n };\n const lift = async (): Promise<void> => {\n switch (cell.fault) {\n case \"freeze-process\":\n if (signalled) {\n if (thawing !== null) await thawing.catch(() => lab.signal(\"CONT\"));\n else await lab.signal(\"CONT\");\n return undefined;\n }\n return peer.thaw();\n case \"fin\":\n case \"middlebox-forget\":\n case \"proxy-idle\":\n case \"server-close\":\n case \"app-stall\":\n case \"none\":\n return undefined;\n case \"unreachable\":\n return lab.lift(\"reject\", faultAt, { code: cell.icmp ?? \"host\" });\n case \"simplex\":\n return lab.lift(\"blackhole\", faultAt, options);\n case \"mtu-blackhole\":\n return lab.lift(\"mtu\", cell.faultAt ?? \"client\", { above: cell.mtu ?? 1200 });\n case \"netem\":\n await netemOn(undefined);\n return undefined;\n case \"rst\":\n return lab.lift(\"rst\", faultAt);\n case \"toxic\":\n await lab.toxic(null);\n return undefined;\n default:\n return heal === null ? lab.lift(\"blackhole\", faultAt) : undefined;\n }\n };\n fault = await apply();\n try {\n // an idle time before the request: a router forgets the flow in it, or a keepalive keeps it\n if (cell.idleBefore !== undefined && cell.probe.mode !== \"idle\")\n await sleep(valueIn(cell.idleBefore, \"ms\"));\n // a client in a gap sends its second request by itself, a gap after its first reply\n if ((cell.probe.mode === \"inflight\" || cell.probe.mode === \"stream\") && !replyInFlight) await go();\n const { prediction } = cell;\n const http = cell.probe.http !== undefined;\n if (prediction.detects !== null && prediction.driver !== undefined) {\n // a driver cell: each of its bands ends, or its silent window passes from the fault\n if (prediction.detects) {\n await driverOutcome(cell, () => ({ ...partial(), fault }), driverWithin(cell));\n } else {\n await sleep(valueIn(prediction.window, \"s\") * 1_000);\n }\n await sleep(TAIL_MS);\n } else if (cell.probe.mode === \"gap\") {\n // the run ends when the second request does: its outcome is a part of the record\n const gap = cell.probe.http?.gap === undefined ? 0 : valueIn(cell.probe.http.gap, \"s\");\n const window = valueIn(prediction.detects ? prediction.high : prediction.window, \"s\");\n await probe.until(HTTP_DONE, (gap + window) * 2_000 + 10_000).catch(() => undefined);\n } else if (cell.fault === \"stall\") {\n if (cell.stall === undefined) throw new Error(`${cell.name} says no stall's length`);\n // the injector holds the end in the VM until the length of the stall after the send\n const sent = await requestSent(probe, 5_000);\n heal = await lab.heal(faultAt, sent.t + valueIn(cell.stall, \"s\") * 1e9);\n const window = valueIn(prediction.detects ? prediction.high : prediction.window, \"s\");\n await probe.until([\"data\", \"error\", \"eof\"], window * 1_000).catch(() => undefined);\n } else if (prediction.detects === null) {\n // a distribution: the stream ends by itself, or the connection does\n const window = valueIn(prediction.window, \"s\") * 1_000;\n await probe.until([\"done\", \"error\", \"eof\", \"deadline\"], window).catch(() => undefined);\n } else if (prediction.detects) {\n const within = valueIn(prediction.high, \"s\") * 2_000 + 10_000;\n if (cell.observer !== undefined && cell.observer !== \"client\") {\n await observerError(cell, partial, within);\n } else if (http && cell.probe.mode !== \"idle\") {\n // an HTTP client can send the request again on a new connection: its outcome ends the run\n await probe.until(HTTP_DONE, within).catch(() => undefined);\n } else {\n // an HTTP client writes `closed` when its pool closes an idle connection by itself\n await probe.until([\"error\", \"eof\", \"deadline\", ...(http ? [\"closed\"] : [])], within).catch(() => undefined);\n }\n } else {\n await sleep(valueIn(prediction.window, \"s\") * 1_000);\n }\n } finally {\n await lift();\n if (toxicFirst) await lab.toxic(null);\n }\n // a thawed target answers the request that it held: the client sees the late reply\n if (cell.fault === \"freeze-process\" && !server)\n await probe.until([\"data\"], 5_000).catch(() => undefined);\n }\n } finally {\n await probe.stop();\n }\n // the prober drains its ring each 20 ms. Its next clock line closes the records of the run\n const settled = await lab.settle();\n const lost = settled === null ? null : settled - lostBefore;\n return {\n environment,\n index,\n events: [...probe.events],\n fault,\n ...(cell.fault === \"stall\" || thawing !== null ? { heal } : {}),\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost,\n ...placed,\n ...(lab.topology === null ? {} : { middlebox: lab.middlebox.slice(middleboxFrom) }),\n ...(cell.target.lines === true ? { server: lab.server.slice(serverFrom) } : {}),\n };\n};\n\n/** This function divides a count of runs across environments, as equally as possible. */\nexport const spread = (runs: number, environments: number): number[] =>\n Array.from(\n { length: environments },\n (_, at) => Math.floor(runs / environments) + (at < runs % environments ? 1 : 0),\n ).filter((count) => count > 0);\n\n/** This function tells if the fault of a cell ends its environment: a server that stopped cannot serve the next run. */\nconst endsEnvironment = (cell: Cell): boolean =>\n cell.fault === \"fin\" && (cell.observer === undefined || cell.observer === \"client\");\n\n/** This function says one line for a run. */\nconst line = (cell: Cell, run: Run, read: Measured): string =>\n `${cell.name}: env ${run.environment} ${run.warmup === true ? \"warm-up\" : `run ${run.index}`} ${read.outcome.kind}` +\n `${read.elapsed === null ? \"\" : ` ${read.elapsed.value} s`}` +\n `${read.void === null ? \"\" : ` (void: ${read.void})`}`;\n\n/**\n * This function does the part of the runs of a cell in one environment, and records its manifest.\n * A warm-up goes first, and the verdict does not count it. On a loaded machine, a new environment is\n * sometimes slow at first: its first exec, its first client, its first fault. The warm-up\n * of a cell whose fault ends the environment is a dry run, without the fault.\n */\nconst inEnvironment = async (\n cell: Cell,\n environment: number,\n count: number,\n say: (line: string) => void,\n): Promise<{ manifest: Manifest; runs: Run[]; warmup: Run }> => {\n if (cell.group !== undefined) return inGroupEnvironment(cell, environment, count, say);\n if (cell.replicaSet !== undefined) return inReplicaSetEnvironment(cell, environment, count, say);\n const lab = await openLab(cell.target, cell.client ?? cell.probe.driver?.name ?? \"tcp\", cell.topology);\n try {\n // the settings of the target first: Redis applies tcp-keepalive to a client when it accepts it\n const settings = await lab.configure(cell.targetSettings ?? {});\n const targetSysctls = await lab.targetSysctl(cell.targetSysctls ?? {});\n if (cell.targetSetup !== undefined) await lab.setup(cell.targetSetup);\n // netem and a length match act on segments only when TSO, GSO and GRO are off on the two ends,\n // and on the hop that the fault is at\n if (cell.fault === \"mtu-blackhole\" || cell.fault === \"netem\") {\n await lab.offload(false, \"target\");\n await lab.offload(false, \"client\");\n if (isHopPlace(cell.faultAt)) await lab.offload(false, cell.faultAt);\n }\n // a latency is on the path before the client connects, thus its round trip and RTO include it\n const latency = netemSpec(cell, undefined);\n if (latency !== null) await lab.netem(latency, \"target\");\n // the first connection must not wait for ARP: under a delay, that is a longer first round trip\n await lab.warm();\n const manifest = await lab.manifest(await lab.sysctl(cell.sysctls), settings, targetSysctls);\n const warmed = await once(lab, cell, environment, -1, endsEnvironment(cell));\n const warmup: Run = { ...warmed, warmup: true };\n say(line(cell, warmup, measure(cell, warmup)));\n const runs: Run[] = [];\n for (let index = 0; index < count; index += 1) {\n const run = await once(lab, cell, environment, index);\n runs.push(run);\n say(line(cell, run, measure(cell, run)));\n }\n return { manifest, runs, warmup };\n } finally {\n await lab.close();\n }\n};\n\n/**\n * This function does the part of the runs of a group cell in one environment. The warm-up is a\n * whole run with its fault, because no fault of a group ends its environment. A server that a\n * fin stopped starts again, and the group converges before each run.\n */\nconst inGroupEnvironment = async (\n cell: Cell,\n environment: number,\n count: number,\n say: (line: string) => void,\n): Promise<{ manifest: Manifest; runs: Run[]; warmup: Run }> => {\n const lab = await openGroupLab(cell.group!, { watchSentinels: cell.calibrates === \"sentinels\" });\n try {\n const manifest = await lab.manifest(await lab.sysctl(cell.sysctls));\n const warmed = await groupOnce(lab, cell, environment, -1);\n const warmup: Run = { ...warmed, warmup: true };\n say(line(cell, warmup, measure(cell, warmup)));\n const runs: Run[] = [];\n for (let index = 0; index < count; index += 1) {\n const run = await groupOnce(lab, cell, environment, index);\n runs.push(run);\n say(line(cell, run, measure(cell, run)));\n }\n return { manifest, runs, warmup };\n } finally {\n await lab.close();\n }\n};\n\n/**\n * This function does the part of the runs of a replica set cell in one environment. The warm-up\n * is a whole run with its fault, as for a group under Sentinels. The set converges before each run.\n */\nconst inReplicaSetEnvironment = async (\n cell: Cell,\n environment: number,\n count: number,\n say: (line: string) => void,\n): Promise<{ manifest: Manifest; runs: Run[]; warmup: Run }> => {\n const lab = await openReplicaSetLab(cell.replicaSet!);\n try {\n const manifest = await lab.manifest(await lab.sysctl(cell.sysctls));\n const warmed = await replicaSetOnce(lab, cell, environment, -1);\n const warmup: Run = { ...warmed, warmup: true };\n say(line(cell, warmup, measure(cell, warmup)));\n const runs: Run[] = [];\n for (let index = 0; index < count; index += 1) {\n const run = await replicaSetOnce(lab, cell, environment, index);\n runs.push(run);\n say(line(cell, run, measure(cell, run)));\n }\n return { manifest, runs, warmup };\n } finally {\n await lab.close();\n }\n};\n\n/**\n * This function does the runs of a cell. They go across new environments, and each environment records\n * its manifest. Then the verdict judges the runs. A median across the edge of the band extends\n * the cell from 11 runs to 22, and then to 44, each extension in new environments. With a\n * concurrency of more than one, that many environments operate at the same time, each with its\n * own prober. The runs then go across at least as many environments.\n *\n * Each environment does a warm-up first, and the result keeps it apart from the counted runs.\n */\nexport const runCell = async (\n cell: Cell,\n say: (line: string) => void = () => {},\n): Promise<CellResult> => {\n const started = new Date().toISOString();\n const manifests: Manifest[] = [];\n const runs: Run[] = [];\n const warmups: Run[] = [];\n const concurrency = Math.max(1, cell.concurrency ?? 1);\n let target: number = cell.runs;\n let next = 0;\n for (;;) {\n const counts = spread(target - runs.length, Math.max(cell.environments, concurrency));\n for (let at = 0; at < counts.length; at += concurrency) {\n const batch = counts.slice(at, at + concurrency).map((count) => ({ count, env: next++ }));\n const done = await Promise.all(\n batch.map(({ count, env }) => inEnvironment(cell, env, count, say)),\n );\n for (const each of done) {\n manifests.push(each.manifest);\n runs.push(...each.runs);\n warmups.push(each.warmup);\n }\n }\n const { measured, verdict } = judge(cell, runs);\n const larger = STEPS.find((step) => step > runs.length);\n if (verdict.decision !== \"extend\" || larger === undefined) {\n const at = { started, ended: new Date().toISOString() };\n return { cell, manifests, runs, warmups, measured, verdict, at };\n }\n target = larger;\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["between","newState","oldState","ERRNO","between","newState","oldState","between","sleep","STEADY","TAIL_MS","waitFor","droppedSoFar","sleep","TAIL_MS","droppedSoFar","driverSettled"],"sources":["../src/cell.ts","../src/verdict.ts","../src/application.ts","../src/driver.ts","../src/timeline.ts","../src/observe.ts","../src/lab.ts","../src/group.ts","../src/replset.ts","../src/run.ts"],"sourcesContent":["import type { KernelRecord } from \"@mark1russell7/prober\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\n\nexport type { KernelRecord };\n\n/**\n * A fault is a failure that the harness injects, by the name of its manifestation. Each name is\n * the last segment of the identifier of the manifestation in the corpus:\n * - `blackhole` is two DROP rules in the network namespace of the target. Thus the measurement\n * gets the kernel timers of the client.\n * - `freeze-process` is the cgroup freezer on the target, while its kernel stays up.\n * - `rst` answers the next segment of the client with a reset, from the namespace of the target.\n * It is the reference for the noise of the harness itself.\n * - `stall` is a black hole that stops by itself, `Cell.stall` after the client sends its\n * request. The VM times it. It shows what recovers, and when.\n * - `fin` stops the process of the peer with SIGKILL, while its kernel stays up. The kernel closes its sockets\n * with a FIN, or with a reset when unread data is in the queue.\n * - `unreachable` answers each segment from the observer with an ICMP unreachable (`Cell.icmp`).\n * Linux keeps it as a soft error on an established socket, and it continues to retransmit.\n * - `simplex` drops the segments in one direction only (`Cell.drops`).\n * - `mtu-blackhole` drops each segment to the observer that is longer than `Cell.mtu` bytes.\n * - `netem` is tc netem on the egress of the target (`Cell.netem`): loss, duplication,\n * reordering or corruption. Its outcome is a distribution.\n *\n * These faults are of a topology (`Cell.topology`):\n * - `middlebox-forget` injects nothing. The router of the topology forgets an idle flow at its own\n * idle timeout, and its form tells what a late packet then meets.\n * - `proxy-idle` injects nothing. The proxy of the topology closes an idle connection at its own\n * timeout.\n * - `toxic` adds a toxic of Toxiproxy (`Cell.toxic`) through its HTTP API.\n * - `server-close` makes the server close the connection of its peer, with the command of the\n * target (`Target.close`): `CLIENT KILL` in Redis.\n * - `none` injects nothing. A calibration cell measures the path itself: for example the time of a\n * proxy between its two sockets (`Cell.calibrates`).\n *\n * A fault at a router of the topology (`Cell.faultAt`) acts on what the router forwards toward\n * one side. It is a black hole, a reset or an ICMP unreachable. It can also be a simplex drop, an\n * MTU drop or netem.\n *\n * These faults are of a replicated group (`Cell.group` or `Cell.replicaSet`), on the part that `Cell.faultAt` names:\n * - `blackhole` drops each segment between the part and each of its peers. `rst` answers each\n * segment from a peer with a reset, thus the part refuses each connection.\n * - `partition` is a black hole between the part and some of its peers only (`GroupPlace.cuts`).\n * - `freeze-process` stops the server process of the part with SIGSTOP, and SIGCONT thaws it. Its\n * kernel stays up. `fin` stops the process with SIGKILL.\n * - `planned-failover` asks a Sentinel for a failover: `SENTINEL FAILOVER <group>`.\n *\n * This fault is of an HTTP server:\n * - `app-stall` injects nothing. The server process holds the reply to the request, because the\n * request asks for that (`HttpProbe.hold`), while its kernel stays up.\n *\n * This fault is of a replica set (`Cell.replicaSet`):\n * - `step-down` sends `replSetStepDown` to the primary, as a client sends a command.\n */\nexport type Fault =\n | \"blackhole\"\n | \"freeze-process\"\n | \"rst\"\n | \"stall\"\n | \"fin\"\n | \"unreachable\"\n | \"simplex\"\n | \"mtu-blackhole\"\n | \"netem\"\n | \"middlebox-forget\"\n | \"proxy-idle\"\n | \"toxic\"\n | \"server-close\"\n | \"partition\"\n | \"planned-failover\"\n | \"app-stall\"\n | \"step-down\"\n | \"none\";\n\n/**\n * A middlebox form tells what a router does with a flow that it forgot, when a late packet comes:\n * - `reset-both` sends a reset to the two ends when it forgets the flow.\n * - `reset-next` answers the next packet from inside with a reset to its sender, and drops a late\n * packet from outside.\n * - `silent-drop` drops each late packet, in each direction.\n * - `recreate` lets a late packet from inside through, and the flow starts again. It drops a late\n * packet from outside.\n * - `none` is a plain NAT router. A late packet from inside starts the flow again, and the stack of\n * the router answers a late packet from outside with a reset.\n */\nexport type MiddleboxForm = \"reset-both\" | \"reset-next\" | \"silent-drop\" | \"recreate\" | \"none\";\n\n/**\n * A router is a hop that forwards and translates the addresses of the side of the client (NAT),\n * with conntrack. It is the router image of the harness.\n */\nexport interface RouterHop {\n readonly name: string;\n readonly kind: \"router\";\n readonly form: MiddleboxForm;\n /** This time is the idle timeout of an established flow: nf_conntrack_tcp_timeout_established, in whole seconds. */\n readonly idle: Quantity;\n /**\n * This time is the interval of the listing that evicts each expired flow. Without it, the\n * interval is 0.1 s. The DESTROY event of an expired flow waits for that listing.\n */\n readonly pump?: Quantity;\n /**\n * These are the sysctls of the namespace of the router, which the router sets itself: it is\n * privileged. An example is `net.ipv4.icmp_ratelimit` for the ICMP errors of a fault at the router.\n */\n readonly sysctls?: Readonly<Record<string, string>>;\n}\n\n/**\n * A proxy is a hop that ends each TCP connection from the side of the client and opens its own to\n * the next hop. It is a stock image, pinned by digest.\n */\nexport interface ProxyHop {\n readonly name: string;\n readonly kind: \"proxy\";\n readonly image: string;\n /** This port is the port that the proxy listens on for the side of the client. */\n readonly port: number;\n readonly command?: readonly string[];\n /** 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. */\n readonly files?: Readonly<Record<string, string>>;\n /** This text is the log line that tells that the proxy serves. */\n readonly ready?: string;\n /** This record is the HTTP API of Toxiproxy: its port, and the name of the proxy that gets the toxics. */\n readonly api?: { readonly port: number; readonly proxy: string };\n /** These are the sysctls of the namespace of the proxy. A privileged sidecar sets them before each run. */\n readonly sysctls?: Readonly<Record<string, string>>;\n /** These are the settings of the proxy, for the manifest: for example its idle timeouts. */\n readonly settings?: Readonly<Record<string, string>>;\n}\n\n/**\n * A hop is one box between the client and the target. Each hop is on two networks: the network\n * of the side of the client, and the network of the side of the target.\n */\nexport type Hop = RouterHop | ProxyHop;\n\n/**\n * A topology is the chain of hops between the client and the target, in sequence from the client.\n * Without hops, the client and the target are a direct pair.\n */\nexport interface Topology {\n readonly name: string;\n readonly hops: readonly Hop[];\n}\n\n/**\n * A group is a replicated group under an external failover authority, as data. It is a primary and\n * its replicas, and the Sentinels that decide which member is the primary. Each member and each\n * Sentinel is a container from the same stock image, pinned by digest. Each has its own network\n * namespace, thus a fault can act on one part. The client finds the primary through the\n * Sentinels, and the parts and the client share one network.\n */\nexport interface Group {\n /** This name is the name of the group that the Sentinels monitor, for example `mymaster`. */\n readonly name: string;\n /** This image is the stock image of each member and each Sentinel, pinned by digest. */\n readonly image: string;\n /** These are the programs of the image: the server, the Sentinel and the command line client. */\n readonly programs: { readonly server: string; readonly sentinel: string; readonly cli: string };\n readonly port: number;\n readonly sentinelPort: number;\n /** These are the names of the members. The first one is the primary when the environment starts. */\n readonly members: readonly string[];\n readonly sentinels: readonly string[];\n /** This count is how many Sentinels must agree that the primary is down. */\n readonly quorum: number;\n /** These are the settings of each member, as its configuration file spells them. The harness adds `replicaof` for each replica. */\n readonly memberSettings: Readonly<Record<string, string>>;\n /**\n * These are the settings of each Sentinel for the group, as `sentinel <setting> <group> <value>`\n * spells them: `down-after-milliseconds`, `failover-timeout` and `parallel-syncs`.\n */\n readonly sentinelSettings: Readonly<Record<string, string>>;\n /** These lines tell that a member and a Sentinel serve. */\n readonly ready: { readonly member: string; readonly sentinel: string };\n}\n\n/**\n * A replica set is a replicated group with no external authority, as data: its members elect their\n * own primary. Each member is a container from the same stock image, pinned by digest, in its own\n * network namespace. The client finds the primary by its own monitors. The harness starts the set\n * with the address of each member, and it waits for a primary.\n */\nexport interface ReplicaSet {\n /** This name is the name of the set, for example `rs0`. */\n readonly name: string;\n /** This image is the stock image of each member, pinned by digest. */\n readonly image: string;\n /** These are the programs of the image: the server and its shell. */\n readonly programs: { readonly server: string; readonly shell: string };\n /** These are the options of the server of each member, after the name of the set: for example the size of its cache. */\n readonly args?: readonly string[];\n readonly port: number;\n /** These are the names of the members. The first one starts the set. */\n readonly members: readonly string[];\n /** These are the settings of the configuration of the set, as `rs.initiate` takes them: for example `electionTimeoutMillis`. */\n readonly settings: Readonly<Record<string, number>>;\n /** This line tells that a member serves. */\n readonly ready: string;\n}\n\n/** A group role is a part of a group by its role at the start of a run: the primary, a replica, or a Sentinel. */\nexport type GroupRole = \"primary\" | \"replica\" | \"sentinel\";\n\n/**\n * A group place is the part of a group that a fault acts on. A network fault cuts the part from\n * some of its peers:\n * - `all`: each other part of the group, and each client. This is the default.\n * - `group`: the other members and the Sentinels, but not the clients.\n * - `clients`: the clients, but not the group.\n * - `one`: one other member, in both directions.\n * - `one-way`: one other member, for the segments from the part only.\n */\nexport interface GroupPlace {\n readonly role: GroupRole;\n readonly cuts?: \"all\" | \"group\" | \"clients\" | \"one\" | \"one-way\";\n}\n\n/** A toxic is one toxic of Toxiproxy, as its API takes it. Its toxicity is 1. */\nexport interface Toxic {\n readonly type:\n | \"latency\"\n | \"timeout\"\n | \"reset_peer\"\n | \"slow_close\"\n | \"bandwidth\"\n | \"limit_data\"\n | \"slicer\";\n readonly stream: \"downstream\" | \"upstream\";\n readonly attributes: Readonly<Record<string, number>>;\n}\n\n/**\n * A reference is the event that the time of a detector counts from: the event that arms the\n * timer in its machine document. Keepalive counts from the last received segment. The give-up of\n * the retransmissions counts from the first retransmission of the RTO timer, not from the tail\n * loss probe one RTO before it. The request itself meets a reset, thus a reset counts from the\n * send of the client. A FIN counts from the change of state of the socket of the peer when its\n * process dies (`peer-closed`). A toxic of Toxiproxy counts from the start of its commit, as the\n * injector stamps it (`fault`).\n *\n * A client that starts into a fault counts from its `connecting` line, or from its first line\n * without one (`client-start`).\n *\n * A heartbeat of a program counts from its first ping that got no answer (`ping-sent`). The\n * program writes a `ping` line before each ping, and the kernel stamps the write (`tx`). No\n * segment with data reaches the socket after that write, up to the detection.\n */\nexport type Reference =\n | \"last-received\"\n | \"first-retransmission\"\n | \"request-sent\"\n | \"peer-closed\"\n | \"fault\"\n | \"client-start\"\n | \"ping-sent\";\n\n/**\n * This prediction tells when a connection moves again after a stall. That occurs at the first\n * firing of its retransmission timer after the stall stops, and its reply comes back one round\n * trip later.\n */\nexport interface Recovers {\n /** This count identifies the firing that gets through: the number of firings before it. */\n readonly firing: number;\n /** This time is the earliest return of the reply, from the send of the request. */\n readonly low: Quantity;\n /** This time is the latest return of the reply, from the send of the request. */\n readonly high: Quantity;\n /**\n * This range is where the stall must stop, from the send, so that this firing is the first\n * after it. The range starts after the latest time of the firing before, and it stops before\n * the earliest time of this firing.\n */\n readonly heal: { readonly low: Quantity; readonly high: Quantity };\n}\n\n/**\n * This prediction tells when the reply to the request comes back, from the send of the request.\n * For example, the reply comes after the latency of a toxic, or after a forgotten flow starts again.\n */\nexport interface Replies {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * An RTT range is the range of the first round trip of the observer that a band assumes. The\n * models read a round trip, for example 1 ms. The band at that value holds within the noise for\n * each round trip in the range. A run whose first round trip is above the range is void.\n */\nexport interface RttRange {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A proxy time is the time of one proxy in one direction that a band includes. It is an upper\n * quantile of the times that a calibration cell of the proxy measured. Each time goes from an\n * arrival on one socket of the proxy to its write on the other. `up` goes toward the target, and\n * `down` toward the client.\n */\nexport interface ProxyTime {\n readonly hop: string;\n readonly direction: \"up\" | \"down\";\n /** This fraction is the quantile of the calibration, for example 0.99. */\n readonly quantile: number;\n readonly time: Quantity;\n}\n\n/**\n * A stage is one step of the timeline of a group after a fault, by the event that a Sentinel\n * publishes for it. The stages are the subjective down, the objective down, the start of a\n * failover and the election. Then the selection of a replica, the promotion, the switch to the new\n * primary, and the conversion of the old primary into a replica.\n */\nexport type SentinelStage =\n | \"+sdown\"\n | \"+odown\"\n | \"+try-failover\"\n | \"+elected-leader\"\n | \"+selected-slave\"\n | \"+promoted-slave\"\n | \"+switch-master\"\n | \"+convert-to-slave\";\n\n/**\n * A replica set stage is one step of the timeline of a replica set after a fault. A line of the log\n * of a member marks it, or a line of the client. The stages are a member that marks the primary\n * DOWN, the start of an election and its win, and the end of the catch-up. Then come the first\n * write of the new primary and the step-down of the old one. The client's lines give its discovery\n * of the new primary, the write-off of the old one, and the first write that a primary accepts.\n */\nexport type ReplicaSetStage =\n | \"member-down\"\n | \"election-start\"\n | \"elected\"\n | \"caught-up\"\n | \"writable\"\n | \"old-primary-steps-down\"\n | \"discovered\"\n | \"written-off\"\n | \"first-write\";\n\n/** A stage is one step of the timeline of a group: of a group under Sentinels, or of a replica set. */\nexport type Stage = SentinelStage | ReplicaSetStage;\n\n/** A stage band is when the first part announces a stage, from the reference of the cell. */\nexport interface StageBand {\n readonly stage: Stage;\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A group prediction tells what a group does after the fault, beside the band of its cell:\n * - `lands` is the primary that the first accepted write after the fault lands on: a new one, or the same one.\n * - `failover` tells if a Sentinel announces a switch to a new primary in the window.\n * - `stages` are the bands of the stages of the timeline. Each band includes the error of the\n * stamps of the observer (`stamps`).\n * - `errors` are the errors that the client must meet on the way, each with the band of its\n * first occurrence. An example is the error of the offline queue when it gives up.\n * - `quiet` are the stages that must not occur after the fault. Each one that a run shows is a finding.\n */\nexport interface GroupPrediction {\n readonly lands: \"new\" | \"same\";\n readonly failover: boolean;\n readonly stages: readonly StageBand[];\n readonly stamps?: Quantity;\n readonly errors?: ReadonlyArray<{ readonly name: string; readonly low: Quantity; readonly high: Quantity }>;\n readonly quiet?: readonly Stage[];\n}\n\n/**\n * A middlebox expectation tells what the router of a topology does with the flow in the window of\n * a silent prediction. Without it, the router keeps the flow. With `forgets`, the router forgets\n * the flow in that band after the last segment of the client, and nothing detects it.\n */\nexport interface MiddleboxExpectation {\n readonly forgets: { readonly low: Quantity; readonly high: Quantity };\n}\n\n/**\n * A request prediction tells what the second request of an HTTP client meets, beside the band of\n * its cell. The client writes it in its lines: the connection of each try, and the outcome.\n * - `connection` is the connection of its first try: the one that the first request used\n * (`reused`), or a new one.\n * - `retried` tells that the client sent it again on a new connection by itself.\n * - `error` is the error that reaches the caller, or null for a reply.\n * - `pending` tells that nothing ends the request in the window of the cell.\n */\nexport interface RequestPrediction {\n readonly connection: \"reused\" | \"new\";\n readonly retried: boolean;\n readonly error: string | null;\n readonly pending?: true;\n}\n\n/**\n * A line match is a line of the client that a driver band waits for. It has the `ev` of the line,\n * and the value of each field that it names. A dotted name is a field in a field, for example\n * `failure.name`. `name` is the field that names the error of the line, for its outcome.\n */\nexport interface LineMatch {\n readonly ev: string;\n readonly where?: Readonly<Record<string, string | number | boolean>>;\n readonly name?: string;\n}\n\n/**\n * A driver step is one more band of a driver after the band of its cell. An example is the error\n * of the retry after the interruption. It has its own reference on a socket of the driver, its\n * line, and its band. A run whose step is outside its band, or absent, has a finding.\n */\nexport interface DriverStep {\n readonly what: string;\n /** This name is the name of the error of the line, or OK. */\n readonly name: string;\n /** This role is the socket of the reference. Without it, the socket is the socket of the band of the cell. */\n readonly socket?: string;\n readonly from: Reference;\n readonly ends: LineMatch;\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/**\n * A driver band is a band of a cell whose client is a driver with more than one socket, for\n * example the MongoDB driver. It names the socket of the reference, and the line that ends the\n * band:\n * - `socket` is the role of the socket, as the client names it in its `socket` lines. The socket\n * `server` is the socket of the target for the first connection of the pool.\n * - `ends` is the line of the client that marks the detection. Without it, the detection is the\n * report of the kernel on the socket: an error, or the end of the file.\n * - `reply` is the line of a reply that a silent prediction times (`replies`), with its reference.\n * - `quiet` are the lines that must not occur after the fault, in the window of the prediction.\n * - `then` are the later bands, each one a test of its own.\n *\n * A band whose line ends it is a timer of the driver. Its `last-received` is the last segment with\n * data that its socket received or sent. The idle timer of a Node socket starts again at each read\n * and each write. A band of the kernel counts from the last segment of any kind that the socket\n * received.\n */\nexport interface DriverBand {\n readonly socket: string;\n readonly ends?: LineMatch;\n readonly reply?: { readonly from: Reference; readonly ends: LineMatch };\n readonly quiet?: readonly LineMatch[];\n readonly then?: readonly DriverStep[];\n}\n\n/**\n * A reconnect prediction tells when the program of the client tries a new connection after the\n * detection. Each wait counts from the start of the reconnect timer of the program (a\n * `reconnecting` line) to the try (a `dial` line). The first wait follows the detection, and each\n * next wait follows the failure of the try before it.\n */\nexport interface Reconnects {\n readonly waits: ReadonlyArray<{ readonly low: Quantity; readonly high: Quantity }>;\n}\n\n/**\n * A prediction is what a cell predicts. Either a detection occurs in an interval [T_lo, T_hi]\n * after the reference event, or nothing detects the fault in a window. Nothing detects a stall\n * that is shorter than the give-up, and the connection recovers from it. The prediction then also\n * tells when. A silent prediction can also tell when the reply to the request comes back\n * (`replies`), and what a router does with the flow (`middlebox`).\n *\n * A band can name the first round trip that it assumes (`rtt`), and the proxy times that it\n * includes (`through`).\n *\n * A cell whose outcome is a distribution, for example under netem loss, has no band. Its\n * prediction is `detects: null` with the reason, and its runs are measurements only. A\n * measurement-only cell that names a reference (`from`) also records the time from it to the end\n * of the connection, or to the last data.\n *\n * For a group cell, the band is the first write that a primary accepts after the fault, and its\n * errno is `OK`. `group` tells which primary, and the stages of the group on the way.\n *\n * For an HTTP cell, `request` tells what the second request meets. The errno `CLOSED` is a close\n * of the observer itself: its pool closes an idle connection, a FIN that it sends.\n *\n * A band of a driver (`driver`) counts from a socket of the driver to a line of the client.\n *\n * The errno `DROPPED` is a drop by the program of the observer itself. Its own timer gave up, and\n * it closed its socket or wrote a close frame. A detection can also tell when the client tries a\n * new connection (`reconnects`).\n */\nexport type Prediction =\n | {\n readonly detects: true;\n /** This name is the timer of the machine that fires first, or the event that detects the fault, with its errno. */\n readonly timer: string;\n /**\n * This name is the errno of the detection, or EOF for an end of file without an error. It is\n * OK for a write that a primary accepts, and CLOSED for a close of the observer itself. It is\n * DROPPED for a drop by the program of the observer.\n */\n readonly errno: string;\n readonly from: Reference;\n /** This time is the closed form, less the most that the timer can fire early. */\n readonly low: Quantity;\n /** This time is the closed form, plus the most that the timer can fire late, plus each proxy time. */\n readonly high: Quantity;\n readonly rtt?: RttRange;\n readonly through?: readonly ProxyTime[];\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n readonly driver?: DriverBand;\n readonly reconnects?: Reconnects;\n /**\n * This name tells how the program drops the connection, for the errno DROPPED. A close frame\n * leaves its socket ESTABLISHED below (`frame`). A close of its socket takes it out of\n * ESTABLISHED at once (`close`).\n */\n readonly drop?: \"frame\" | \"close\";\n }\n | {\n readonly detects: false;\n readonly window: Quantity;\n readonly recovers?: Recovers;\n readonly replies?: Replies;\n readonly middlebox?: MiddleboxExpectation;\n readonly rtt?: RttRange;\n readonly through?: readonly ProxyTime[];\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n readonly driver?: DriverBand;\n }\n | {\n readonly detects: null;\n readonly window: Quantity;\n readonly why: string;\n readonly from?: Reference;\n /**\n * This choice ends the measured time at the close of the socket by the observer itself, the\n * change from ESTABLISHED to FIN_WAIT1. Without it, the time ends at a report of the kernel or\n * at the last data. Then an idle pool that closes its own socket gives no end.\n */\n readonly ends?: \"own-close\";\n readonly group?: GroupPrediction;\n readonly request?: RequestPrediction;\n };\n\n/** A target is the server that a cell connects to: a stock image. */\nexport interface Target {\n /** This image is a stock image, pinned by digest. */\n readonly image: string;\n readonly port: number;\n /** This text is the log line that tells that the target serves. */\n readonly ready: string;\n /**\n * These commands set and read one setting of the target, as argv in it. The name and the value\n * come after them: `redis-cli CONFIG SET` and `CONFIG GET` for Redis. Without them, the target\n * has no settings to set.\n */\n readonly settings?: { readonly set: readonly string[]; readonly get: readonly string[] };\n /**\n * This command replaces the command of the image, for example a shell that holds the server\n * process as its child. Then a `fin` fault can stop the server, and the container stays up.\n */\n readonly command?: readonly string[];\n /** This command stops the server process in the target with SIGKILL, as argv, for a `fin` fault. */\n readonly kill?: readonly string[];\n /** This command makes the server close the connection of each client, as argv, for a `server-close` fault. */\n readonly close?: readonly string[];\n /**\n * These files go into the target before it starts, by their path, for example the script of a\n * server on a stock Node image. The image stays stock.\n */\n readonly files?: Readonly<Record<string, string>>;\n /**\n * This flag tells that the target writes JSON lines with an `ev` and a `t` on its standard\n * output, on CLOCK_MONOTONIC. Each run then keeps its lines (`Run.server`).\n */\n readonly lines?: true;\n /**\n * This name is the server process, as /proc/PID/comm holds it. With it, a freeze of the target\n * stops this process with SIGSTOP from a sidecar in its process namespace, and the injector\n * stamps it. SIGCONT then thaws it. Without it, the freeze is the cgroup freezer, with no stamps.\n */\n readonly program?: string;\n /**\n * This name is an image of the harness that the target operates in place of `image`. An\n * example is the image of the procedures client, which also holds the script of its server.\n * `image` is then the stock base of that image, for the record.\n */\n readonly harness?: HarnessImage;\n}\n\n/**\n * A harness image is an image of the harness that a target can operate. `procedures` is the\n * image of `@procedures/client` on Node: its WebSocket client and its server script.\n */\nexport type HarnessImage = \"procedures\";\n\n/**\n * A client is what the runs of a cell use: the TCP client of the harness, or a driver on its\n * socket. The HTTP clients are the `http.Transport` of Go (`go-http`), the `http.Agent` of Node\n * (`http-agent`) and an HTTP/1.1 `Client` of undici (`undici`). The MongoDB driver is `mongodb`, and the WebSocket client of\n * `@procedures/client` is `procedures-ws`.\n */\nexport type Client = \"tcp\" | \"ioredis\" | \"go-http\" | \"http-agent\" | \"undici\" | \"mongodb\" | \"procedures-ws\";\n\n/**\n * A WebSocket probe is the workload and the settings of the WebSocket client of\n * `@procedures/client` (`procedures-ws`). The cell gives each setting, thus the client takes\n * nothing from a default.\n */\nexport interface WsProbe {\n /**\n * This name is the class of the sockets of the client: the global `WebSocket` of Node (`global`),\n * or the `WebSocket` of the `ws` package (`ws`, the option `webSocket`).\n */\n readonly socket: \"global\" | \"ws\";\n /**\n * These are the settings of the heartbeat: a ping each interval, the wait for its pong, and the\n * misses in sequence that drop the connection. Null turns the heartbeat off.\n */\n readonly heartbeat: { readonly interval: Quantity; readonly timeout: Quantity; readonly misses: number } | null;\n /**\n * These are the settings of the reconnect: the first delay, the multiplier of each next delay,\n * the longest delay and the most tries. `jitter` is the random part of each delay, from 0 to 1.\n * Null turns the reconnect off.\n */\n readonly reconnect: {\n readonly initialDelay: Quantity;\n readonly multiplier: number;\n readonly maxDelay: Quantity;\n readonly attempts: number;\n readonly jitter: number;\n } | null;\n /** This time is how long a call waits for its first frame. */\n readonly requestTimeout: Quantity;\n /** This time is how long a call waits for a connection. */\n readonly connectionTimeout: Quantity;\n /** This time is how long the server holds the reply to the call in flight. Without it, the server replies at once. */\n readonly hold?: Quantity;\n}\n\n/**\n * An HTTP probe is the workload of an HTTP client. The first request is a GET of the root. The\n * second request follows the mode of the probe: after the signal (`inflight`), or after a gap\n * (`gap`).\n */\nexport interface HttpProbe {\n /** This method is the method of the second request. */\n readonly method: \"GET\" | \"POST\";\n /** This time is the gap of a `gap` workload: from the end of the first reply to the second request. */\n readonly gap?: Quantity;\n /** This time is how long the server holds the reply to the second request (`/hold?ms=`). */\n readonly hold?: Quantity;\n /** This length is the length of the body of the reply to the second request, in bytes (`/big?bytes=`). */\n readonly bytes?: number;\n /** These are settings of the client over its defaults, by the names of its flags: for example `idle-conn-timeout-ms`. */\n readonly options?: Readonly<Record<string, number>>;\n}\n\n/**\n * A client image is a process that a lab starts as the client of each run, in the network\n * namespace of the client. An example is an agent of an application on Node. Its command gets the\n * arguments of the TCP client of the harness. These are the address and the port of the target, then\n * the options of the cell. It writes JSON lines with an `ev` and a `t` (CLOCK_MONOTONIC in\n * nanoseconds) on its standard output, as the client of the harness does.\n */\nexport interface ClientImage {\n readonly image: string;\n readonly command: readonly string[];\n}\n\n/**\n * An observer is the side whose detection a cell measures. The client is one observer. The\n * server is another observer. It is the hold of the target on the connection of the client, at\n * its server socket in its namespace. The fault is on the peer of the observer. Thus\n * freeze-process freezes the target for the client, and the client for the server.\n *\n * A proxy of a topology is the third observer: its socket toward the client, or its socket toward\n * the target.\n */\nexport type Observer =\n | \"client\"\n | \"server\"\n | { readonly hop: string; readonly toward: \"client\" | \"target\" };\n\n/** A cell is one cell of the matrix: a topology, its settings, a workload, a fault and the prediction. */\nexport interface Cell {\n readonly name: string;\n readonly target: Target;\n /** These are the sysctls of the network namespace of the client, as the kernel spells them. The harness sets them before the client connects. */\n readonly sysctls: Readonly<Record<string, string>>;\n readonly probe: {\n /**\n * This mode tells if the client is idle after its first reply, or sends a request into the\n * fault. In a stream, it sends `count` requests into it, each `interval` after the reply before.\n * An HTTP client in a `gap` sends its second request a gap after its first reply, by itself.\n */\n readonly mode: \"idle\" | \"inflight\" | \"stream\" | \"gap\";\n /** This count is the number of requests of a stream. */\n readonly count?: number;\n /** This time is the pause after each reply of a stream, before the next request. */\n readonly interval?: Quantity;\n /** This flag sets SO_KEEPALIVE without overrides for the socket: the keepalive sysctls of the namespace apply. */\n readonly keepalive: boolean;\n /** This time sets TCP_USER_TIMEOUT. Without it, the default of the kernel (off) applies. */\n readonly userTimeout?: Quantity;\n /**\n * This text is the request that an in-flight run sends, as a Redis inline command. Without it,\n * the request is PING. BLPOP with a timeout makes the server answer only at the timeout. Thus\n * a cell on the server side has a reply in flight when the fault occurs.\n */\n readonly request?: string;\n /**\n * This driver replaces the TCP client of the harness, with its own options. The driver then\n * ignores `keepalive` and `userTimeout`:\n * - ioredis 6 on Node, whose keepAlive is the keepalive delay of the socket.\n * - the MongoDB driver on Node, against the replica set `set`. Its operation is a write, or\n * the read of a long document (`read-long`). `Cell.driverOptions` are its options.\n */\n readonly driver?:\n | { readonly name: \"ioredis\"; readonly keepAlive: Quantity }\n | { readonly name: \"mongodb\"; readonly set: string; readonly op?: \"write\" | \"read-long\" };\n /** This workload is the workload of an HTTP client (`Cell.client` is an HTTP client). The client then ignores `keepalive` and `userTimeout`. */\n readonly http?: HttpProbe;\n /** These are the settings of the WebSocket client (`Cell.client` is `procedures-ws`). The client then ignores `keepalive` and `userTimeout`. */\n readonly ws?: WsProbe;\n };\n readonly fault: Fault;\n /** This time is the length of a stall, from the send of the request. */\n readonly stall?: Quantity;\n /** This code is the ICMP unreachable of an `unreachable` fault. */\n readonly icmp?: \"host\" | \"net\" | \"port\" | \"admin\";\n /** This direction is the one that a `simplex` fault drops: the segments to the server, or to the client. */\n readonly drops?: \"to-server\" | \"to-client\";\n /** This length is the longest segment, in bytes, that an `mtu-blackhole` fault lets through to the observer. */\n readonly mtu?: number;\n /**\n * This specification is the tc netem of a `netem` fault, for example \"loss 5%\". It is on the\n * egress of the target, or at a hop (`faultAt`) on the egress of its device toward one side.\n */\n readonly netem?: string;\n /**\n * This delay goes on the egress of the target before the client connects, with tc netem. Thus\n * each round trip is longer by the delay, and so is the RTO.\n */\n readonly latency?: Quantity;\n /** These commands run in the target before a client connects, each as argv: for example, a large value to read. */\n readonly targetSetup?: readonly (readonly string[])[];\n readonly prediction: Prediction;\n /** This side is the side whose detection the cell measures. Without it, the side is the client. */\n readonly observer?: Observer;\n /** These are the settings of the target. The harness sets them in each environment before a client connects. */\n readonly targetSettings?: Readonly<Record<string, string>>;\n /** These are the sysctls of the network namespace of the target, for a cell on the server side: its tcp_retries2. */\n readonly targetSysctls?: Readonly<Record<string, string>>;\n /** This topology puts hops between the client and the target. Without it, the client and the target are a direct pair. */\n readonly topology?: Topology;\n /** This place is where the fault goes, when it is not the peer of the observer: for example one side of a hop. */\n readonly faultAt?: FaultPlace;\n /** This time is how long an in-flight client stays idle after the fault holds, before it sends its request. */\n readonly idleBefore?: Quantity;\n /** This toxic is the toxic of a `toxic` fault, which the Toxiproxy hop gets. */\n readonly toxic?: Toxic;\n /** This image, or this client of the harness, replaces the TCP client of the harness for each run. An HTTP client is one. */\n readonly client?: ClientImage | Client;\n /**\n * This name is the proxy hop whose own time a calibration cell measures. The time of a request\n * (`up`) starts at its arrival on the socket of the proxy toward the client. It stops at the\n * write on the socket toward the target. The time of its reply (`down`) goes the other way.\n *\n * For a group cell, `sentinels` makes the prober watch the namespace of each Sentinel too. Each\n * stamp of the observer then gets the write of its Sentinel that it read (`lags`).\n */\n readonly calibrates?: string;\n /**\n * This group replaces the target: a replicated group under its Sentinels. The client is then\n * ioredis in Sentinel mode, and it writes a stream of sequence numbers. `target` stays for the\n * record, and the group does not read it.\n */\n readonly group?: Group;\n /**\n * This replica set replaces the target: a replicated group whose members elect their own\n * primary. The client is then the MongoDB driver, and it writes a stream of sequence numbers.\n * `target` stays for the record, and the set does not read it.\n */\n readonly replicaSet?: ReplicaSet;\n /**\n * This time is how long a fault on a group holds after its commit. The injector then lifts it at\n * that instant, on the clock of the VM. On a direct pair, a frozen target thaws after it.\n */\n readonly hold?: Quantity;\n /** This flag makes a planned failover coordinated: `SENTINEL FAILOVER <group> COORDINATED`. */\n readonly coordinated?: boolean;\n /**\n * These are options of the driver over its defaults: of ioredis for the client of a group, or of\n * the MongoDB driver for its client. A cell at the defaults has none.\n */\n readonly driverOptions?: Readonly<Record<string, unknown>>;\n /** This write concern is the w of each write of the MongoDB client. Without it, it is majority. */\n readonly writeConcern?: \"majority\" | 1;\n /** This flag makes the fault hold before the client starts. The client then meets it at its first selection. */\n readonly before?: boolean;\n /** This count is the number of runs of the cell (11), across new environments (two at least). */\n readonly runs: number;\n readonly environments: number;\n /**\n * This count is the number of environments at the same time, each with its own prober. Without\n * it, the count is one. The calibration does its reference cells at 1, 4 and 8, to find how\n * many environments the machine holds before its noise increases.\n */\n readonly concurrency?: number;\n /**\n * This time is δ_abs of the environment of the cell: the noise of the pipeline. Each band includes\n * it, and a timer must be at least that late to make a run void. Stage 0 measures it in each\n * environment. Without it, the cell gets `NOISE`, the δ_abs of the first environment (4 ms).\n */\n readonly noise?: Quantity;\n}\n\n/** A hop place is a hop of a topology, with the side that the rules of a fault face. */\nexport interface HopPlace {\n readonly hop: string;\n readonly toward: \"client\" | \"target\";\n}\n\n/**\n * A fault place is the namespace that gets the rules of a fault. \"target\" and \"client\" are the two\n * ends. A hop place is a hop, with the side that the rules face. That side is the next hop toward\n * the client, or the next hop toward the target. The rules of a proxy act on its sockets. The\n * rules of a router act on what it forwards, by its device toward that side: the FORWARD chain.\n *\n * A group place is a part of a group, by its role.\n */\nexport type FaultPlace = \"target\" | \"client\" | HopPlace | GroupPlace;\n\n/** This function tells if a fault place is a hop of a topology. */\nexport const isHopPlace = (place: FaultPlace | undefined): place is HopPlace =>\n typeof place === \"object\" && \"hop\" in place;\n\n/** 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. */\nexport interface MiddleboxEvent {\n readonly ev: string;\n readonly t: number;\n readonly [field: string]: unknown;\n}\n\n/** A probe event is one line that the client wrote. `t` is CLOCK_MONOTONIC in nanoseconds: an instant, not an amount. */\nexport interface ProbeEvent {\n readonly ev: string;\n readonly t: number;\n readonly [field: string]: unknown;\n}\n\n/** A run is one run of a cell, as the harness records it. */\nexport interface Run {\n readonly environment: number;\n readonly index: number;\n /**\n * This flag marks the warm-up of an environment. It occurs before the counted runs, and the\n * verdict does not count it. On a loaded machine, a new environment is sometimes slow at first.\n */\n readonly warmup?: true;\n readonly events: readonly ProbeEvent[];\n /** This bracket is the time of the injector around the commit of the fault, on the same clock. It is null for a freeze. */\n readonly fault: { readonly t0: number; readonly t1: number } | null;\n /** This bracket is the time around the commit that stopped a stall. */\n readonly heal?: { readonly t0: number; readonly t1: number } | null;\n /** These are the records of the prober from the start to the end of the run, in the two namespaces. */\n readonly kernel: readonly KernelRecord[];\n /** This inode identifies the network namespace of the client, thus the records of the client. */\n readonly netns: number;\n /** This inode identifies the namespace of the target, where the records of its server socket are. */\n readonly targetNetns: number;\n /**\n * This count is the number of records that the prober lost in the run. One lost record makes\n * the run void. Null also makes the run void: no clock line came after the run to tell the count.\n */\n readonly lost: number | null;\n /**\n * This rate is the HZ of the kernel of the environment, from its manifest. With it, the rule of a\n * late timer is in time. A run without it keeps the rule in jiffies, as the runs before 0.9.0.\n */\n readonly hz?: number;\n /** These are the addresses and the namespaces of a topology, which the measurement of a run reads. */\n readonly topology?: RunTopology;\n /** These are the lines of the routers of a topology from the start to the end of the run. */\n readonly middlebox?: readonly MiddleboxEvent[];\n /** This record is the group of a group cell: its parts, and what it did in the run. */\n readonly group?: RunGroup;\n /** These are the lines of a target that writes them (`Target.lines`), from the start to the end of the run. */\n readonly server?: readonly ProbeEvent[];\n}\n\n/** A Sentinel event is one line of the observer of a group: an event that a Sentinel published, on the clock of the VM. */\nexport interface SentinelEvent {\n readonly ev: string;\n readonly t: number;\n /** This text is the address and port of the Sentinel. */\n readonly sentinel?: string;\n /** This name is the event, for example `+switch-master`. */\n readonly event?: string;\n /** This text is the detail of the event, as the Sentinel wrote it. */\n readonly detail?: string;\n readonly [field: string]: unknown;\n}\n\n/** A part is one member or Sentinel of a group, as a run found it: its address and its network namespace. */\nexport interface RunPart {\n readonly ip: string;\n readonly netns: number;\n}\n\n/** A run group is the group of a run: its parts, the primary before and after, the timeline, and the writes that it kept. */\nexport interface RunGroup {\n readonly members: Readonly<Record<string, RunPart>>;\n readonly sentinels: Readonly<Record<string, RunPart>>;\n /** This name is the member that was the primary at the start of the run. */\n readonly primary: string;\n /** This name is the part that got the fault, or null for a fault on the whole group. */\n readonly at: string | null;\n /**\n * These are the lines of the timeline from the start to the end of the run. For a group under\n * Sentinels, they are the lines of the observer of the Sentinels. For a replica set, they are\n * the log lines of the members (`ev` \"mongod\"), with their times on the monotonic clock.\n */\n readonly timeline: readonly SentinelEvent[];\n /** This name is the member that was the primary when the group converged after the run, or null when it did not converge. */\n readonly after: string | null;\n /** These are the sequence numbers that the group holds after the run, when it converged, or null. */\n readonly holds: readonly number[] | null;\n}\n\n/**\n * A run topology tells where each socket of a run is, in a topology:\n * - `targetPeer` is the address of the peer of the target, as the target sees it: the address of\n * the last hop.\n * - `hops` are the network namespaces of the hops, by their names, with the address of the peer of\n * each hop on each side.\n */\nexport interface RunTopology {\n readonly targetPeer: string;\n readonly hops: Readonly<\n Record<string, { readonly netns: number; readonly client: string; readonly target: string }>\n >;\n}\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Prediction, Stage } from \"./cell.ts\";\nimport type { Measured, Reordering } from \"./observe.ts\";\n\n/** This function gives the words of the outcome of a second request: its connection, a retry, and its end. */\nexport const requestWords = (outcome: {\n readonly connection: \"reused\" | \"new\";\n readonly retried: boolean;\n readonly error: string | null;\n readonly pending?: boolean | undefined;\n}): string =>\n `${outcome.connection}${outcome.retried ? \", a retry\" : \"\"}, ${outcome.pending === true ? \"pending\" : (outcome.error ?? \"a reply\")}`;\n\n/**\n * This amount is the noise of the pipeline itself, δ_abs, as the calibration measured it. It is\n * the 99.9th percentile of the whole latency of a reset, at the concurrency cap or less. Its\n * minimum is one jiffy. On 2026-10-12 the worst of 60 resets at 1 and 4 environments took\n * 1.141 ms, thus the minimum sets it to 4 ms. The calibration occurs again after each change to\n * the harness, the kernel or Docker.\n */\nexport const NOISE: Quantity = quantity(4, \"ms\");\n\n/**\n * This time is how late the injector can commit or lift a fault before the run is void. Stage 0\n * measures the commit apart from δ_abs, and the commit is longer. On a hosted runner it took up to\n * 2.75 ms at 4 environments, where δ_abs is 1 ms. Thus a cell's δ_abs does not change it.\n */\nexport const INJECT: Quantity = NOISE;\n\n/** This function gives the δ_abs of a cell: its own, or `NOISE` for a cell that gives none. */\nexport const noiseOf = (cell: { readonly noise?: Quantity | undefined }): Quantity => cell.noise ?? NOISE;\n\n/**\n * This function tells if a timer firing came late enough to void a run. A firing two jiffies or\n * more behind its wheel slot came late because the host stopped a vCPU. With the HZ of the run,\n * the rule is in time: the delay must also reach δ_abs, because each band already includes that\n * noise. At HZ 250 two jiffies are 8 ms, more than a δ_abs of 4 ms. At HZ 1000 they are 2 ms.\n */\nexport const delayVoids = (behind: number, hz: number | undefined, noise: Quantity): boolean => {\n if (behind < 2) return false;\n if (hz === undefined || !(hz > 0)) return true;\n return behind / hz >= Math.max(2 / hz, valueIn(noise, \"s\"));\n};\n/** This fraction, δ_rel, is how far the median of a cell can be outside the prediction and stay equivalent. */\nexport const RELATIVE = 0.05;\n/** These are the run counts of a cell: 11, then 22 and 44 while its median crosses the edge of the band. */\nexport const STEPS = [11, 22, 44] as const;\n\n/**\n * A decision is the result of a verdict. A cell whose outcome is a distribution has no band, and\n * its decision is `measured`: a record, not a test.\n */\nexport type Decision = \"holds\" | \"fails\" | \"extend\" | \"inconclusive\" | \"measured\";\n\n/** An interval is a range of times. */\nexport interface Interval {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/** A spread is the distribution of a set of times: its count, its median and its upper quantiles by nearest rank. */\nexport interface Spread {\n readonly count: number;\n readonly median: Quantity;\n readonly p99: Quantity;\n readonly p999: Quantity;\n readonly max: Quantity;\n}\n\n/** A verdict is the judgement of a cell on its runs. */\nexport interface Verdict {\n readonly decision: Decision;\n readonly counted: number;\n readonly voided: number;\n /** This band is where each run must land: [T_lo − δ_abs, T_hi + δ_abs]. It is null for a silent prediction. */\n readonly band: Interval | null;\n /** These are the runs outside the band, and the reason for each. Each is a finding. */\n readonly outside: ReadonlyArray<{ readonly run: string; readonly why: string }>;\n readonly median: Quantity | null;\n /** This interval is the order-statistic interval of the median at 95 %, or the whole range for fewer than six runs. */\n readonly medianInterval: Interval | null;\n /** This record is the sum of what reordering did in each counted run of a measurement-only cell. */\n readonly reordering?: Reordering;\n /** This record is the time of the proxy of a calibration cell, in each direction. */\n readonly proxy?: { readonly hop: string; readonly up: Spread | null; readonly down: Spread | null };\n /** This record is what the group of a group cell did over the counted runs: its stages, and its writes. */\n readonly group?: GroupVerdict;\n /**\n * These are the outcomes of the second request of an HTTP client over the counted runs, each\n * with its count: for example \"reused, a retry, a reply\". An outcome other than the predicted\n * one is a finding of its run.\n */\n readonly requests?: Readonly<Record<string, number>>;\n /** This record is what the client did after the detection over the counted runs, for a prediction that says when it reconnects. */\n readonly reconnect?: ReconnectVerdict;\n /** These are the outcomes of the call in flight of a WebSocket client over the counted runs, each with its count. */\n readonly calls?: Readonly<Record<string, number>>;\n /**\n * This record is the time from a drop of the program to the close of its socket by its own side,\n * over the counted runs. It also counts the runs where the socket did not close itself.\n */\n readonly left?: { readonly spread: Spread | null; readonly stayed: number };\n readonly why: string;\n}\n\n/**\n * A reconnect verdict sums the reconnect of the client over the counted runs. Each wait before a\n * try gets its spread. A wait outside its band is a finding of its run, thus it is in `outside`.\n * - `syn` is the time from the detection to the first SYN of a new socket.\n * - `established` is the time from the detection to the first new socket that became ESTABLISHED.\n * - `connect` is the time from a `dial` line to the SYN of its socket: the work of the program.\n */\nexport interface ReconnectVerdict {\n readonly waits: ReadonlyArray<Spread | null>;\n readonly syn: Spread | null;\n readonly established: Spread | null;\n readonly connect: Spread | null;\n}\n\n/**\n * A stage verdict is the test of one stage of the timeline of a group against its band. The band\n * is [T_lo − δ_abs, T_hi + the error of the stamps + δ_abs]. A run that did not announce the stage\n * is outside too.\n */\nexport interface StageVerdict {\n readonly stage: Stage;\n readonly band: Interval;\n readonly median: Quantity | null;\n readonly outside: ReadonlyArray<{ readonly run: string; readonly why: string }>;\n readonly decision: \"holds\" | \"fails\";\n}\n\n/**\n * A group verdict sums what the group of a group cell did in its counted runs:\n * - `stages` tests each stage that the prediction gives a band.\n * - `medians` is the median of each stage that a run announced, from the fault.\n * - `lost` counts the writes that a primary accepted and the group did not keep, in each run.\n * - `old` counts the writes that the old primary accepted after the fault, in each run.\n * - `unknown` counts the writes whose outcome the client did not see, and how many the group kept.\n * - `errors` are the errors on the way: the runs that met each, and the median of its first time.\n * - `lags` is the spread of the delays of the stamps, for a calibration.\n */\nexport interface GroupVerdict {\n readonly stages: readonly StageVerdict[];\n readonly medians: Partial<Record<Stage, Quantity>>;\n readonly lost: { readonly runs: number; readonly total: number; readonly median: number | null; readonly max: number | null };\n readonly old: { readonly median: number | null; readonly max: number | null };\n readonly unknown: { readonly total: number; readonly held: number };\n readonly errors: ReadonlyArray<{ readonly name: string; readonly runs: number; readonly first: Quantity }>;\n readonly lags?: Spread | null;\n}\n\nconst s = (value: number): Quantity => quantity(Math.round(value * 1e6) / 1e6, \"s\");\nconst name = (run: Measured): string => `env ${run.environment} run ${run.index}`;\n\n/** This function gives the value at quantile p of sorted values, by nearest rank: x_(⌈p·n⌉). */\nexport const quantileOf = (sorted: readonly number[], p: number): number =>\n sorted[Math.min(sorted.length, Math.max(1, Math.ceil(p * sorted.length))) - 1]!;\n\n/** This function gives the spread of a set of times, or null for none. */\nexport const spreadOf = (times: readonly Quantity[]): Spread | null => {\n if (times.length === 0) return null;\n const sorted = times.map((time) => valueIn(time, \"s\")).sort((a, b) => a - b);\n return {\n count: sorted.length,\n median: s(middle(sorted)),\n p99: s(quantileOf(sorted, 0.99)),\n p999: s(quantileOf(sorted, 0.999)),\n max: s(sorted.at(-1)!),\n };\n};\n\n/** This function adds the reordering records of runs: the counts add, and each largest value is the largest of all. */\nconst reorderingTotal = (runs: readonly Measured[]): Reordering | undefined => {\n const each = runs.flatMap((run) => (run.reordering === undefined ? [] : [run.reordering]));\n if (each.length === 0) return undefined;\n const sum = (key: \"outOfOrder\" | \"duplicates\" | \"fast\") => each.reduce((total, one) => total + one[key], 0);\n const add = (key: \"ooo\" | \"dsacks\" | \"seen\") =>\n each.some((one) => one[key] !== null) ? each.reduce((total, one) => total + (one[key] ?? 0), 0) : null;\n const top = (key: \"sacked\" | \"lost\" | \"degree\") =>\n each.some((one) => one[key] !== null) ? Math.max(...each.map((one) => one[key] ?? 0)) : null;\n return {\n outOfOrder: sum(\"outOfOrder\"),\n duplicates: sum(\"duplicates\"),\n ooo: add(\"ooo\"),\n sacked: top(\"sacked\"),\n lost: top(\"lost\"),\n dsacks: add(\"dsacks\"),\n fast: sum(\"fast\"),\n degree: top(\"degree\"),\n seen: add(\"seen\"),\n };\n};\n\n/** This function says a reordering record in words. */\nconst reorderingWords = (total: Reordering): string =>\n [\n `${total.outOfOrder} arrivals out of order and ${total.duplicates} duplicate arrivals at the client`,\n ...(total.ooo === null ? [] : [`its kernel counted ${total.ooo} segments out of order`]),\n ...(total.sacked === null ? [] : [`the server held up to ${total.sacked} segments SACKed`]),\n ...(total.dsacks === null ? [] : [`got ${total.dsacks} DSACKs`]),\n `sent ${total.fast} fast retransmissions`,\n ...(total.seen === null ? [] : [`saw ${total.seen} reorderings`]),\n ...(total.degree === null ? [] : [`and its reordering degree went to ${total.degree}`]),\n ].join(\", \");\n\n/** This function says a spread in words, in milliseconds. */\nconst spreadWords = (spread: Spread | null): string => {\n if (spread === null) return \"no time\";\n const ms = (time: Quantity) => Math.round(valueIn(time, \"s\") * 1e6) / 1e3;\n return `median ${ms(spread.median)} ms, p99 ${ms(spread.p99)} ms, p99.9 ${ms(spread.p999)} ms, max ${ms(spread.max)} ms of ${spread.count}`;\n};\n\n/** This function gives P(X ≤ j) for X ~ Binomial(n, 1/2), exactly. */\nconst below = (n: number, j: number): number => {\n let term = 1;\n let sum = 0;\n for (let i = 0; i <= j; i += 1) {\n sum += term;\n term = (term * (n - i)) / (i + 1);\n }\n return sum / 2 ** n;\n};\n\n/**\n * This function gives the distribution-free interval of a median: [x_(k), x_(n+1−k)], for the\n * largest k whose two tails each hold 2.5 % or less. For eleven runs, it gives [x_(2), x_(10)],\n * at 98.8 %.\n */\nexport const medianInterval = (sorted: readonly number[]): [number, number] => {\n const n = sorted.length;\n let k = 0;\n while (k + 1 <= n / 2 && below(n, k) <= 0.025) k += 1;\n return k === 0 ? [sorted[0]!, sorted[n - 1]!] : [sorted[k - 1]!, sorted[n - k]!];\n};\n\nconst middle = (sorted: readonly number[]): number => {\n const half = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 1 ? sorted[half]! : (sorted[half - 1]! + sorted[half]!) / 2;\n};\n\n/**\n * This function gives the verdict of a cell, from two tests:\n * - Containment: each run lands in [T_lo − δ_abs, T_hi + δ_abs]. A run outside is a finding.\n * - Median equivalence: the interval of the median is in the band, made wider by δ_rel. An\n * interval outside the band fails. An interval across its edge extends the cell to 22 runs, then\n * 44, and then the verdict is inconclusive.\n *\n * A silent prediction holds only if no run detected the fault in its window. After a stall, the\n * return of the reply gets the same test as a detection, by its interval, and a detection is a\n * finding. The distribution test (a DKW band) needs a predicted distribution from a simulator,\n * which does not exist at this time.\n */\nexport const verdictOf = (prediction: Prediction, runs: readonly Measured[], noise: Quantity = NOISE): Verdict => {\n const band = verdictOfBand(prediction, runs, noise);\n const counted = runs.filter((run) => run.void === null);\n const asked = counted.some((run) => run.request !== undefined) ? withRequests(band, counted) : band;\n const verdict = withProgram(asked, counted);\n if (prediction.group === undefined && !counted.some((run) => run.group !== undefined)) return verdict;\n return { ...verdict, group: groupVerdictOf(prediction, counted, noise) };\n};\n\n/**\n * This function adds the reconnect of the client and the outcomes of its call in flight to a\n * verdict. Its reason gets a sentence for each. A verdict whose runs have neither stays as it is.\n */\nconst withProgram = (verdict: Verdict, counted: readonly Measured[]): Verdict => {\n const reconnects = counted.flatMap((run) => (run.reconnect === undefined ? [] : [run.reconnect]));\n const calls = counted.flatMap((run) => (run.call === undefined ? [] : [run.call]));\n const lefts = counted.flatMap((run) => (run.left === undefined ? [] : [run.left]));\n if (reconnects.length === 0 && calls.length === 0 && lefts.length === 0) return verdict;\n const ms = (spread: Spread | null) => (spread === null ? \"none\" : `${Math.round(valueIn(spread.median, \"s\") * 1e6) / 1e3} ms`);\n const tries = Math.max(0, ...reconnects.map((each) => each.waits.length));\n const reconnect: ReconnectVerdict | undefined =\n reconnects.length === 0\n ? undefined\n : {\n waits: Array.from({ length: tries }, (_, at) =>\n spreadOf(reconnects.flatMap((each) => (each.waits[at] === undefined ? [] : [each.waits[at]!]))),\n ),\n syn: spreadOf(reconnects.flatMap((each) => (each.syn === null ? [] : [each.syn]))),\n established: spreadOf(reconnects.flatMap((each) => (each.established === null ? [] : [each.established]))),\n connect: spreadOf(reconnects.flatMap((each) => each.connects)),\n };\n const outcomes: Record<string, number> = {};\n for (const call of calls) outcomes[call.outcome] = (outcomes[call.outcome] ?? 0) + 1;\n const gone = lefts.flatMap((each) => (each === null ? [] : [each]));\n const left = lefts.length === 0 ? undefined : { spread: spreadOf(gone), stayed: lefts.length - gone.length };\n const said = [\n ...(left === undefined\n ? []\n : [\n `After the drop, the program closed its socket in ${gone.length} runs, median ${ms(left.spread)} after its drop line${left.stayed === 0 ? \"\" : `, and it did not close it in ${left.stayed}`}.`,\n ]),\n ...(reconnect === undefined\n ? []\n : [\n `Reconnect: the waits before each try, median ${reconnect.waits.map(ms).join(\", \")}; the first SYN median ${ms(reconnect.syn)} and ESTABLISHED median ${ms(reconnect.established)} after the detection.`,\n ]),\n ...(calls.length === 0\n ? []\n : [`Calls in flight: ${Object.entries(outcomes).map(([outcome, n]) => `${n} ${outcome}`).join(\"; \")}.`]),\n ];\n return {\n ...verdict,\n ...(reconnect === undefined ? {} : { reconnect }),\n ...(calls.length === 0 ? {} : { calls: outcomes }),\n ...(left === undefined ? {} : { left }),\n why: `${verdict.why.endsWith(\".\") ? verdict.why : `${verdict.why}.`} ${said.join(\" \")}`,\n };\n};\n\n/**\n * This function adds the outcomes of the second request of an HTTP client to a verdict. A cell\n * whose outcome is a distribution gets them in its reason too.\n */\nconst withRequests = (verdict: Verdict, counted: readonly Measured[]): Verdict => {\n const requests: Record<string, number> = {};\n for (const run of counted) {\n const words = run.request === null || run.request === undefined ? \"no second request\" : requestWords(run.request);\n requests[words] = (requests[words] ?? 0) + 1;\n }\n const listed = Object.entries(requests)\n .map(([words, n]) => `${n} ${words}`)\n .join(\"; \");\n return {\n ...verdict,\n requests,\n why: verdict.decision === \"measured\" ? `${verdict.why} Second requests: ${listed}.` : verdict.why,\n };\n};\n\n/** This function gives the median of some counts, or null for none. */\nconst medianCount = (values: readonly number[]): number | null =>\n values.length === 0 ? null : middle([...values].sort((a, b) => a - b));\n\n/** This function sums what the group of a group cell did in its counted runs, and tests each stage that has a band. */\nexport const groupVerdictOf = (prediction: Prediction, counted: readonly Measured[], delta: Quantity = NOISE): GroupVerdict => {\n const noise = valueIn(delta, \"s\");\n const stamps = prediction.group?.stamps === undefined ? 0 : valueIn(prediction.group.stamps, \"s\");\n const stages = (prediction.group?.stages ?? []).map((band): StageVerdict => {\n const interval = { low: s(valueIn(band.low, \"s\") - noise), high: s(valueIn(band.high, \"s\") + stamps + noise) };\n const outside: Array<{ run: string; why: string }> = [];\n const values: number[] = [];\n for (const run of counted) {\n const at = run.group?.stages[band.stage];\n if (at === undefined) {\n // a stage of a Sentinel starts with +: a Sentinel announces it. The members and the client of a replica set mark the others\n const who = band.stage.startsWith(\"+\") ? \"no Sentinel announced\" : \"no member or client marked\";\n outside.push({ run: name(run), why: `${who} ${band.stage}` });\n continue;\n }\n const value = valueIn(at, \"s\");\n values.push(value);\n if (value < valueIn(interval.low, \"s\") || value > valueIn(interval.high, \"s\"))\n outside.push({ run: name(run), why: `${band.stage} at ${value} s is outside [${interval.low.value}, ${interval.high.value}] s` });\n }\n values.sort((a, b) => a - b);\n return {\n stage: band.stage,\n band: interval,\n median: values.length === 0 ? null : s(middle(values)),\n outside,\n decision: outside.length === 0 && values.length > 0 ? \"holds\" : \"fails\",\n };\n });\n const medians: Partial<Record<Stage, Quantity>> = {};\n const seen = new Set(counted.flatMap((run) => Object.keys(run.group?.stages ?? {}) as Stage[]));\n for (const stage of seen) {\n const values = counted.flatMap((run) => {\n const at = run.group?.stages[stage];\n return at === undefined ? [] : [valueIn(at, \"s\")];\n });\n medians[stage] = s(middle(values.sort((a, b) => a - b)));\n }\n const lostCounts = counted.flatMap((run) => (run.group?.writes.lost === null || run.group === undefined ? [] : [run.group.writes.lost.length]));\n const oldCounts = counted.flatMap((run) => (run.group === undefined ? [] : [run.group.writes.old]));\n const unknown = counted.flatMap((run) => run.group?.writes.unknown ?? []);\n const errors = new Map<string, number[]>();\n for (const run of counted)\n for (const error of run.group?.errors ?? []) errors.set(error.name, [...(errors.get(error.name) ?? []), valueIn(error.first, \"s\")]);\n const lags = counted.flatMap((run) => run.group?.lags ?? []);\n return {\n stages,\n medians,\n lost: {\n runs: lostCounts.length,\n total: lostCounts.reduce((sum, n) => sum + n, 0),\n median: medianCount(lostCounts),\n max: lostCounts.length === 0 ? null : Math.max(...lostCounts),\n },\n old: { median: medianCount(oldCounts), max: oldCounts.length === 0 ? null : Math.max(...oldCounts) },\n unknown: { total: unknown.length, held: unknown.filter((each) => each.held === true).length },\n errors: [...errors].map(([error, firsts]) => ({ name: error, runs: firsts.length, first: s(middle(firsts.sort((a, b) => a - b))) })),\n ...(lags.length === 0 ? {} : { lags: spreadOf(lags) }),\n };\n};\n\n/** This function gives the verdict of the band of a cell: its containment and the equivalence of its median. */\nconst verdictOfBand = (prediction: Prediction, runs: readonly Measured[], delta: Quantity = NOISE): Verdict => {\n const counted = runs.filter((run) => run.void === null);\n const voided = runs.length - counted.length;\n const outside = counted\n .filter((run) => run.finding !== null)\n .map((run) => ({ run: name(run), why: run.finding! }));\n const empty = { counted: counted.length, voided, median: null, medianInterval: null };\n if (counted.length === 0)\n return { ...empty, decision: \"inconclusive\", band: null, outside, why: \"no run counted\" };\n\n if (prediction.detects === null) {\n // a calibration of a proxy: its time in each direction, over all the requests of the runs\n const crossed = counted.flatMap((run) => (run.crossings === undefined ? [] : [run.crossings]));\n if (crossed.length > 0) {\n const hop = crossed[0]!.hop;\n const up = spreadOf(crossed.flatMap((each) => each.up));\n const down = spreadOf(crossed.flatMap((each) => each.down));\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: up?.median ?? null,\n proxy: { hop, up, down },\n why: `${counted.length} runs measured ${hop}: toward the target ${spreadWords(up)}; toward the client ${spreadWords(down)}. ${prediction.why}`,\n };\n }\n // a measurement-only cell with a reference: the outcomes, and the times from the reference\n if (prediction.from !== undefined) {\n const times = counted\n .flatMap((run) => (run.elapsed === null ? [] : [valueIn(run.elapsed, \"s\")]))\n .sort((a, b) => a - b);\n const kinds = new Map<string, number>();\n for (const run of counted) {\n const kind = run.outcome.kind === \"error\" ? run.outcome.errno : run.outcome.kind;\n kinds.set(kind, (kinds.get(kind) ?? 0) + 1);\n }\n const outcomes = [...kinds].map(([kind, n]) => `${n} ${kind}`).join(\", \");\n const timed =\n times.length === 0\n ? \"no time\"\n : `median ${s(middle(times)).value} s from ${prediction.from}, range [${s(times[0]!).value}, ${s(times.at(-1)!).value}] s`;\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: times.length === 0 ? null : s(middle(times)),\n why: `${counted.length} runs measured: ${outcomes}; ${timed}. ${prediction.why}`,\n };\n }\n const samples = counted\n .flatMap((run) => (run.samples ?? []).map((sample) => valueIn(sample, \"s\")))\n .sort((a, b) => a - b);\n const retransmissions = counted.reduce((sum, run) => sum + run.retransmissions, 0);\n const ended = counted.filter((run) => run.outcome.kind === \"error\" || run.outcome.kind === \"eof\");\n const reordering = reorderingTotal(counted);\n // a run in which nothing came out of order, twice or SACKed says nothing about reordering\n const anything =\n reordering !== undefined &&\n [reordering.outOfOrder, reordering.duplicates, reordering.ooo, reordering.sacked, reordering.dsacks, reordering.fast, reordering.seen].some(\n (count) => (count ?? 0) > 0,\n );\n const reordered = anything ? ` Reordering: ${reorderingWords(reordering)}.` : \"\";\n const why =\n samples.length === 0\n ? `${counted.length} runs measured, with no reply: ${prediction.why}`\n : `${counted.length} runs, ${samples.length} replies: median ${s(middle(samples)).value} s, p99 ${s(quantileOf(samples, 0.99)).value} s, slowest ${s(samples.at(-1)!).value} s, ${retransmissions} retransmissions, ${ended.length} connections ended.${reordered} ${prediction.why}`;\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: samples.length === 0 ? null : s(middle(samples)),\n ...(reordering === undefined ? {} : { reordering }),\n why,\n };\n }\n\n if (!prediction.detects && prediction.recovers === undefined && prediction.replies === undefined) {\n const decision = outside.length === 0 ? \"holds\" : \"fails\";\n const why =\n decision === \"holds\"\n ? `nothing detected it in ${counted.length} runs of ${valueIn(prediction.window, \"s\")} s`\n : `${outside.length} of ${counted.length} runs detected it`;\n return { ...empty, decision, band: null, outside, why };\n }\n\n // the interval of a detection, of the return of the reply after a stall, or of a reply in its band\n const timed = prediction.detects ? prediction : (prediction.recovers ?? prediction.replies)!;\n const noise = valueIn(delta, \"s\");\n const low = valueIn(timed.low, \"s\");\n const high = valueIn(timed.high, \"s\");\n const band = { low: s(low - noise), high: s(high + noise) };\n const values: number[] = [];\n for (const run of counted) {\n if (run.elapsed === null) continue;\n const value = valueIn(run.elapsed, \"s\");\n values.push(value);\n if (value < low - noise || value > high + noise)\n outside.push({\n run: name(run),\n why: `${value} s is outside [${band.low.value}, ${band.high.value}] s`,\n });\n }\n values.sort((a, b) => a - b);\n if (values.length === 0)\n return { ...empty, decision: \"fails\", band, outside, why: \"no run measured its time\" };\n const [from, to] = medianInterval(values);\n const median = s(middle(values));\n const interval = { low: s(from), high: s(to) };\n const wide = { low: low * (1 - RELATIVE), high: high * (1 + RELATIVE) };\n const inside = from >= wide.low && to <= wide.high;\n const disjoint = to < wide.low || from > wide.high;\n // a run can be outside for its time and for a finding: it counts once\n const off = new Set(outside.map((each) => each.run)).size;\n const counts = `${counted.length - off} of ${counted.length} runs inside [${band.low.value}, ${band.high.value}] s`;\n const centre = `median ${median.value} s, its interval [${interval.low.value}, ${interval.high.value}] s`;\n const result = {\n counted: counted.length,\n voided,\n band,\n outside,\n median,\n medianInterval: interval,\n };\n if (outside.length > 0 || disjoint)\n return { ...result, decision: \"fails\", why: `${counts}; ${centre}` };\n if (inside) return { ...result, decision: \"holds\", why: `${counts}; ${centre}` };\n const decision = counted.length < STEPS[2] ? \"extend\" : \"inconclusive\";\n return { ...result, decision, why: `${counts}; ${centre} straddles the band` };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, KernelRecord, ProbeEvent, Reconnects, Run } from \"./cell.ts\";\nimport { NOISE } from \"./verdict.ts\";\n\n/**\n * This module reads the acts of the program of the observer: its heartbeat, its drop of the\n * connection, its reconnect and its call in flight. The kernel has no report of a detection by\n * a program. Thus the program writes a line before each of its own acts, on CLOCK_MONOTONIC:\n * - `ping` before each ping of its heartbeat, and `drop` before it closes the connection by itself.\n * - `reconnecting` when it starts its reconnect timer, and `dial` before each new connection.\n *\n * The kernel then stamps each write of the program on the socket (`tx`) and each change of the\n * state of the socket. A line of the client names its socket by its local address (`local`), and\n * a line of a server names it by its peer (`peer`).\n */\n\n/** This function gives the interval between two instants on the one clock, in seconds to the microsecond. */\nconst between = (from: number, to: number): Quantity => quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\n\n/** This function gives the state of a state record after the change, and before it. TCP_ESTABLISHED is 1, TCP_SYN_SENT 2. */\nconst newState = (record: KernelRecord): number | undefined => (record as { readonly new?: number }).new;\nconst oldState = (record: KernelRecord): number | undefined => (record as { readonly old?: number }).old;\n\n/**\n * A program event is a line of the program of the observer, with the kernel's stamp of its act.\n * The act is a write, or a change of state of the socket. Without a record of the kernel in δ_abs\n * after the line, the instant is the stamp of the line.\n */\nexport interface ProgramEvent {\n readonly line: ProbeEvent;\n readonly at: number;\n /** This flag tells that the kernel stamped the event. */\n readonly kernel: boolean;\n /** This name is the kind of the record of the kernel: a write, or a change of state. It is null without a record. */\n readonly record: \"write\" | \"state\" | null;\n}\n\n/** This function gives the lines of the program of the observer: the client, or the target for the server. A proxy has no lines. */\nexport const programLines = (cell: Cell, run: Run): readonly ProbeEvent[] => {\n const observer = cell.observer ?? \"client\";\n if (observer === \"client\") return run.events;\n if (observer === \"server\") return run.server ?? [];\n return [];\n};\n\n/**\n * This function tells if a line of the program is about the connection of the run. The client\n * names it by its local address, and a server by its peer. A line with neither is about the one\n * connection of its program. A line whose address is null is about a socket that did not open:\n * a try that failed.\n */\nconst onConnection = (line: ProbeEvent, local: unknown): boolean => {\n if (!(\"local\" in line) && !(\"peer\" in line)) return true;\n return (line[\"local\"] ?? line[\"peer\"]) === local;\n};\n\n/** This function gives the first record of a socket that matches, at the stamp of a line or in δ_abs after it. */\nconst kernelAt = (\n socket: readonly KernelRecord[],\n line: ProbeEvent,\n matches: (record: KernelRecord) => boolean,\n delta: Quantity,\n): KernelRecord | undefined => {\n const noise = valueIn(delta, \"s\") * 1e9;\n return socket.find((record) => record.mono >= line.t && record.mono <= line.t + noise && matches(record));\n};\n\nconst eventOf = (\n socket: readonly KernelRecord[],\n line: ProbeEvent,\n matches: (record: KernelRecord) => boolean,\n delta: Quantity = NOISE,\n): ProgramEvent => {\n const record = kernelAt(socket, line, matches, delta);\n return {\n line,\n at: record?.mono ?? line.t,\n kernel: record !== undefined,\n record: record === undefined ? null : record.ev === \"tx\" ? \"write\" : \"state\",\n };\n};\n\n/**\n * This function gives the time from a line of the program to the close of the socket by its own\n * side. That close is its change from ESTABLISHED to FIN_WAIT1, or to CLOSE (an abort). A FIN of\n * the peer (CLOSE_WAIT) is not such a close. It is null when the socket did not close itself.\n */\nexport const leftAfter = (socket: readonly KernelRecord[], line: ProbeEvent): Quantity | null => {\n const left = socket.find(\n (record) => record.ev === \"state\" && oldState(record) === 1 && (newState(record) === 4 || newState(record) === 7) && record.mono >= line.t,\n );\n return left === undefined ? null : between(line.t, left.mono);\n};\n\n/**\n * This function tells how a drop differs from the way that its prediction gives, or gives null.\n * A close closes the socket in δ_abs. A close frame is a write, and the program does not close\n * the socket at the drop. A peer that answers the close frame can close it, and that is no finding.\n */\nexport const dropFinding = (\n expected: \"frame\" | \"close\",\n drop: ProgramEvent,\n left: Quantity | null,\n delta: Quantity = NOISE,\n): string | null => {\n const noise = valueIn(delta, \"s\");\n const soon = left !== null && valueIn(left, \"s\") <= noise;\n if (expected === \"close\") {\n if (left === null) return \"the program did not close its socket after the drop, where a close is at once\";\n return soon ? null : `the program closed its socket ${left.value} s after the drop, not at once`;\n }\n if (drop.record !== \"write\") return \"the drop wrote no close frame on its socket\";\n return soon ? \"the program closed its socket at the drop, where a close frame leaves it open\" : null;\n};\n\nconst isWrite = (record: KernelRecord): boolean => record.ev === \"tx\";\n\n/** This function gives each ping of the program on the connection of the run, with the kernel's stamp of its write. */\nexport const pingsOf = (\n lines: readonly ProbeEvent[],\n socket: readonly KernelRecord[],\n local: unknown,\n delta: Quantity = NOISE,\n): ProgramEvent[] =>\n lines.filter((line) => line.ev === \"ping\" && onConnection(line, local)).map((line) => eventOf(socket, line, isWrite, delta));\n\n/**\n * This function gives the first ping that got no answer: no segment with data reached the socket\n * after its write, up to `until`. Its write starts the wait of the heartbeat that the fault met.\n */\nexport const firstUnanswered = (\n pings: readonly ProgramEvent[],\n socket: readonly KernelRecord[],\n until: number = Number.POSITIVE_INFINITY,\n): ProgramEvent | undefined =>\n pings\n .filter((ping) => ping.at <= until)\n .find(\n (ping) =>\n !socket.some((record) => record.ev === \"rx\" && (record.seg?.len.value ?? 0) > 0 && record.mono > ping.at && record.mono <= until),\n );\n\n/**\n * This function gives the first drop of the connection by its program. The kernel stamps it as the\n * write of a close frame. Or it stamps the change of the socket out of ESTABLISHED: a close or an\n * abort.\n */\nexport const dropOf = (\n lines: readonly ProbeEvent[],\n socket: readonly KernelRecord[],\n local: unknown,\n delta: Quantity = NOISE,\n): ProgramEvent | undefined => {\n const line = lines.find((each) => each.ev === \"drop\" && onConnection(each, local));\n return line === undefined\n ? undefined\n : eventOf(socket, line, (record) => isWrite(record) || (record.ev === \"state\" && oldState(record) === 1), delta);\n};\n\n/** A reconnect reading is what the client did after the detection, from its lines and from the records of the kernel. */\nexport interface ReconnectReading {\n /** These are the waits of the program before each try: from a `reconnecting` line to the next `dial` line. */\n readonly waits: readonly Quantity[];\n /** This time is from the detection to the first SYN of a new socket. The kernel stamps the change to SYN_SENT at connect(2). */\n readonly syn: Quantity | null;\n /** This time is from the detection to the first new socket of the client that became ESTABLISHED. */\n readonly established: Quantity | null;\n /** These are the times from each `dial` line after the detection to the SYN of its socket: the work of the program before connect(2). */\n readonly connects: readonly Quantity[];\n}\n\n/**\n * This function reads the reconnect of the client after the detection at `detected`. The socket of\n * the run has the cookie `cookie`. The lines of the program count from `from`: the drop line of a\n * drop, which comes before the kernel's stamp of it.\n */\nexport const reconnectOf = (run: Run, detected: number, cookie: number | undefined, from: number = detected): ReconnectReading => {\n const after = run.events.filter((line) => line.t > from);\n const starts = after.filter((line) => line.ev === \"reconnecting\");\n const dials = after.filter((line) => line.ev === \"dial\");\n const waits = starts.flatMap((start, at) => {\n const next = starts[at + 1]?.t ?? Number.POSITIVE_INFINITY;\n const dial = dials.find((each) => each.t >= start.t && each.t < next);\n return dial === undefined ? [] : [between(start.t, dial.t)];\n });\n const fresh = run.kernel.filter((record) => record.netns === run.netns && record.cookie !== cookie && record.ev === \"state\" && record.mono > detected);\n const syns = fresh.filter((record) => newState(record) === 2);\n const established = fresh.find((record) => newState(record) === 1);\n const connects = dials.flatMap((dial) => {\n const syn = syns.find((record) => record.mono >= dial.t);\n return syn === undefined ? [] : [between(dial.t, syn.mono)];\n });\n return {\n waits,\n syn: syns[0] === undefined ? null : between(detected, syns[0].mono),\n established: established === undefined ? null : between(detected, established.mono),\n connects,\n };\n};\n\n/** This function tells how the waits of a reconnect differ from their bands, each widened by δ_abs, or gives null. */\nexport const reconnectFinding = (expected: Reconnects, read: ReconnectReading, delta: Quantity = NOISE): string | null => {\n const noise = valueIn(delta, \"s\");\n const round = (value: number) => Math.round(value * 1e6) / 1e6;\n const found = expected.waits.flatMap((band, at) => {\n const wait = read.waits[at];\n if (wait === undefined) return [`the client made no try ${at + 1} after the detection`];\n const value = valueIn(wait, \"s\");\n const low = round(valueIn(band.low, \"s\") - noise);\n const high = round(valueIn(band.high, \"s\") + noise);\n return value < low || value > high ? [`its wait before try ${at + 1} was ${value} s, outside [${low}, ${high}] s`] : [];\n });\n return found.length === 0 ? null : found.join(\". \");\n};\n\n/**\n * A call reading is what the call in flight of a WebSocket client met: its error, a reply, or\n * nothing in the run. The time counts from the detection to its end.\n */\nexport interface CallReading {\n readonly outcome: string;\n readonly after: Quantity | null;\n}\n\n/** This function reads the call that the client sent into the fault: its `call-error` or `data` line after the request. */\nexport const callOf = (events: readonly ProbeEvent[], request: ProbeEvent, detected: number | undefined): CallReading => {\n const end = events.find((line) => (line.ev === \"call-error\" || line.ev === \"data\") && line.t > request.t);\n if (end === undefined) return { outcome: \"pending\", after: null };\n const outcome = end.ev === \"data\" ? \"a reply\" : String(end[\"message\"] ?? end[\"code\"] ?? \"an error\");\n return { outcome, after: detected === undefined ? null : between(detected, end.t) };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, DriverBand, KernelRecord, LineMatch, ProbeEvent, Reference, Run } from \"./cell.ts\";\nimport type { Measured, Outcome } from \"./observe.ts\";\nimport { INJECT, delayVoids, noiseOf } from \"./verdict.ts\";\n\n/**\n * This module measures a run of a cell whose client is a driver with more than one socket, for\n * example the MongoDB driver. Its monitor, its pool and its round-trip pinger each have their own\n * socket, and the client names each socket by its role. A band counts from an event of one socket,\n * as the kernel records it. It ends at a line of the client, or at the report of the kernel on\n * that socket.\n */\n\n/** A driver socket is one socket of the driver in a run: its role, its local address, its server, and when it opened and ended. */\nexport interface DriverSocket {\n readonly role: string;\n readonly local: string;\n readonly server: string;\n readonly opened: number;\n readonly ended: number | null;\n}\n\n/** A driver step time is the time of one later band of a run. It is null when its line did not come. */\nexport interface DriverStepTime {\n readonly what: string;\n readonly name: string | null;\n readonly elapsed: Quantity | null;\n}\n\n/** These are the errno numbers that the kernel reports to a socket, by the names that predictions use. */\nconst ERRNO: Readonly<Record<number, string>> = {\n 32: \"EPIPE\",\n 101: \"ENETUNREACH\",\n 103: \"ECONNABORTED\",\n 104: \"ECONNRESET\",\n 110: \"ETIMEDOUT\",\n 111: \"ECONNREFUSED\",\n 113: \"EHOSTUNREACH\",\n};\n\nconst between = (from: number, to: number): Quantity => quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\nconst round = (seconds: number): number => Math.round(seconds * 1e6) / 1e6;\n\n/** This function gives the value of a field of a line by its name. A dotted name is a field in a field. */\nexport const fieldOf = (line: unknown, path: string): unknown =>\n path.split(\".\").reduce<unknown>((value, key) => (value !== null && typeof value === \"object\" ? (value as Record<string, unknown>)[key] : undefined), line);\n\n/** This function tells if a line of the client matches a line match: its `ev`, and each field that the match names. */\nexport const matches = (line: ProbeEvent, match: LineMatch): boolean =>\n line.ev === match.ev && Object.entries(match.where ?? {}).every(([path, value]) => fieldOf(line, path) === value);\n\n/** This function says a line match in words, for a finding. */\nexport const wordsOf = (match: LineMatch): string => {\n const where = Object.entries(match.where ?? {}).map(([path, value]) => `${path} ${String(value)}`);\n return where.length === 0 ? `a ${match.ev} line` : `a ${match.ev} line with ${where.join(\", \")}`;\n};\n\n/** This function gives the sockets of the driver in a run, from the `socket` and `socket-end` lines of the client. */\nexport const socketsOf = (events: readonly ProbeEvent[]): DriverSocket[] =>\n events\n .filter((event) => event.ev === \"socket\")\n .map((event) => {\n const local = String(event[\"local\"]);\n const end = events.find((each) => each.ev === \"socket-end\" && each[\"local\"] === local && each.t >= event.t);\n return { role: String(event[\"role\"]), local, server: String(event[\"server\"]), opened: event.t, ended: end?.t ?? null };\n });\n\n/**\n * This function finds the socket of a role at an instant. The pool's socket is the first one, which\n * the client names in its `connected` line. Each other role has the latest socket that opened at\n * the instant or before it.\n */\nexport const socketAt = (events: readonly ProbeEvent[], role: string, at: number): DriverSocket | undefined => {\n const sockets = socketsOf(events);\n if (role === \"pool\") {\n const local = events.find((event) => event.ev === \"connected\")?.[\"local\"];\n return sockets.find((socket) => socket.local === local);\n }\n return sockets.filter((socket) => socket.role === role && socket.opened <= at).at(-1);\n};\n\n/** A kernel place is the namespace and the cookie of a socket, and its records in the run. */\ninterface KernelPlace {\n readonly netns: number;\n readonly cookie: number;\n readonly records: readonly KernelRecord[];\n}\n\nconst placeOf = (run: Run, netns: number, find: (record: KernelRecord) => boolean): KernelPlace | undefined => {\n const cookie = run.kernel.find((record) => record.netns === netns && find(record))?.cookie;\n if (cookie === undefined) return undefined;\n return { netns, cookie, records: run.kernel.filter((record) => record.netns === netns && record.cookie === cookie) };\n};\n\nconst portOf = (address: string): string => address.split(\":\").at(-1) ?? \"\";\n\n/**\n * This function gives the socket of the target for a socket of the client. In a direct pair, its\n * peer is the address of the client socket. Behind a router, its peer is the address of the router\n * with the port of the client socket. The router keeps the port when it is free.\n */\nconst peerPlace = (run: Run, local: string): KernelPlace | undefined => {\n const peer = run.topology === undefined ? local : `${run.topology.targetPeer}:${portOf(local)}`;\n return placeOf(run, run.targetNetns, (record) => record.dst === peer);\n};\n\n/** This function gives the kernel place of a socket of a role at an instant. The role `server` is a socket of the target. */\nconst kernelOf = (run: Run, role: string, at: number): { socket: DriverSocket | undefined; place: KernelPlace | undefined } => {\n if (role === \"server\") {\n const pool = socketAt(run.events, \"pool\", at);\n return { socket: pool, place: pool === undefined ? undefined : peerPlace(run, pool.local) };\n }\n const socket = socketAt(run.events, role, at);\n return { socket, place: socket === undefined ? undefined : placeOf(run, run.netns, (record) => record.src === socket.local) };\n};\n\nconst newState = (record: KernelRecord): number | undefined => (record as { readonly new?: number }).new;\nconst oldState = (record: KernelRecord): number | undefined => (record as { readonly old?: number }).old;\n\n/** This function gives the instant that a run starts to look for its outcome. It is the fault, the reply before it, or the start of the client. */\nconst outsetOf = (run: Run): number => {\n const ready = run.events.find((event) => event.ev === \"ready\");\n return run.fault?.t0 ?? ready?.t ?? run.events[0]?.t ?? 0;\n};\n\n/**\n * This function gives the instant of a reference in a run, on a socket of the driver:\n * - `last-received`: the last segment that the socket received before the fault held. A band of\n * the kernel counts from the last segment of any kind. A band that a line ends is a timer of\n * the driver. It counts from the last segment with data that the socket received or sent.\n * - `request-sent`: the send of the operation into the fault, the second `sent` line.\n * - `first-retransmission`: the first retransmission of the RTO timer on the socket.\n * - `peer-closed`: the change of state of the socket of the target, when its process dies.\n * - `fault`: the start of the commit of the fault. `client-start`: the `connecting` line.\n */\nexport const referenceOf = (run: Run, from: Reference, role: string, data: boolean): number | undefined => {\n const ready = run.events.find((event) => event.ev === \"ready\");\n const before = run.fault?.t1 ?? ready?.t ?? Infinity;\n if (from === \"fault\") return run.fault?.t0;\n if (from === \"client-start\") return (run.events.find((event) => event.ev === \"connecting\") ?? run.events[0])?.t;\n if (from === \"request-sent\") return run.events.filter((event) => event.ev === \"sent\")[1]?.t;\n const { socket, place } = kernelOf(run, role, before);\n if (place === undefined) return undefined;\n if (from === \"last-received\") {\n // the idle timer of a Node socket starts again at each read and at each write of data\n const counts = (record: KernelRecord) =>\n data ? (record.ev === \"rx\" || record.ev === \"tx\") && (record.seg?.len.value ?? 0) > 0 : record.ev === \"rx\";\n return place.records.filter((record) => record.mono <= before && counts(record)).at(-1)?.mono;\n }\n if (from === \"first-retransmission\") {\n const request = run.events.filter((event) => event.ev === \"sent\")[1]?.t ?? before;\n return place.records.find((record) => record.ev === \"retrans\" && record.why === \"rto\" && record.mono > request)?.mono;\n }\n // the close of the peer: the socket of the target leaves ESTABLISHED\n const peer = socket === undefined ? undefined : peerPlace(run, socket.local);\n return peer?.records.find((record) => record.ev === \"state\" && oldState(record) === 1)?.mono;\n};\n\n/** This function gives the first line of the client that matches, after an instant. */\nconst lineAfter = (events: readonly ProbeEvent[], match: LineMatch, after: number): ProbeEvent | undefined =>\n events.find((event) => event.t > after && matches(event, match));\n\n/** This function gives the name of the error of a line: the field that the match names, or `name`. */\nconst nameOf = (line: ProbeEvent, match: LineMatch): string => String(fieldOf(line, match.name ?? \"name\") ?? line.ev);\n\n/** This function gives the report of the kernel on a socket after an instant: an error, or the end of the file (CLOSE_WAIT). */\nconst reportOf = (place: KernelPlace, after: number): { at: number; outcome: Outcome } | undefined => {\n const report = place.records.find(\n (record) => record.mono > after && ((record.ev === \"error\" && (record.err ?? 0) !== 0) || (record.ev === \"state\" && newState(record) === 8)),\n );\n if (report === undefined) return undefined;\n return report.ev === \"error\"\n ? { at: report.mono, outcome: { kind: \"error\", errno: ERRNO[report.err!] ?? `E${report.err}` } }\n : { at: report.mono, outcome: { kind: \"eof\" } };\n};\n\n/** This function gives the latest instant of the bands of a driver prediction, in seconds from their references. */\nexport const longestOf = (cell: Cell): number => {\n const { prediction } = cell;\n if (prediction.detects === null) return valueIn(prediction.window, \"s\");\n const steps = (prediction.driver?.then ?? []).map((step) => valueIn(step.high, \"s\"));\n const own = prediction.detects ? valueIn(prediction.high, \"s\") : valueIn(prediction.window, \"s\");\n return Math.max(own, ...steps);\n};\n\n/**\n * This function tells if a run of a driver cell has its outcome, so that its wait can stop. For a\n * detection, the outcome is the end of its band and of each later band. A silent prediction waits\n * for its window.\n */\nexport const settled = (cell: Cell, run: Run): boolean => {\n const { prediction } = cell;\n if (prediction.detects !== true || prediction.driver === undefined) return false;\n const driver: DriverBand = prediction.driver;\n const after = outsetOf(run);\n const ended =\n driver.ends === undefined\n ? (() => {\n const { place } = kernelOf(run, driver.socket, run.fault?.t1 ?? after);\n return place !== undefined && reportOf(place, after) !== undefined;\n })()\n : lineAfter(run.events, driver.ends, after) !== undefined;\n return ended && (driver.then ?? []).every((step) => lineAfter(run.events, step.ends, after) !== undefined);\n};\n\n/**\n * This function examines the keepalive timer of a socket between two instants. A firing two\n * jiffies or more behind its wheel slot was delayed by the host, which stopped a vCPU. A firing\n * that found the socket held by its owner put itself off by HZ/20. Either way, the run measured\n * something other than the timer.\n */\nconst delayed = (place: KernelPlace, from: number, to: number, hz: number | undefined, noise: Quantity): string | null => {\n const firings = place.records.filter((record) => record.ev === \"timer\" && record.what === \"keepalive\" && record.mono >= from && record.mono <= to);\n const late = firings.find((record) => delayVoids(record.behind?.value ?? 0, hz, noise));\n if (late !== undefined) return `its keepalive timer ran ${late.behind!.value} jiffies behind its wheel slot: the VM delayed it, not the kernel`;\n const held = firings.find((record) => record.owned === true);\n return held === undefined ? null : \"its keepalive timer found the socket held by its owner and put itself off by HZ/20: the run measured the owner, not the timer\";\n};\n\n/**\n * This function reads one run of a driver cell.\n * - The band of the cell counts from its reference on its socket. It ends at its line, or at the\n * report of the kernel on that socket.\n * - Each later band (`then`) gets the same test. A later band outside its band, or absent, is a finding.\n * - A line that the prediction names as quiet, after the fault and in its window, is a finding.\n * So is a flow of an open socket of the driver that a router forgot, for a silent prediction.\n * - A silent prediction can time a reply (`reply`), and the verdict tests it against `replies`.\n *\n * A run is void when the prober lost records, or when the client did not get its first reply. It\n * is also void when the operation went out before the fault held. A late lift of a hold and a\n * keepalive probe before the fault also make it void. So do a delayed timer and a round trip\n * above the assumption.\n */\nexport const measureDriver = (cell: Cell, run: Run): Measured => {\n const { prediction } = cell;\n if (prediction.detects === null || prediction.driver === undefined) throw new Error(`${cell.name} has no driver band`);\n const driver = prediction.driver;\n const { events } = run;\n const ready = events.find((event) => event.ev === \"ready\");\n const request = events.filter((event) => event.ev === \"sent\")[1];\n const after = outsetOf(run);\n const before = run.fault?.t1 ?? ready?.t ?? after;\n const { place } = kernelOf(run, driver.socket, before);\n const records = place?.records ?? [];\n const pool = kernelOf(run, \"pool\", before).place;\n const srtt = pool?.records.find((record) => record.tcp?.srtt !== undefined)?.tcp?.srtt;\n const rtt = srtt === undefined ? null : quantity(valueIn(srtt, \"us\") / 1000, \"ms\");\n const base = {\n environment: run.environment,\n index: run.index,\n probes: records.filter((record) => record.ev === \"probe\" && record.what === \"keepalive\").length,\n retransmissions: records.filter((record) => record.ev === \"retrans\").length,\n acks: request === undefined ? 0 : records.filter((record) => record.ev === \"rx\" && record.mono > request.t).length,\n rtt,\n };\n const nothing = { outcome: { kind: \"silent\" } as const, elapsed: null, socket: null, noticed: null, steps: [], finding: null };\n\n if (cell.before !== true && ready === undefined) return { ...base, ...nothing, void: \"the client never had its reply\" };\n if (run.lost === null) return { ...base, ...nothing, void: \"the prober wrote no clock line after the run, so nothing says what it dropped\" };\n if (run.lost > 0) return { ...base, ...nothing, void: `the prober dropped ${run.lost} records` };\n\n let voided: string | null = null;\n if (cell.probe.mode !== \"idle\" && cell.before !== true) {\n if (request === undefined) voided = \"the operation was never sent into the fault\";\n else if (run.fault !== null && run.fault.t1 > request.t) voided = \"the operation went out before the fault held\";\n }\n const keepalive = records.find((record) => record.ev === \"probe\" && record.what === \"keepalive\");\n if (voided === null && run.fault !== null && keepalive !== undefined && keepalive.mono < run.fault.t1)\n voided = \"a keepalive probe went out before the fault held\";\n if (voided === null && cell.hold !== undefined && run.fault !== null) {\n const heal = run.heal ?? null;\n if (heal === null) voided = \"the fault never lifted\";\n else {\n const late = (heal.t0 - run.fault.t0) / 1e9 - valueIn(cell.hold, \"s\");\n if (late > valueIn(INJECT, \"s\")) voided = `the lift came ${Math.round(late * 1e4) / 10} ms after its hold: the injector was late, not the client`;\n }\n }\n const assumed = prediction.rtt;\n if (voided === null && assumed !== undefined && rtt !== null && valueIn(rtt, \"ms\") > valueIn(assumed.high, \"ms\"))\n voided = `rtt-over-assumption: its first round trip was ${rtt.value} ms, above the ${valueIn(assumed.high, \"ms\")} ms that the prediction assumes`;\n\n const findings: string[] = [];\n const noise = valueIn(noiseOf(cell), \"s\");\n // the end of the window: the window of a silent prediction, or the end of the run\n const until = prediction.detects ? Infinity : after + valueIn(prediction.window, \"s\") * 1e9;\n for (const match of driver.quiet ?? []) {\n const line = events.find((event) => event.t > after && event.t <= until && matches(event, match));\n if (line !== undefined) findings.push(`${wordsOf(match)} came ${between(after, line.t).value} s after the fault, where none should`);\n }\n if (!prediction.detects && run.middlebox !== undefined) {\n // a router forgets a flow of a socket that is still open: the flow of a live connection is gone\n for (const socket of socketsOf(events)) {\n const forgot = run.middlebox.find(\n (line) =>\n line.ev === \"ct\" &&\n line[\"type\"] === \"DESTROY\" &&\n String(line[\"orig\"] ?? \"\").startsWith(`${socket.local}>`) &&\n line.t > socket.opened &&\n (socket.ended === null || line.t < socket.ended) &&\n line.t <= until,\n );\n if (forgot !== undefined) findings.push(`a router forgot the flow of the ${socket.role} socket ${between(after, forgot.t).value} s after the fault, where it should keep it`);\n }\n }\n const stepTimes: DriverStepTime[] = (driver.then ?? []).map((step) => {\n const line = lineAfter(events, step.ends, after);\n const from = referenceOf(run, step.from, step.socket ?? driver.socket, true);\n if (line === undefined || from === undefined) {\n findings.push(line === undefined ? `${step.what}: ${wordsOf(step.ends)} never came` : `${step.what}: its reference (${step.from}) never came`);\n return { what: step.what, name: line === undefined ? null : nameOf(line, step.ends), elapsed: null };\n }\n const elapsed = between(from, line.t);\n const name = nameOf(line, step.ends);\n if (step.name !== \"OK\" && name !== step.name) findings.push(`${step.what}: it ended with ${name}, not ${step.name}`);\n const [low, high] = [round(valueIn(step.low, \"s\") - noise), round(valueIn(step.high, \"s\") + noise)];\n if (elapsed.value < low || elapsed.value > high) findings.push(`${step.what}: ${elapsed.value} s is outside [${low}, ${high}] s`);\n return { what: step.what, name, elapsed };\n });\n const stepped = stepTimes.length === 0 ? {} : { driver: { steps: stepTimes } };\n\n if (!prediction.detects) {\n if (driver.reply === undefined || prediction.replies === undefined)\n return { ...base, ...nothing, ...stepped, void: voided, finding: findings[0] ?? null };\n const line = lineAfter(events, driver.reply.ends, after);\n const from = referenceOf(run, driver.reply.from, driver.socket, true);\n if (line === undefined || from === undefined)\n return { ...base, ...nothing, ...stepped, void: voided, finding: findings[0] ?? `the reply never came: ${wordsOf(driver.reply.ends)}` };\n return {\n ...base,\n ...nothing,\n ...stepped,\n outcome: { kind: \"answered\" },\n elapsed: between(from, line.t),\n void: voided,\n finding: findings[0] ?? null,\n };\n }\n\n // the band of the cell: its line, or the report of the kernel on its socket\n const line = driver.ends === undefined ? undefined : lineAfter(events, driver.ends, after);\n const report = driver.ends === undefined && place !== undefined ? reportOf(place, after) : undefined;\n if (driver.ends === undefined && place === undefined)\n return { ...base, ...nothing, ...stepped, void: voided ?? `the prober saw nothing of the ${driver.socket} socket`, finding: findings[0] ?? null };\n const ended = line?.t ?? report?.at;\n const outcome: Outcome =\n line !== undefined && driver.ends !== undefined ? { kind: \"error\", errno: nameOf(line, driver.ends) } : (report?.outcome ?? { kind: \"silent\" });\n if (ended === undefined)\n return { ...base, ...nothing, ...stepped, void: voided, finding: findings[0] ?? `nothing detected it: ${prediction.timer} never fired` };\n const reference = referenceOf(run, prediction.from, driver.socket, driver.ends !== undefined);\n if (reference === undefined)\n return { ...base, ...nothing, ...stepped, outcome, void: voided, finding: `it ended without its reference event (${prediction.from} on the ${driver.socket} socket)` };\n if (driver.ends === undefined && prediction.from === \"last-received\" && place !== undefined) voided ??= delayed(place, reference, ended, run.hz, noiseOf(cell));\n const name = outcome.kind === \"error\" ? outcome.errno : outcome.kind === \"eof\" ? \"EOF\" : \"silent\";\n if (prediction.errno !== \"OK\" && name !== prediction.errno) findings.unshift(`it ended with ${name}, not ${prediction.errno}`);\n // the client's own view: its first error line after the report of the kernel\n const seen = report === undefined ? undefined : events.find((event) => event.t >= report.at && (event.ev === \"error\" || event.ev === \"socket-error\"));\n return {\n ...base,\n ...stepped,\n outcome,\n elapsed: between(reference, ended),\n socket: seen === undefined ? null : between(reference, seen.t),\n noticed: report === undefined || seen === undefined ? null : between(report.at, seen.t),\n steps: (prediction.from === \"last-received\" ? records.filter((record) => record.ev === \"probe\" && record.what === \"keepalive\" && record.mono > reference) : []).map(\n (record) => between(reference, record.mono),\n ),\n void: voided,\n finding: findings[0] ?? null,\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, KernelRecord, ProbeEvent, ReplicaSetStage, Run, SentinelEvent, SentinelStage, Stage } from \"./cell.ts\";\nimport { socketsOf } from \"./driver.ts\";\nimport { INJECT, noiseOf } from \"./verdict.ts\";\n\n/**\n * This module measures a run of a group cell. It gives the first write that a primary accepted\n * after the fault, and the timeline of the group. The Sentinels of a group announce its timeline.\n * The logs of the members of a replica set and its client tell it. The module also gives what\n * became of each write.\n */\n\n/** These are the stages of a timeline under Sentinels, in their sequence after a fault. */\nexport const STAGES: readonly SentinelStage[] = [\n \"+sdown\",\n \"+odown\",\n \"+try-failover\",\n \"+elected-leader\",\n \"+selected-slave\",\n \"+promoted-slave\",\n \"+switch-master\",\n \"+convert-to-slave\",\n];\n\n/** An error on the way is one kind of error that the client met after the fault. It has its first time from the fault, and its count. */\nexport interface ErrorOnTheWay {\n readonly name: string;\n readonly first: Quantity;\n readonly count: number;\n}\n\n/**\n * A writes record tells what became of the writes of a run, by their sequence numbers:\n * - `old` counts the writes that the primary of the start of the run accepted after the fault.\n * - `lost` are the writes that a primary accepted and the group did not keep. They are the cost\n * of a split brain. It is null when the group did not converge after the run.\n * - `unknown` are the writes that the client sent and did not see accepted: an error, or no answer.\n * `held` tells if the group kept each one, or null when it did not converge.\n */\nexport interface Writes {\n readonly sent: number;\n readonly acked: number;\n readonly old: number;\n readonly lost: readonly number[] | null;\n readonly unknown: ReadonlyArray<{ readonly n: number; readonly held: boolean | null }>;\n}\n\n/** A group measurement is what one run of a group cell measured, beside its time. */\nexport interface GroupMeasured {\n /** These are the member that was the primary at the start, and the member of the first accepted write after the fault. */\n readonly from: string;\n readonly to: string | null;\n /** These are the times of each stage from the fault: the first announcement by any Sentinel. A stage that no Sentinel announced is absent. */\n readonly stages: Partial<Record<Stage, Quantity>>;\n readonly writes: Writes;\n readonly errors: readonly ErrorOnTheWay[];\n /** These are the delays of the stamps of the observer after the writes of the Sentinels, for a calibration. */\n readonly lags?: readonly Quantity[];\n}\n\nconst between = (from: number, to: number): Quantity => quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\n\n/** This function gives the member whose address and port is `at`, or null. */\nconst memberAt = (run: Run, at: unknown): string | null => {\n if (typeof at !== \"string\") return null;\n const ip = at.split(\":\")[0];\n return Object.entries(run.group?.members ?? {}).find(([, part]) => part.ip === ip)?.[0] ?? null;\n};\n\n/** This function gives the time of each stage of a timeline from an instant: the first announcement of each, after the instant. */\nexport const stagesOf = (timeline: readonly SentinelEvent[], from: number): Partial<Record<Stage, Quantity>> => {\n const out: Partial<Record<Stage, Quantity>> = {};\n for (const line of timeline) {\n if (line.ev !== \"sentinel\" || line.t < from) continue;\n const stage = STAGES.find((each) => each === line.event);\n if (stage === undefined || out[stage] !== undefined) continue;\n // the down states count for the primary only, not for a replica or a Sentinel\n if ((stage === \"+sdown\" || stage === \"+odown\") && !String(line.detail ?? \"\").startsWith(\"master \")) continue;\n out[stage] = between(from, line.t);\n }\n return out;\n};\n\n/** These are the stages of the timeline of a replica set, in their usual sequence after a fault. */\nexport const REPLICA_SET_STAGES: readonly ReplicaSetStage[] = [\n \"member-down\",\n \"election-start\",\n \"elected\",\n \"caught-up\",\n \"writable\",\n \"old-primary-steps-down\",\n \"discovered\",\n \"written-off\",\n \"first-write\",\n];\n\n/**\n * A member mark is how a log line of mongod marks a stage: by its id, or by its message. The\n * message is the fallback for a version whose ids differ. `at` tells where the line comes from:\n * the old primary, or another member.\n */\ninterface MemberMark {\n readonly ids: readonly number[];\n readonly message: RegExp;\n readonly at: \"old\" | \"other\";\n}\n\n/** These are the log lines of mongod that mark each stage of a member. */\nexport const MEMBER_MARKS: Readonly<Record<Exclude<ReplicaSetStage, \"discovered\" | \"written-off\" | \"first-write\">, MemberMark>> = {\n \"member-down\": { ids: [21216], message: /now in state DOWN/, at: \"other\" },\n \"election-start\": { ids: [4615652, 21437], message: /^Starting an election|Skipping dry run|Conducting a dry run/, at: \"other\" },\n elected: { ids: [21450], message: /^Election succeeded/, at: \"other\" },\n \"caught-up\": { ids: [21363], message: /^Exited primary catch-up mode/, at: \"other\" },\n writable: { ids: [21331], message: /^Transition to primary complete/, at: \"other\" },\n \"old-primary-steps-down\": { ids: [21809, 21475, 962660], message: /relinquishing primary|Stepping down from primary/, at: \"old\" },\n};\n\n/** This function tells if a log line of a member marks a stage, with the old primary of the run. */\nconst marks = (line: SentinelEvent, stage: keyof typeof MEMBER_MARKS, old: string, oldAddress: string): boolean => {\n const mark = MEMBER_MARKS[stage];\n const member = String(line[\"member\"] ?? \"\");\n if ((mark.at === \"old\") !== (member === old)) return false;\n const attr = (line[\"attr\"] ?? {}) as Record<string, unknown>;\n // the DOWN state counts for the old primary only, as another member sees it\n if (stage === \"member-down\" && !JSON.stringify(attr).includes(oldAddress)) return false;\n // the generic state change of the old primary: PRIMARY to another state\n if (stage === \"old-primary-steps-down\" && Number(line[\"id\"]) === 21358 && attr[\"oldState\"] === \"PRIMARY\") return true;\n return mark.ids.includes(Number(line[\"id\"])) || mark.message.test(String(line[\"msg\"] ?? \"\"));\n};\n\n/**\n * This function gives the time of each stage of a replica set from an instant, in a run. The stages\n * of the members come from their log lines (`ev` \"mongod\"), whose times the lab puts on the\n * monotonic clock. The stages of the client come from its lines:\n * - `discovered`: a member other than the old primary becomes RSPrimary in the view of the client.\n * - `written-off`: the first heartbeat of the client to the old primary that fails.\n * - `first-write`: the first write that a primary accepts, of a write that the client sent after\n * the commit of the fault (`sentAfter`). It is on a new primary when `lands` is `new`.\n */\nexport const replicaSetStagesOf = (\n run: Run,\n from: number,\n sentAfter: number,\n lands: \"new\" | \"same\",\n): Partial<Record<Stage, Quantity>> => {\n const out: Partial<Record<Stage, Quantity>> = {};\n const old = run.group?.primary ?? \"\";\n const oldIp = run.group?.members[old]?.ip ?? \"\";\n const oldAddress = `${oldIp}:`;\n for (const line of run.group?.timeline ?? []) {\n if (line.ev !== \"mongod\" || line.t < from) continue;\n for (const stage of Object.keys(MEMBER_MARKS) as Array<keyof typeof MEMBER_MARKS>)\n if (out[stage] === undefined && marks(line, stage, old, oldAddress)) out[stage] = between(from, line.t);\n }\n const isOld = (address: unknown): boolean => typeof address === \"string\" && address.startsWith(oldAddress);\n for (const event of run.events) {\n if (event.t < from) continue;\n if (out.discovered === undefined && event.ev === \"sdam\" && event[\"event\"] === \"server-changed\" && !isOld(event[\"address\"])) {\n const next = event[\"next\"] as { readonly type?: string } | null;\n if (next?.type === \"RSPrimary\") out.discovered = between(from, event.t);\n }\n if (out[\"written-off\"] === undefined && event.ev === \"sdam\" && event[\"event\"] === \"heartbeat-failed\" && isOld(event[\"address\"]))\n out[\"written-off\"] = between(from, event.t);\n }\n const accepted = acceptedAfter(run, sentAfter, lands);\n if (accepted !== undefined) out[\"first-write\"] = between(from, accepted.t);\n return out;\n};\n\n/** This function gives the first write that a primary accepted, of a write sent after an instant. It is on a new primary for `new`. */\nconst acceptedAfter = (run: Run, sentAfter: number, lands: \"new\" | \"same\"): ProbeEvent | undefined => {\n const sent = new Map<number, number>();\n for (const event of run.events) if (event.ev === \"sent\") sent.set(Number(event[\"n\"]), event.t);\n const primary = run.group?.primary ?? \"\";\n return run.events.find((event) => {\n if (event.ev !== \"ack\" || (sent.get(Number(event[\"n\"])) ?? -Infinity) <= sentAfter) return false;\n const member = memberAt(run, event[\"at\"]);\n return member !== null && (lands === \"new\" ? member !== primary : true);\n });\n};\n\n/** This function gives what became of each write of a run, from the lines of the client and the keys that the group kept. */\nexport const writesOf = (run: Run, after: number, primary: string): Writes => {\n const { events } = run;\n const sent = events.filter((event) => event.ev === \"sent\");\n // without its done line, the stop ended the last write that the client sent\n const done = events.find((event) => event.ev === \"done\")?.t ?? sent.at(-1)?.t ?? Infinity;\n const acks = new Map<number, ProbeEvent>();\n for (const event of events) if (event.ev === \"ack\") acks.set(Number(event[\"n\"]), event);\n const holds = run.group?.holds ?? null;\n const kept = holds === null ? null : new Set(holds);\n const old = [...acks.values()].filter((event) => event.t > after && memberAt(run, event[\"at\"]) === primary).length;\n const lost = kept === null ? null : [...acks.keys()].filter((n) => !kept.has(n)).sort((a, b) => a - b);\n // a write in flight when the client stopped is not on the way: the stop ended it\n const unknown = sent\n .filter((event) => event.t < done && !acks.has(Number(event[\"n\"])))\n .map((event) => {\n const n = Number(event[\"n\"]);\n return { n, held: kept === null ? null : kept.has(n) };\n });\n return { sent: sent.length, acked: acks.size, old, lost, unknown };\n};\n\n/** This function gives the errors that the client met after an instant: each kind, its first time, and its count. */\nexport const errorsOf = (events: readonly ProbeEvent[], after: number): ErrorOnTheWay[] => {\n const done = events.find((event) => event.ev === \"done\")?.t ?? Infinity;\n const kinds = new Map<string, { first: number; count: number }>();\n for (const event of events) {\n if (event.t <= after || event.t >= done) continue;\n const name =\n event.ev === \"error\"\n ? String(event[\"errno\"] ?? event[\"name\"] ?? \"error\")\n : event.ev === \"fail\"\n ? String(event[\"name\"] ?? \"fail\")\n : event.ev === \"sentinel-error\"\n ? \"sentinel-error\"\n : null;\n if (name === null) continue;\n const seen = kinds.get(name);\n if (seen === undefined) kinds.set(name, { first: event.t, count: 1 });\n else seen.count += 1;\n }\n return [...kinds].map(([name, { first, count }]) => ({ name, first: between(after, first), count }));\n};\n\n/**\n * This function gives the delay of each stamp of the observer after the write of its Sentinel that\n * the observer read. That write is the latest write of the Sentinel's socket toward the observer,\n * at the stamp or before it. The prober records each write (`tx`) on the clock of the stamps.\n */\nexport const lagsOf = (run: Run, kernel: readonly KernelRecord[]): Quantity[] => {\n const timeline = run.group?.timeline ?? [];\n const locals = new Map<string, string>();\n for (const line of timeline) if (line.ev === \"subscribed\") locals.set(String(line.sentinel), String(line[\"local\"]));\n const out: Quantity[] = [];\n for (const line of timeline) {\n if (line.ev !== \"sentinel\") continue;\n const sentinel = String(line.sentinel);\n const local = locals.get(sentinel);\n if (local === undefined) continue;\n const write = kernel\n .filter((record) => record.ev === \"tx\" && record.src === sentinel && record.dst === local && record.mono <= line.t)\n .at(-1);\n if (write !== undefined) out.push(between(write.mono, line.t));\n }\n return out;\n};\n\n/** This type is the measurement of observe.ts, which a group run fills. */\nexport interface GroupReading {\n readonly outcome: { readonly kind: \"answered\" } | { readonly kind: \"silent\" };\n readonly elapsed: Quantity | null;\n readonly socket: Quantity | null;\n readonly noticed: Quantity | null;\n readonly void: string | null;\n readonly finding: string | null;\n readonly group: GroupMeasured;\n}\n\n/**\n * This function reads one run of a group cell.\n * - The reference is the start of the commit of the fault, as the injector stamped it. For a\n * stall, it is the first write into the fault, as for the stall of a direct pair.\n * - The outcome is the first write that a primary accepted after the fault. The prediction says\n * which primary: a new one, or the same one after the lift. In a replica set, it is the first\n * accepted write of a write that the client sent after the commit of the fault.\n * - The stages come from the Sentinels, or from the logs of the members of a replica set and the\n * lines of its client (`replicaSetStagesOf`).\n * - Its time is the last segment with data that the client's socket to that primary received\n * before the client saw the acceptance. The stamp of the client is the fallback.\n * - A run is void when the prober lost records, or when the client did not become ready. It is\n * also void when a Sentinel entered its TILT mode, or when the election split its votes. A band\n * from the fault whose commit took longer than δ_abs is void too. So is a band whose hold lifted\n * more than δ_abs late.\n */\nexport const measureGroup = (cell: Cell, run: Run): GroupReading => {\n const { events, kernel } = run;\n const { prediction } = cell;\n const expected = prediction.group;\n const primary = run.group?.primary ?? \"\";\n const ready = events.find((event) => event.ev === \"ready\");\n const t0 = run.fault?.t0;\n const empty: GroupMeasured = {\n from: primary,\n to: null,\n stages: {},\n writes: { sent: 0, acked: 0, old: 0, lost: null, unknown: [] },\n errors: [],\n };\n const silent = { outcome: { kind: \"silent\" } as const, elapsed: null, socket: null, noticed: null, finding: null };\n if (ready === undefined) return { ...silent, void: \"the client never became ready\", group: empty };\n if (t0 === undefined || run.fault === null) return { ...silent, void: \"the fault never committed\", group: empty };\n if (run.lost === null)\n return { ...silent, void: \"the prober wrote no clock line after the run, so nothing says what it dropped\", group: empty };\n if (run.lost > 0) return { ...silent, void: `the prober dropped ${run.lost} records`, group: empty };\n const timeline = run.group?.timeline ?? [];\n const heal = run.heal ?? null;\n const lands = expected?.lands ?? \"new\";\n // a replica set has no authority: its members and the client tell its stages\n const replica = cell.replicaSet !== undefined;\n const stall = prediction.detects === false && prediction.recovers !== undefined;\n const stages = replica ? replicaSetStagesOf(run, t0, run.fault.t1, lands) : stagesOf(timeline, t0);\n // the first write that a primary accepted after the fault: on a new primary, or on the same one\n // after the lift. In a replica set, it is the first write sent after the commit of the fault\n // that a primary accepts: a partial cut leaves the old primary its writes\n const after = lands === \"same\" && heal !== null && (!replica || stall) ? heal.t0 : t0;\n const sentAt = new Map<number, number>();\n for (const event of events) if (event.ev === \"sent\") sentAt.set(Number(event[\"n\"]), event.t);\n const accepted = events.find((event) => {\n if (event.ev !== \"ack\" || event.t <= after) return false;\n if (replica && (sentAt.get(Number(event[\"n\"])) ?? -Infinity) <= run.fault!.t1) return false;\n const member = memberAt(run, event[\"at\"]);\n return member !== null && (lands === \"new\" ? member !== primary : true);\n });\n const to = accepted === undefined ? null : memberAt(run, accepted[\"at\"]);\n // the first write of a replica set is the outcome itself: a stall's first write is the write into it\n if (replica) {\n delete stages[\"first-write\"];\n if (accepted !== undefined) stages[\"first-write\"] = between(t0, accepted.t);\n }\n const group: GroupMeasured = {\n from: primary,\n to,\n stages,\n writes: writesOf(run, t0, primary),\n errors: errorsOf(events, t0),\n ...(cell.calibrates === \"sentinels\" ? { lags: lagsOf(run, kernel) } : {}),\n };\n let voided: string | null = null;\n const tilt = timeline.find((line) => line.ev === \"sentinel\" && line.event === \"+tilt\" && line.t >= t0);\n if (tilt !== undefined) voided = `a Sentinel entered TILT ${between(t0, tilt.t).value} s after the fault: the host delayed it, not its timers`;\n const split = timeline.find((line) => line.ev === \"sentinel\" && line.event === \"-failover-abort-not-elected\" && line.t >= t0);\n const elected = timeline.find((line) => line.ev === \"sentinel\" && line.event === \"+elected-leader\" && line.t >= t0);\n if (voided === null && split !== undefined && (elected === undefined || elected.t > split.t))\n voided = \"the election split its votes and runs again after 2 × failover-timeout, which the band does not bound\";\n // a Sentinel starts a failover in the tick of its +odown, unless an attempt of its own came less\n // than 2 × failover-timeout before: then the run measured an earlier run, not its own fault\n const odown = stages[\"+odown\"];\n if (voided === null && odown !== undefined && stages[\"+try-failover\"] === undefined)\n voided = \"no Sentinel started a failover after +odown: each was in the hold-off of 2 × failover-timeout after an earlier attempt\";\n // a hold lifts at its instant in the VM: a band assumes that instant, and a lift later than the\n // noise moved each stage after it\n if (voided === null && cell.hold !== undefined && prediction.detects !== null) {\n if (heal === null) voided = \"the fault never lifted\";\n else {\n const late = (heal.t0 - t0) / 1e9 - valueIn(cell.hold, \"s\");\n if (late > valueIn(INJECT, \"s\"))\n voided = `the lift came ${Math.round(late * 1e4) / 10} ms after its hold: the injector was late, not the group`;\n }\n }\n // a band holds for the round trips that it assumes: the first one of the client's socket to the old primary\n const assumed = prediction.detects === null ? undefined : prediction.rtt;\n const first = kernel.find(\n (record) =>\n record.netns === run.netns &&\n record.dst === `${run.group?.members[primary]?.ip}:${(cell.group ?? cell.replicaSet)?.port}` &&\n record.tcp?.srtt !== undefined,\n );\n const rtt = first?.tcp?.srtt === undefined ? null : valueIn(first.tcp.srtt, \"us\") / 1000;\n if (voided === null && assumed !== undefined && rtt !== null && rtt > valueIn(assumed.high, \"ms\"))\n voided = `rtt-over-assumption: its first round trip was ${rtt} ms, above the ${valueIn(assumed.high, \"ms\")} ms that the prediction assumes`;\n if (voided === null && prediction.detects === true && prediction.from === \"fault\" && (run.fault.t1 - run.fault.t0) / 1e6 > valueIn(INJECT, \"ms\"))\n voided = `the commit of the fault took ${Math.round((run.fault.t1 - run.fault.t0) / 1e5) / 10} ms, longer than the noise of the pipeline: its start is not known`;\n\n // the reference: the fault, or for a stall the first write into it\n let reference = t0;\n if (prediction.detects === false && prediction.recovers !== undefined) {\n const into = events.find((event) => event.ev === \"sent\" && event.t > run.fault!.t1);\n if (into === undefined || (heal !== null && into.t > heal.t0)) {\n voided ??= \"the write in flight went out before the fault held\";\n } else {\n reference = into.t;\n // the stall must end where the prediction assumed: after the firing before, and before its own\n const { heal: bounds } = prediction.recovers;\n if (heal === null) voided ??= \"the stall never ended\";\n else if (\n valueIn(between(reference, heal.t0), \"s\") <= valueIn(bounds.low, \"s\") ||\n valueIn(between(reference, heal.t1), \"s\") >= valueIn(bounds.high, \"s\")\n )\n voided ??= `the stall ended ${between(reference, heal.t0).value}–${between(reference, heal.t1).value} s after the write, outside the ${bounds.low.value}–${bounds.high.value} s its prediction assumed`;\n }\n }\n\n // findings: a failover where none should be, a client on the wrong primary, an error out of its band\n const findings: string[] = [];\n // a failover: a Sentinel switches the primary, or another member of a replica set wins an election\n const switched = replica ? stages[\"elected\"] : stages[\"+switch-master\"];\n if (expected?.failover === false && switched !== undefined)\n findings.push(`the group failed over ${switched.value} s after the fault, where it should not`);\n for (const stage of expected?.quiet ?? []) {\n const at = stages[stage];\n if (at !== undefined) findings.push(`${stage} came ${at.value} s after the fault, where it should not`);\n }\n if (accepted !== undefined && lands === \"same\" && to !== primary)\n findings.push(`the client wrote to ${to ?? String(accepted[\"at\"])}, where it should stay on ${primary}`);\n for (const error of expected?.errors ?? []) {\n const met = group.errors.find((each) => each.name === error.name);\n const noise = valueIn(noiseOf(cell), \"s\");\n if (met === undefined) findings.push(`the client never met ${error.name}`);\n else if (met.first.value < valueIn(error.low, \"s\") - noise || met.first.value > valueIn(error.high, \"s\") + noise)\n findings.push(`the client met ${error.name} ${met.first.value} s after the fault, outside [${valueIn(error.low, \"s\")}, ${valueIn(error.high, \"s\")}] s`);\n }\n if (accepted === undefined) {\n if (prediction.detects !== null)\n findings.push(lands === \"new\" ? \"the client never wrote to a new primary in the window\" : \"the client never wrote again in the window\");\n return { ...silent, void: voided, finding: findings[0] ?? null, group };\n }\n // the kernel's time of the acceptance: the last segment with data from that primary at the\n // client's stamp or before it. The monitor and the pinger of a driver have their own sockets to\n // each member, thus only the sockets of the pool count\n const address = String(accepted[\"at\"]);\n const others = new Set(socketsOf(events).filter((socket) => socket.role !== \"pool\").map((socket) => socket.local));\n const segment = kernel\n .filter(\n (record) =>\n record.netns === run.netns &&\n record.ev === \"rx\" &&\n record.dst === address &&\n !others.has(String(record.src)) &&\n (record.seg?.len.value ?? 0) > 0 &&\n record.mono <= accepted.t &&\n record.mono > after,\n )\n .at(-1);\n const at = segment?.mono ?? accepted.t;\n return {\n outcome: { kind: \"answered\" },\n elapsed: between(reference, at),\n socket: between(reference, accepted.t),\n noticed: segment === undefined ? null : between(segment.mono, accepted.t),\n void: voided,\n finding: findings[0] ?? null,\n group,\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { CallReading, ReconnectReading } from \"./application.ts\";\nimport { callOf, dropFinding, dropOf, firstUnanswered, leftAfter, pingsOf, programLines, reconnectFinding, reconnectOf } from \"./application.ts\";\nimport type { Cell, KernelRecord, ProbeEvent, Reference, Run } from \"./cell.ts\";\nimport type { DriverStepTime } from \"./driver.ts\";\nimport { measureDriver } from \"./driver.ts\";\nimport type { GroupMeasured } from \"./timeline.ts\";\nimport { measureGroup } from \"./timeline.ts\";\nimport { INJECT, delayVoids, noiseOf, requestWords } from \"./verdict.ts\";\n\n/**\n * An outcome tells how the wait of a run ended. The socket failed with an errno, or it read the\n * end of the file. Or nothing came, or the reply came back after a stall. The observer can also\n * close the connection itself (`closed`), or its program can drop it on its own timer (`dropped`).\n */\nexport type Outcome =\n | { readonly kind: \"error\"; readonly errno: string }\n | { readonly kind: \"eof\" }\n | { readonly kind: \"silent\" }\n | { readonly kind: \"answered\" }\n | { readonly kind: \"closed\" }\n | { readonly kind: \"dropped\" };\n\n/**\n * A request reading is what the second request of an HTTP client met, from the lines of the\n * client. It holds the connection of its first try, a new try, and its outcome. `gap` is the time\n * from the end of the first reply to the send of the second request.\n */\nexport interface RequestReading {\n readonly connection: \"reused\" | \"new\";\n readonly retried: boolean;\n readonly error: string | null;\n readonly pending: boolean;\n readonly gap: Quantity | null;\n}\n\n/** A measurement is what one run measured, against the prediction of its cell. */\nexport interface Measured {\n readonly environment: number;\n readonly index: number;\n readonly outcome: Outcome;\n /**\n * This time is from the reference event of the prediction to the detection, both as the kernel\n * stamped them in the records of the prober. It is null when nothing detected the fault.\n */\n readonly elapsed: Quantity | null;\n /** This time is the same interval from the TCP_INFO samples of the client, the second vantage. */\n readonly socket: Quantity | null;\n /** This time is how long the client took to see the error after the kernel reported it. */\n readonly noticed: Quantity | null;\n /**\n * These are the steps of the kernel after the reference: each keepalive probe, or each firing of\n * the RTO timer. After a stall, they are each retransmission from the send, the loss probe first.\n */\n readonly steps: readonly Quantity[];\n /**\n * These are the reply times of a measurement-only cell. For each request after the first, a\n * sample is the time from its send to the first data after it.\n */\n readonly samples?: readonly Quantity[];\n /** This count is the number of keepalive probes that the kernel sent. */\n readonly probes: number;\n /** This count is the number of segments that the kernel sent again. */\n readonly retransmissions: number;\n /** This count is the number of segments that the socket received after the request. */\n readonly acks: number;\n /** This count is the number of bytes that the client read after the request. */\n readonly bytes?: number;\n /**\n * This time is when a router of the topology forgot the flow of the client. It counts from the\n * last segment that the client received before the fault. It is null when no router forgot the flow.\n */\n readonly forgot?: Quantity | null;\n /**\n * This time is the first round trip of the socket of the observer. It is the smoothed round trip\n * in its first record, which is the sample of the handshake. It is null when no record has it.\n */\n readonly rtt?: Quantity | null;\n /** This record tells what reordering did in a run of a measurement-only cell, from the records of its two ends. */\n readonly reordering?: Reordering;\n /** These are the times of the proxy of a calibration cell, one for each request and its reply. */\n readonly crossings?: Crossings;\n /** This record is what a run of a group cell measured: the stages of its timeline, and what became of each write. */\n readonly group?: GroupMeasured;\n /** This record is what the second request of an HTTP client met, for a cell whose prediction says it. */\n readonly request?: RequestReading | null;\n /** This record is the time of each later band of a driver cell (`DriverBand.then`). */\n readonly driver?: { readonly steps: readonly DriverStepTime[] };\n /** This record is what the client did after the detection, for a prediction that says when it reconnects. */\n readonly reconnect?: ReconnectReading;\n /** This record is what the call in flight of a WebSocket client met. */\n readonly call?: CallReading;\n /**\n * This time is from the drop line of the program to the close of its socket by its own side: a\n * FIN or an abort. A prediction that says how the program drops (`drop`) gets it. It is null\n * when the socket did not close itself in the run.\n */\n readonly left?: Quantity | null;\n /** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */\n readonly void: string | null;\n /** This text tells what in the run disagrees with the prediction, other than its timing, or null. */\n readonly finding: string | null;\n}\n\n/**\n * A reordering record is what reordering, duplicates and losses did to the data of a run. The\n * receiver is the client, and the sender is the socket of its peer.\n * - `outOfOrder` counts the arrivals at the client beyond a hole, and `duplicates` counts the\n * arrivals whose data the client had. The prober reads the two from each arrival, against `rcv_nxt`.\n * - An arrival can hold more than one segment. The kernel joins the segments that wait in the\n * backlog of a socket while its owner holds it.\n * - `ooo` is the count of the kernel of the client in segments (`rcv_ooopack`).\n * - `sacked` and `lost` are the most segments that the sender held as SACKed and as lost.\n * - `dsacks` is the DSACK blocks that the sender received, and `fast` its fast retransmissions.\n * - `degree` is the highest reordering degree of the sender (`tp->reordering`), and `seen` the\n * reorderings that it detected (`reord_seen`).\n *\n * The counts of the kernel are null for a record of the prober before version 0.2.\n */\nexport interface Reordering {\n readonly outOfOrder: number;\n readonly duplicates: number;\n readonly ooo: number | null;\n readonly sacked: number | null;\n readonly lost: number | null;\n readonly dsacks: number | null;\n readonly fast: number;\n readonly degree: number | null;\n readonly seen: number | null;\n}\n\n/**\n * A crossings record is the time of a proxy for each request (`up`) and for each reply (`down`).\n * The time of a request starts at its arrival on the socket toward the client. It stops at the\n * write on the socket toward the target. The time of a reply goes the other way. A write is a\n * `tx` record of the prober.\n */\nexport interface Crossings {\n readonly hop: string;\n readonly up: readonly Quantity[];\n readonly down: readonly Quantity[];\n}\n\n/** These are the errno numbers that the kernel reports to a socket, by the names that predictions use. */\nconst ERRNO: Readonly<Record<number, string>> = {\n 32: \"EPIPE\",\n 101: \"ENETUNREACH\",\n 103: \"ECONNABORTED\",\n 104: \"ECONNRESET\",\n 110: \"ETIMEDOUT\",\n 111: \"ECONNREFUSED\",\n 113: \"EHOSTUNREACH\",\n};\n\n/** This function gives the interval between two instants on the one clock, in seconds to the microsecond. */\nconst between = (from: number, to: number): Quantity =>\n quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\n\n/** These functions give the states of a state record: before the change, and after it. TCP_ESTABLISHED is 1, TCP_CLOSE_WAIT 8. */\nconst oldState = (record: KernelRecord): number | undefined => (record as { readonly old?: number }).old;\nconst newState = (record: KernelRecord): number | undefined => (record as { readonly new?: number }).new;\n\nconst count = (event: ProbeEvent | undefined, field: string): number =>\n event === undefined ? 0 : Number(event[field] ?? 0);\n\n/** This function gives the view of the client on the same interval: TCP_INFO, with a sample each 2 ms. */\nconst socketVantage = (\n cell: Cell,\n events: readonly ProbeEvent[],\n ready: ProbeEvent,\n request: ProbeEvent,\n): Quantity | null => {\n const { from } = cell.prediction.detects === true ? cell.prediction : { from: undefined };\n if (from === undefined || from === \"peer-closed\" || from === \"fault\" || from === \"ping-sent\") return null;\n const ended = events.find((event) => event.ev === \"error\" || event.ev === \"eof\");\n const reference =\n from === \"last-received\"\n ? ready\n : from === \"request-sent\"\n ? request\n : events.find(\n (event) =>\n event.ev === \"kernel\" && event.t >= request.t && count(event, \"retransmits\") > 0,\n );\n return ended === undefined || reference === undefined ? null : between(reference.t, ended.t);\n};\n\n/** A socket place is a socket of a run: its namespace and its cookie. */\nexport interface SocketPlace {\n readonly netns: number;\n readonly cookie: number;\n}\n\nconst ipOf = (address: string): string => address.split(\":\")[0]!;\n\n/**\n * This function finds the socket that became ESTABLISHED in a run, in a namespace, whose peer\n * matches. In a topology, an address of a hop serves each run, thus only the socket that the\n * run opened is the socket of the run.\n */\nconst opened = (\n run: Run,\n netns: number,\n peer: (dst: string) => boolean,\n): SocketPlace | undefined => {\n const record = run.kernel.find(\n (each) =>\n each.netns === netns &&\n each.ev === \"state\" &&\n newState(each) === 1 &&\n each.dst !== undefined &&\n peer(each.dst),\n );\n return record?.cookie === undefined ? undefined : { netns, cookie: record.cookie };\n};\n\n/** This function finds the socket of the target for the connection of a run. In a topology, its peer is the last hop. */\nconst targetSocket = (run: Run, local: unknown): SocketPlace | undefined => {\n if (run.topology === undefined) {\n const cookie = run.kernel.find(\n (record) => record.netns === run.targetNetns && record.dst === local,\n )?.cookie;\n return cookie === undefined ? undefined : { netns: run.targetNetns, cookie };\n }\n const peer = run.topology.targetPeer;\n return opened(run, run.targetNetns, (dst) => ipOf(dst) === peer);\n};\n\n/**\n * This function finds the socket of the observer of a cell in a run:\n * - the client socket, by the address that it connected from, in its namespace.\n * - the socket of the target for the same connection, in its namespace.\n * - a socket of a proxy of the topology, toward the client or toward the target.\n */\nexport const socketOf = (cell: Cell, run: Run): SocketPlace | undefined => {\n const local = run.events.find((event) => event.ev === \"connected\")?.[\"local\"];\n const observer = cell.observer ?? \"client\";\n if (observer === \"client\") {\n const cookie = run.kernel.find((record) => record.netns === run.netns && record.src === local)?.cookie;\n return cookie === undefined ? undefined : { netns: run.netns, cookie };\n }\n if (observer === \"server\") return targetSocket(run, local);\n const hop = run.topology?.hops[observer.hop];\n if (hop === undefined) return undefined;\n return observer.toward === \"client\"\n ? opened(run, hop.netns, (dst) => ipOf(dst) === hop.client)\n : opened(run, hop.netns, (dst) => dst === hop.target);\n};\n\n/**\n * This function finds the socket of the peer of the observer: its close is the reference of a FIN.\n * In a direct pair, it is the other end of the connection. In a topology, it is the socket of the\n * target, for the client.\n */\nconst peerOf = (cell: Cell, run: Run): SocketPlace | undefined => {\n const local = run.events.find((event) => event.ev === \"connected\")?.[\"local\"];\n const server = cell.observer === \"server\";\n if (run.topology !== undefined) return server ? undefined : targetSocket(run, local);\n const netns = server ? run.netns : run.targetNetns;\n const cookie = run.kernel.find(\n (record) => record.netns === netns && (server ? record.src : record.dst) === local,\n )?.cookie;\n return cookie === undefined ? undefined : { netns, cookie };\n};\n\n/** This function tells if sequence number a is after b, in the 32-bit space of TCP (`after` in include/net/tcp.h). */\nconst after32 = (a: number, b: number): boolean => ((a - b) | 0) > 0;\n\n/** This function gives the largest value of a count of the kernel in records, or null when no record has it. */\nconst most = (records: readonly KernelRecord[], count: (record: KernelRecord) => number | undefined): number | null =>\n records.reduce<number | null>((top, record) => {\n const value = count(record);\n return value === undefined ? top : Math.max(top ?? 0, value);\n }, null);\n\n/** This function gives what reordering did in a run, from the records of the receiver and of the sender. */\nexport const reorderingOf = (\n receiver: readonly KernelRecord[],\n sender: readonly KernelRecord[],\n): Reordering => {\n const arrivals = receiver.filter(\n (record) => record.ev === \"rx\" && (record.seg?.len.value ?? 0) > 0 && record.tcp?.rcv_nxt !== undefined,\n );\n const outOfOrder = arrivals.filter((record) => after32(record.seg!.seq, record.tcp!.rcv_nxt!)).length;\n const duplicates = arrivals.filter(\n (record) => !after32(record.seg!.seq + record.seg!.len.value, record.tcp!.rcv_nxt!),\n ).length;\n return {\n outOfOrder,\n duplicates,\n ooo: most(receiver, (record) => record.tcp?.rcv_ooopack),\n sacked: most(sender, (record) => record.tcp?.sacked),\n lost: most(sender, (record) => record.tcp?.lost),\n dsacks: most(sender, (record) => record.tcp?.dsack_dups),\n fast: sender.filter((record) => record.ev === \"retrans\" && record.why === \"fast\").length,\n degree: most(sender, (record) => record.tcp?.reordering),\n seen: most(sender, (record) => record.tcp?.reord_seen),\n };\n};\n\n/**\n * This function gives the times of a proxy in a run, for each request after the first. The time\n * of a request starts at its arrival on the socket of the proxy toward the client. It stops at the\n * next write on the socket toward the target. The time of its reply starts at its arrival on the\n * socket toward the target, and it stops at the next write toward the client. A request without\n * each record gives no time.\n */\nexport const crossingsOf = (\n run: Run,\n hop: string,\n toClient: SocketPlace | undefined,\n toTarget: SocketPlace | undefined,\n): Crossings => {\n const up: Quantity[] = [];\n const down: Quantity[] = [];\n if (toClient === undefined || toTarget === undefined) return { hop, up, down };\n const of = (place: SocketPlace) =>\n run.kernel.filter((record) => record.netns === place.netns && record.cookie === place.cookie);\n const near = of(toClient);\n const far = of(toTarget);\n const arrival = (records: readonly KernelRecord[], from: number, to: number) =>\n records.find((record) => record.ev === \"rx\" && (record.seg?.len.value ?? 0) > 0 && record.mono > from && record.mono < to);\n const write = (records: readonly KernelRecord[], from: number, to: number) =>\n records.find((record) => record.ev === \"tx\" && record.mono >= from && record.mono < to);\n const sends = run.events.filter((event) => event.ev === \"sent\").slice(1);\n sends.forEach((send, n) => {\n const next = sends[n + 1]?.t ?? Infinity;\n const request = arrival(near, send.t, next);\n const relayed = request === undefined ? undefined : write(far, request.mono, next);\n if (request === undefined || relayed === undefined) return;\n up.push(between(request.mono, relayed.mono));\n const reply = arrival(far, relayed.mono, next);\n const answered = reply === undefined ? undefined : write(near, reply.mono, next);\n if (reply !== undefined && answered !== undefined) down.push(between(reply.mono, answered.mono));\n });\n return { hop, up, down };\n};\n\n/**\n * This function reads one run against its cell, from the records of the kernel first.\n * - The socket of the run is the socket of its observer. It is the socket that the client connected\n * from, in its namespace, or the socket of the target or of a proxy for that connection.\n * - The fault must hold before the event that it must meet. That is before the request goes into\n * it, and before the first keepalive probe. A live peer answers that probe, and the answer\n * starts the idle time again. A run where the fault was late is void. A run whose records the\n * prober lost is also void.\n * - For an HTTP client, it also reads what the second request met (`requestOf`). An outcome other\n * than the one that the prediction gives is a finding.\n */\nexport const measure = (cell: Cell, run: Run): Measured => {\n const read = measureSocket(cell, run);\n const expected = cell.prediction.request;\n if (cell.probe.http === undefined && expected === undefined) return read;\n const request = requestOf(run.events);\n if (expected === undefined || read.void !== null) return { ...read, request };\n const differs = requestFinding(expected, request);\n const finding = read.finding === null ? differs : differs === null ? read.finding : `${read.finding}. ${differs}`;\n return { ...read, request, finding };\n};\n\n/** This function tells how a request reading differs from its prediction, or gives null. */\nconst requestFinding = (expected: NonNullable<Cell[\"prediction\"][\"request\"]>, read: RequestReading | null): string | null => {\n if (read === null) return `the second request never went out, where it should meet: ${requestWords(expected)}`;\n const same =\n read.connection === expected.connection &&\n read.retried === expected.retried &&\n read.error === expected.error &&\n read.pending === (expected.pending === true);\n return same ? null : `the second request met: ${requestWords(read)}, not ${requestWords(expected)}`;\n};\n\n/**\n * This function reads the second request of an HTTP client from its lines. Its first try is its\n * first `request` line, and each `retry` line is a new try. Its end is its `error` line, or its\n * `replied` line. A request with neither is pending. It gives null when the client wrote no try.\n *\n * A new try that gets no connection writes no `retry` line. Its dial writes a `dial-error` line.\n * Thus a `dial-error` line after the first try and before the end is also a new try.\n */\nexport const requestOf = (events: readonly ProbeEvent[]): RequestReading | null => {\n const of = (event: ProbeEvent) => Number(event[\"i\"]) === 2;\n const tries = events.filter((event) => (event.ev === \"request\" || event.ev === \"retry\") && of(event));\n const first = tries[0];\n if (first === undefined) return null;\n const failed = events.find((event) => event.ev === \"error\" && of(event));\n const replied = events.some((event) => event.ev === \"replied\" && of(event));\n const ready = events.find((event) => event.ev === \"ready\");\n const sent = events.find((event) => event.ev === \"sent\" && of(event));\n const end = failed ?? events.find((event) => event.ev === \"done\" && of(event));\n const dialed = events.some(\n (event) => event.ev === \"dial-error\" && event.t > first.t && (end === undefined || event.t <= end.t),\n );\n return {\n connection: first[\"reused\"] === true ? \"reused\" : \"new\",\n retried: dialed || tries.some((each) => each.ev === \"retry\"),\n error: failed === undefined ? null : String(failed[\"errno\"]),\n pending: failed === undefined && !replied,\n gap: ready === undefined || sent === undefined ? null : between(ready.t, sent.t),\n };\n};\n\n/** This function reads the socket of one run against its cell, as `measure` does without the second request of an HTTP client. */\nconst measureSocket = (cell: Cell, run: Run): Measured => {\n if (cell.group !== undefined || cell.replicaSet !== undefined) {\n const read = measureGroup(cell, run);\n return {\n environment: run.environment,\n index: run.index,\n outcome: read.outcome,\n elapsed: read.elapsed,\n socket: read.socket,\n noticed: read.noticed,\n steps: [],\n probes: 0,\n retransmissions: run.kernel.filter((record) => record.netns === run.netns && record.ev === \"retrans\").length,\n acks: read.group.writes.acked,\n void: read.void,\n finding: read.finding,\n group: read.group,\n };\n }\n // a driver with more than one socket: its bands name their socket and their line\n if (cell.prediction.detects !== null && cell.prediction.driver !== undefined) return measureDriver(cell, run);\n const { events, kernel } = run;\n const ready = events.find((event) => event.ev === \"ready\");\n const local = events.find((event) => event.ev === \"connected\")?.[\"local\"];\n // a client in a gap sends its second request by itself, and its idle connection counts from its first reply\n const inflight = cell.probe.mode !== \"idle\" && cell.probe.mode !== \"gap\";\n const request = inflight ? events.filter((event) => event.ev === \"sent\")[1] : ready;\n const server = cell.observer === \"server\";\n const place = socketOf(cell, run);\n const netns = place?.netns;\n const cookie = place?.cookie;\n const socket = kernel.filter((record) => record.netns === netns && record.cookie === cookie);\n // the socket of the peer for the same connection: a FIN counts from the change of state that its close makes\n const peerPlace = peerOf(cell, run);\n const peerNetns = peerPlace?.netns;\n const peerCookie = peerPlace?.cookie;\n // the bytes that the client read after its request, and the first and last time that data came\n const after = request === undefined ? [] : events.filter((event) => event.ev === \"data\" && event.t > request.t);\n const bytes = after.reduce((sum, event) => sum + Number(event[\"n\"] ?? 0), 0);\n // the end of the flow in a router of the topology: its DESTROY line, by the address of the client\n const forgotten = (run.middlebox ?? []).find(\n (line) =>\n line.ev === \"ct\" &&\n line[\"type\"] === \"DESTROY\" &&\n typeof line[\"orig\"] === \"string\" &&\n (line[\"orig\"] as string).startsWith(`${String(local)}>`),\n );\n const keepalives = socket.filter(\n (record) => record.ev === \"probe\" && record.what === \"keepalive\",\n );\n const retransmitted = socket.filter((record) => record.ev === \"retrans\");\n /**\n * This function examines the timer that drives the measurement: keepalive or retransmit. It\n * finds the first firing in an interval that was two jiffies or more behind its wheel slot. The\n * wheel does a slot in the tick when it is due, thus such a delay comes from the CPU: the host\n * stopped a vCPU. The prediction is about the timing of the kernel, and that firing measured\n * the timing of the host. One jiffy is a tick that occurred while the wheel worked, and δ_abs\n * includes it. The function ignores the other timer: the retransmit timer fires one time, idle,\n * after a reply, and it does not move a keepalive.\n */\n const delayed = (from: number, to: number, timer: \"keepalive\" | \"retransmit\"): string | null => {\n const firings = socket.filter(\n (record) =>\n record.ev === \"timer\" && record.what === timer && record.mono >= from && record.mono <= to,\n );\n const late = firings.find((record) => delayVoids(record.behind?.value ?? 0, run.hz, noiseOf(cell)));\n if (late !== undefined)\n return `its ${timer} timer ran ${late.behind!.value} jiffies behind its wheel slot: the VM delayed it, not the kernel`;\n // a keepalive timer that finds its socket held by its owner sends nothing, and it tries again\n // HZ/20 later (tcp_keepalive_timer). The retransmit timer only gives its work to the release\n // by the owner, some microseconds later\n const held = firings.find((record) => timer === \"keepalive\" && record.owned === true);\n return held === undefined\n ? null\n : \"its keepalive timer found the socket held by its owner and put itself off by HZ/20: the run measured the owner, not the timer\";\n };\n const acks =\n request === undefined\n ? 0\n : socket.filter((record) => record.ev === \"rx\" && record.mono > request.t).length;\n // the first round trip of the observer: the smoothed round trip in its first record, the sample of the handshake\n const srtt = socket.find((record) => record.tcp?.srtt !== undefined)?.tcp?.srtt;\n const rtt = srtt === undefined ? null : quantity(valueIn(srtt, \"us\") / 1000, \"ms\");\n const base = {\n environment: run.environment,\n index: run.index,\n probes: keepalives.length,\n retransmissions: retransmitted.length,\n acks,\n rtt,\n };\n const nothing = {\n outcome: { kind: \"silent\" } as const,\n elapsed: null,\n socket: null,\n noticed: null,\n steps: [],\n finding: null,\n };\n\n if (ready === undefined) return { ...base, ...nothing, void: \"the client never had its reply\" };\n if (request === undefined)\n return { ...base, ...nothing, void: \"the request was never sent into the fault\" };\n if (run.lost === null)\n return {\n ...base,\n ...nothing,\n void: \"the prober wrote no clock line after the run, so nothing says what it dropped\",\n };\n if (run.lost > 0) return { ...base, ...nothing, void: `the prober dropped ${run.lost} records` };\n if (cookie === undefined)\n return { ...base, ...nothing, void: \"the prober saw nothing of this connection\" };\n let voided: string | null = null;\n if (run.fault !== null) {\n // the request of a client must go into the fault. The request of a cell on the server side must\n // reach the server first, and its reply must not reach the client before the fault holds. An\n // mtu-blackhole drops only the long reply, thus its request goes after the fault, as a client's does\n const replyFirst = server && inflight && cell.fault !== \"mtu-blackhole\";\n const replied = events.some(\n (event) => event.ev === \"data\" && event.t > request.t && event.t < run.fault!.t1,\n );\n if (inflight && !replyFirst && run.fault.t1 > request.t)\n voided = \"the request went out before the fault held\";\n else if (replyFirst && run.fault.t1 < request.t)\n voided = \"the request went out into the fault, so the server never had it\";\n else if (replyFirst && replied)\n voided = \"the server's reply arrived before the fault held\";\n else if (keepalives[0] !== undefined && keepalives[0].mono < run.fault.t1)\n voided = \"a keepalive probe went out before the fault held\";\n }\n // a band holds for the first round trips that its prediction assumes. Above them, the run measured another path\n const assumed = cell.prediction.detects === null ? undefined : cell.prediction.rtt;\n if (voided === null && assumed !== undefined && rtt !== null && valueIn(rtt, \"ms\") > valueIn(assumed.high, \"ms\"))\n voided = `rtt-over-assumption: its first round trip was ${rtt.value} ms, above the ${valueIn(assumed.high, \"ms\")} ms that the prediction assumes`;\n\n // the detection as the kernel reported it to the owner of the socket, and as the client saw it.\n // The process of a server or of a proxy tells nothing that the harness reads, thus its kernel tells all\n const remote = cell.observer !== undefined && cell.observer !== \"client\";\n const reported = socket.find((record) => record.ev === \"error\" && (record.err ?? 0) !== 0);\n const seen = remote\n ? undefined\n : events.find((event) => event.ev === \"error\" || event.ev === \"eof\");\n // an end of file without an error is the change of state that the FIN of the peer makes, from\n // ESTABLISHED to CLOSE_WAIT, as the kernel stamps it\n const closeWait = socket.find((record) => record.ev === \"state\" && newState(record) === 8);\n // a close of the observer itself, for a prediction that says it: from ESTABLISHED to FIN_WAIT1,\n // the FIN that its pool sends when it closes an idle connection\n const own =\n (cell.prediction.detects === true && cell.prediction.errno === \"CLOSED\") ||\n (cell.prediction.detects === null && cell.prediction.ends === \"own-close\")\n ? socket.find((record) => record.ev === \"state\" && oldState(record) === 1 && newState(record) === 4)\n : undefined;\n // a drop by the program of the observer on its own timer: its `drop` line, at the kernel's stamp\n // of its close frame or its close. It counts when it comes before each report of the kernel\n const lines = programLines(cell, run);\n const kernelEnded = reported?.mono ?? closeWait?.mono ?? own?.mono ?? seen?.t;\n const first = dropOf(lines, socket, local, noiseOf(cell));\n const drop = first !== undefined && (kernelEnded === undefined || first.at <= kernelEnded) ? first : undefined;\n const dropped = drop !== undefined;\n const outcome: Outcome = dropped\n ? { kind: \"dropped\" }\n : reported !== undefined\n ? { kind: \"error\", errno: ERRNO[reported.err!] ?? `E${reported.err}` }\n : closeWait !== undefined || seen?.ev === \"eof\"\n ? { kind: \"eof\" }\n : own !== undefined\n ? { kind: \"closed\" }\n : seen !== undefined\n ? { kind: \"error\", errno: String(seen[\"errno\"]) }\n : { kind: \"silent\" };\n const ended = drop?.at ?? kernelEnded;\n const noticed =\n reported !== undefined && seen !== undefined && !dropped ? between(reported.mono, seen.t) : null;\n const { prediction } = cell;\n // the pings of a heartbeat of the program, at the kernel's stamps of their writes\n const pings = pingsOf(lines, socket, local, noiseOf(cell));\n // keepalive counts from the last segment received before the fault. The budget of the give-up\n // counts from the first retransmission of the RTO timer, not from the tail loss probe before it.\n // A reset counts from the send of the client. The kernel has no record of that send, thus the\n // stamp is the stamp of the client. A toxic counts from the start of its commit. A heartbeat\n // counts from the write of its first ping that got no answer before the detection\n const before = run.fault?.t1 ?? ready.t;\n const referenceAt = (from: Reference): number | undefined => {\n if (from === \"request-sent\") return request.t;\n if (from === \"fault\") return run.fault?.t0;\n if (from === \"client-start\") return (events.find((event) => event.ev === \"connecting\") ?? events[0])?.t;\n if (from === \"ping-sent\") return firstUnanswered(pings, socket, ended)?.at;\n const record: KernelRecord | undefined =\n from === \"last-received\"\n ? socket.filter((each) => each.ev === \"rx\" && each.mono <= before).at(-1)\n : from === \"first-retransmission\"\n ? retransmitted.find((each) => each.why === \"rto\")\n : kernel.find(\n (each) =>\n each.netns === peerNetns &&\n each.cookie === peerCookie &&\n each.ev === \"state\" &&\n oldState(each) === 1,\n );\n return record?.mono;\n };\n // in a topology, the bytes after the request and the instant that a router forgot the flow\n const lastRx =\n kernel\n .filter((each) => each.netns === run.netns && each.src === local && each.ev === \"rx\" && each.mono <= before)\n .at(-1)?.mono ?? ready.t;\n const chain =\n run.topology === undefined\n ? {}\n : { bytes, forgot: forgotten === undefined ? null : between(lastRx, forgotten.t) };\n\n // a measurement-only cell: each request of the run and the time to its reply, and the time from\n // its reference to the end of the connection, or to the last data\n if (prediction.detects === null) {\n const sends = events.filter((event) => event.ev === \"sent\").slice(1);\n // a reply ends at its last byte when the client says so (`replied`), and at its first data otherwise\n const answers = events.some((event) => event.ev === \"replied\") ? \"replied\" : \"data\";\n const samples = sends.flatMap((send, n) => {\n const next = sends[n + 1]?.t ?? Infinity;\n const reply = events.find((event) => event.ev === answers && event.t > send.t && event.t < next);\n return reply === undefined ? [] : [between(send.t, reply.t)];\n });\n const from = prediction.from === undefined ? undefined : referenceAt(prediction.from);\n const until = ended ?? after.at(-1)?.t;\n // the data of the client comes from its peer: the receiver and the sender of each reply\n const peerRecords =\n peerCookie === undefined\n ? []\n : kernel.filter((record) => record.netns === peerNetns && record.cookie === peerCookie);\n const proxy = cell.calibrates === undefined ? undefined : run.topology?.hops[cell.calibrates];\n const crossings =\n cell.calibrates === undefined\n ? {}\n : {\n crossings: crossingsOf(\n run,\n cell.calibrates,\n proxy === undefined ? undefined : socketOf({ ...cell, observer: { hop: cell.calibrates, toward: \"client\" } }, run),\n proxy === undefined ? undefined : socketOf({ ...cell, observer: { hop: cell.calibrates, toward: \"target\" } }, run),\n ),\n };\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome: ended !== undefined ? outcome : { kind: \"answered\" },\n ...(from === undefined || until === undefined ? {} : { elapsed: between(from, until) }),\n noticed: prediction.from === undefined ? null : noticed,\n steps: retransmitted\n .filter((record) => record.mono > request.t)\n .map((record) => between(request.t, record.mono)),\n samples,\n reordering: reorderingOf(\n socket.filter((record) => record.mono > request.t),\n peerRecords.filter((record) => record.mono > request.t),\n ),\n ...crossings,\n void: voided,\n };\n }\n\n // a stall: nothing must detect it, and the connection must move again at the first\n // retransmission after it stops, timed from the send of the request, or of the first ping\n if (!prediction.detects && prediction.recovers !== undefined) {\n const { recovers } = prediction;\n // an idle client sends only the pings of its heartbeat: its stall counts from its first ping into it\n const start =\n cell.probe.mode === \"idle\"\n ? (lines.find((line) => line.ev === \"ping\" && line.t >= (run.fault?.t1 ?? 0)) ?? request)\n : request;\n const heal = run.heal ?? null;\n if (voided === null && heal === null) voided = \"the stall never ended\";\n else if (voided === null && heal !== null) {\n const from = between(start.t, heal.t0);\n const to = between(start.t, heal.t1);\n if (\n valueIn(from, \"s\") <= valueIn(recovers.heal.low, \"s\") ||\n valueIn(to, \"s\") >= valueIn(recovers.heal.high, \"s\")\n )\n voided = `the stall ended ${from.value}–${to.value} s after the send, outside the ${recovers.heal.low.value}–${recovers.heal.high.value} s its prediction assumed`;\n }\n const steps = retransmitted\n .filter((record) => record.mono > start.t)\n .map((record) => between(start.t, record.mono));\n if (ended !== undefined)\n return {\n ...base,\n ...nothing,\n outcome,\n steps,\n void: voided,\n finding: `detected after ${between(start.t, ended).value} s, where it should recover`,\n };\n // the first segment back after the stall stopped: the answer of the server to the\n // retransmission that got through, and the read of the reply by the client\n const answer =\n heal === null\n ? undefined\n : socket.find((record) => record.ev === \"rx\" && record.mono > heal.t0);\n if (answer === undefined)\n return {\n ...base,\n ...nothing,\n steps,\n void: voided,\n finding: \"it never moved again: nothing came back after the stall ended\",\n };\n const reply = events.find((event) => event.ev === \"data\" && event.t > start.t);\n voided ??= delayed(start.t, answer.mono, \"retransmit\");\n return {\n ...base,\n outcome: { kind: \"answered\" },\n elapsed: between(start.t, answer.mono),\n socket: reply === undefined ? null : between(start.t, reply.t),\n noticed: reply === undefined ? null : between(answer.mono, reply.t),\n steps,\n void: voided,\n finding: null,\n };\n }\n\n // a reply in a band: nothing must detect the fault, and the reply must come back in its time,\n // from the send of the request to the first segment that the socket receives after it\n if (!prediction.detects && prediction.replies !== undefined) {\n const window = valueIn(prediction.window, \"s\") * 1e9;\n if (ended !== undefined && ended - request.t <= window)\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n elapsed: between(request.t, ended),\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where the reply should come`,\n };\n // the first segment with data: a proxy can acknowledge the request first, with no data\n const answer = socket.find(\n (record) => record.ev === \"rx\" && record.mono > request.t && (record.seg?.len.value ?? 0) > 0,\n );\n const reply = after[0];\n if (answer === undefined)\n return { ...base, ...nothing, ...chain, void: voided, finding: \"the reply never came\" };\n return {\n ...base,\n ...chain,\n outcome: { kind: \"answered\" },\n elapsed: between(request.t, answer.mono),\n socket: reply === undefined ? null : between(request.t, reply.t),\n noticed: reply === undefined ? null : between(answer.mono, reply.t),\n steps: [],\n void: voided,\n finding: null,\n };\n }\n\n if (!prediction.detects) {\n // the test of silence covers the window from the send of the request\n const window = valueIn(prediction.window, \"s\") * 1e9;\n if (ended === undefined || ended - request.t > window) {\n // the router keeps the flow in the window, unless the prediction says that it forgets it,\n // and when. Either way, nothing detects it\n const forgetIn = forgotten !== undefined && forgotten.t - request.t <= window ? forgotten : undefined;\n const expected = prediction.middlebox?.forgets;\n const after = forgetIn === undefined ? null : between(lastRx, forgetIn.t);\n let finding: string | null = null;\n if (expected === undefined) {\n if (after !== null) finding = `a router forgot the flow ${after.value} s after the last segment, where it should keep it`;\n } else if (after === null) {\n finding = \"no router forgot the flow in the window, where it should forget it\";\n } else {\n const noise = valueIn(noiseOf(cell), \"s\");\n const low = valueIn(expected.low, \"s\") - noise;\n const high = valueIn(expected.high, \"s\") + noise;\n if (after.value < low || after.value > high)\n finding = `a router forgot the flow ${after.value} s after the last segment, outside [${Math.round(low * 1e6) / 1e6}, ${Math.round(high * 1e6) / 1e6}] s`;\n }\n return { ...base, ...nothing, ...chain, void: voided, finding };\n }\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n elapsed: between(request.t, ended),\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where nothing should`,\n };\n }\n\n if (ended === undefined)\n return {\n ...base,\n ...nothing,\n ...chain,\n void: voided,\n finding: `nothing detected it: ${prediction.timer} never fired`,\n };\n const reference = referenceAt(prediction.from);\n if (reference === undefined) {\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n noticed,\n void: voided,\n finding: `it ended without its reference event (${prediction.from})`,\n };\n }\n // the steps of a heartbeat are its later pings, each from the first ping that got no answer\n const steps =\n prediction.from === \"ping-sent\"\n ? pings.filter((ping) => ping.at > reference && ping.at <= ended).map((ping) => between(reference, ping.at))\n : (prediction.from === \"last-received\"\n ? keepalives\n : prediction.from === \"first-retransmission\"\n ? retransmitted.filter((record) => record.why === \"rto\" && record.mono > reference)\n : []\n ).map((record) => between(reference, record.mono));\n const matches =\n prediction.errno === \"EOF\"\n ? outcome.kind === \"eof\"\n : prediction.errno === \"CLOSED\"\n ? outcome.kind === \"closed\"\n : prediction.errno === \"DROPPED\"\n ? outcome.kind === \"dropped\"\n : outcome.kind === \"error\" && outcome.errno === prediction.errno;\n const words =\n outcome.kind === \"error\"\n ? outcome.errno\n : outcome.kind === \"closed\"\n ? \"its own close\"\n : outcome.kind === \"dropped\"\n ? \"a drop by its own program\"\n : \"end of file\";\n // the reconnect of the client after the detection, against the bands of its waits\n const reconnect = prediction.reconnects === undefined ? undefined : reconnectOf(run, ended, cookie, drop?.line.t ?? ended);\n const late = reconnect === undefined || prediction.reconnects === undefined ? null : reconnectFinding(prediction.reconnects, reconnect, noiseOf(cell));\n // a drop of the program against the way that the prediction gives: a close frame or a close\n const left = prediction.drop === undefined || drop === undefined ? undefined : leftAfter(socket, drop.line);\n const how = prediction.drop === undefined || drop === undefined || left === undefined ? null : dropFinding(prediction.drop, drop, left, noiseOf(cell));\n const found = [matches ? null : `it ended with ${words}, not ${prediction.errno}`, late, how].filter((each) => each !== null);\n const finding = found.length === 0 ? null : found.join(\". \");\n // the timer of an HTTP client or server is a timer of the program, not of the kernel: the kernel\n // timers of its socket do not drive the measurement\n const programTimer = cell.probe.http !== undefined && prediction.from === \"last-received\" && prediction.timer !== \"keepalive\";\n // a firing at the reference itself only moves the start of the interval. A reset meets the send\n // with no timer, a FIN arrives with none, and a toxic has its own clock\n if ((prediction.from === \"last-received\" && !programTimer) || prediction.from === \"first-retransmission\")\n voided ??= delayed(\n reference,\n ended,\n prediction.from === \"last-received\" ? \"keepalive\" : \"retransmit\",\n );\n // a heartbeat is a timer of the program. The retransmit timer of its ping fires in the same\n // interval, thus a delay of that timer behind its wheel slot tells that the VM stopped a vCPU\n if (prediction.from === \"ping-sent\") voided ??= delayed(reference, ended, \"retransmit\");\n // a timer of conntrack (expire_…) reaches its DESTROY through the listing of the router. A slow\n // listing between the expiry and the detection breaks the bound of its lag: the run measured the\n // pump, not the router. The expiry is at most the width of the band before the detection\n const lag = (valueIn(prediction.high, \"s\") - valueIn(prediction.low, \"s\")) * 1e9;\n const slow = prediction.timer.startsWith(\"expire_\")\n ? (run.middlebox ?? []).find(\n (line) =>\n line.ev === \"pump-slow\" &&\n line.t <= ended &&\n line.t + Number(line[\"took\"]) * 1e9 >= ended - lag,\n )\n : undefined;\n if (slow !== undefined)\n voided ??= `a listing of the router took ${Math.round(Number(slow[\"took\"]) * 1e4) / 10} ms, longer than the bound of its prediction`;\n // a toxic starts somewhere in the commit of its request. A commit longer than the noise of the\n // pipeline leaves its start unknown within δ_abs\n const commit = run.fault === null ? 0 : (run.fault.t1 - run.fault.t0) / 1e6;\n if (prediction.from === \"fault\" && commit > valueIn(INJECT, \"ms\"))\n voided ??= `the commit of the fault took ${Math.round(commit * 10) / 10} ms, longer than the noise of the pipeline: its start is not known`;\n // the view of the program of a heartbeat: from the line of its first ping without an answer to\n // the line of its drop, or to the line of the error that it saw\n const unanswered = prediction.from === \"ping-sent\" ? firstUnanswered(pings, socket, ended) : undefined;\n const endLine = drop?.line ?? seen;\n const view =\n unanswered !== undefined\n ? endLine === undefined\n ? null\n : between(unanswered.line.t, endLine.t)\n : remote\n ? null\n : socketVantage(cell, events, ready, request);\n return {\n ...base,\n ...chain,\n outcome,\n elapsed: between(reference, ended),\n socket: view,\n noticed,\n steps,\n ...(reconnect === undefined ? {} : { reconnect }),\n ...(left === undefined ? {} : { left }),\n ...(cell.probe.ws !== undefined && inflight ? { call: callOf(events, request, ended) } : {}),\n void: voided,\n finding,\n };\n};\n","import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n realpathSync,\n rmSync,\n statSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { StartedNetwork, StartedTestContainer } from \"testcontainers\";\nimport type * as Testcontainers from \"testcontainers\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport { isHopPlace } from \"./cell.ts\";\nimport type {\n Client,\n ClientImage,\n FaultPlace,\n Hop,\n HopPlace,\n KernelRecord,\n MiddleboxEvent,\n ProbeEvent,\n ProxyHop,\n RunTopology,\n Target,\n Topology,\n Toxic,\n} from \"./cell.ts\";\n\n/**\n * These are the roots of the two packages that the images read: this package, and the prober\n * that Node resolves for it. In a workspace, they are two folders side by side. From npm, the\n * prober is a link, and a Docker build context does not follow a link. Thus each image builds\n * from a context that the harness copies from the two roots.\n */\nconst ROOTS: Readonly<Record<string, string>> = {\n harness: fileURLToPath(new URL(\"..\", import.meta.url)),\n prober: dirname(\n realpathSync(createRequire(import.meta.url).resolve(\"@mark1russell7/prober/package.json\")),\n ),\n};\n\n/** This function gives the path of a part of an image, which is `<package>/<path>` in the context. */\nconst pathOf = (part: string): string => {\n const [root, ...rest] = part.split(\"/\");\n const base = ROOTS[root!];\n if (base === undefined) throw new Error(`${part} is in no package that the images read`);\n return join(base, ...rest);\n};\n\n/** This function loads Testcontainers when a lab first opens. Code that only measures and judges does not load it. */\nexport const containers = (): Promise<typeof Testcontainers> => import(\"testcontainers\");\n\n/** An image is an image that the harness builds: its Dockerfile, and each file or folder that the Dockerfile reads. */\ninterface Image {\n readonly name: string;\n readonly dockerfile: string;\n readonly from: readonly string[];\n}\n\n/** This image holds the prober, the TCP client, the injector and the namespace admin. */\nconst HARNESS: Image = {\n name: \"loom-harness\",\n dockerfile: \"harness/image/Dockerfile\",\n from: [\n \"harness/image/Dockerfile\",\n \"harness/image/native\",\n \"prober/bpf\",\n \"prober/native\",\n \"prober/fixtures\",\n ],\n};\n/** This image holds the Node client: ioredis 6.0.0 on Node 24.21.0. */\nconst NODE: Image = {\n name: \"loom-harness-node\",\n dockerfile: \"harness/image/node/Dockerfile\",\n from: [\"harness/image/node\"],\n};\n/** This image holds the Go HTTP client: the http.Transport of Go 1.27.2. */\nconst GO_HTTP: Image = {\n name: \"loom-harness-go-http\",\n dockerfile: \"harness/image/go-http/Dockerfile\",\n from: [\"harness/image/go-http\"],\n};\n/** This image holds the Node HTTP clients: the http.Agent of Node 24.21.0, and undici 8.12.2. */\nconst NODE_HTTP: Image = {\n name: \"loom-harness-node-http\",\n dockerfile: \"harness/image/node-http/Dockerfile\",\n from: [\"harness/image/node-http\"],\n};\n/** This image holds the MongoDB client: the driver mongodb 7.7.0 on Node 24.21.0, and its step-down script. */\nconst MONGO: Image = {\n name: \"loom-harness-mongo\",\n dockerfile: \"harness/image/mongo/Dockerfile\",\n from: [\"harness/image/mongo\"],\n};\n/** This image holds `@procedures/client` 0.3.0 on Node 24.21.0: its WebSocket client as a client of the harness, and the script of its server. */\nconst PROCEDURES: Image = {\n name: \"loom-harness-procedures\",\n dockerfile: \"harness/image/procedures/Dockerfile\",\n from: [\"harness/image/procedures\"],\n};\n/** This image is the router of a middlebox chain: NAT and conntrack, with a form for a forgotten flow. */\nconst ROUTER: Image = {\n name: \"loom-harness-router\",\n dockerfile: \"harness/image/router/Dockerfile\",\n from: [\"harness/image/router\"],\n};\n\n/** This function gives each file under a path, in a stable sequence, but not the output of a local build. */\nconst sources = (path: string): string[] => {\n if (statSync(path).isFile()) return [path];\n const entries = readdirSync(path, { withFileTypes: true });\n return entries\n .filter((entry) => entry.name !== \"target\" && entry.name !== \"node_modules\")\n .flatMap((entry) => sources(join(path, entry.name)))\n .sort();\n};\n\n/** This function gives each file of an image, with its path in the build context. */\nconst filesOf = (image: Image): Array<{ readonly file: string; readonly at: string }> =>\n image.from.flatMap((part) => {\n const base = pathOf(part);\n return sources(base).map((file) => ({\n file,\n at: join(part, relative(base, file)).replaceAll(\"\\\\\", \"/\"),\n }));\n });\n\n/** This function gives the tag of an image from its content. Thus a changed probe or client cannot operate as an old one. */\nconst imageTag = (image: Image): string => {\n const hash = createHash(\"sha256\");\n for (const { file, at } of filesOf(image)) {\n hash.update(at);\n hash.update(readFileSync(file));\n }\n return `${image.name}:${hash.digest(\"hex\").slice(0, 12)}`;\n};\n\n/** This function copies the files of an image into a new build context, and gives its path. */\nconst stage = (image: Image): string => {\n const context = mkdtempSync(join(tmpdir(), `${image.name}-`));\n for (const { file, at } of filesOf(image)) {\n mkdirSync(dirname(join(context, at)), { recursive: true });\n copyFileSync(file, join(context, at));\n }\n return context;\n};\n\nconst built = new Map<string, Promise<string>>();\n/** This function builds an image one time for each process. Docker keeps its layers between runs. */\nconst build = (image: Image): Promise<string> => {\n let tag = built.get(image.name);\n if (tag === undefined) {\n tag = (async () => {\n const name = imageTag(image);\n const { GenericContainer } = await containers();\n const context = stage(image);\n try {\n await GenericContainer.fromDockerfile(context, image.dockerfile).build(name, {\n deleteOnExit: false,\n });\n } finally {\n rmSync(context, { recursive: true, force: true });\n }\n return name;\n })();\n built.set(image.name, tag);\n }\n return tag;\n};\n\n/** This function gives the image of the harness. */\nexport const harnessImage = (): Promise<string> => build(HARNESS);\n/** This function gives the image of the Node client. */\nexport const nodeClientImage = (): Promise<string> => build(NODE);\n/** This function gives the image of the router. */\nexport const routerImage = (): Promise<string> => build(ROUTER);\n/** This function gives the image of the Go HTTP client. */\nexport const goHttpImage = (): Promise<string> => build(GO_HTTP);\n/** This function gives the image of the Node HTTP clients. */\nexport const nodeHttpImage = (): Promise<string> => build(NODE_HTTP);\n/** This function gives the image of the MongoDB client. */\nexport const mongoClientImage = (): Promise<string> => build(MONGO);\n/** This function gives the image of `@procedures/client`: its WebSocket client and its server. */\nexport const proceduresImage = (): Promise<string> => build(PROCEDURES);\n\n/** This function gives the image that a target operates: its stock image, or an image of the harness (`Target.harness`). */\nexport const targetImageOf = (target: Target): Promise<string> =>\n target.harness === \"procedures\" ? proceduresImage() : Promise.resolve(target.image);\n\n/**\n * This time is the interval of the listing of a router, when its hop does not set one. A listing\n * evicts each expired flow that it walks past, and the DESTROY event of that flow follows.\n */\nexport const PUMP: Quantity = { value: 0.1, unit: \"s\" };\n/**\n * This time is the longest listing of the conntrack table of a router that a prediction assumes.\n * A DESTROY follows an expiry within the interval of the pump and two listings. On 2026-10-15,\n * the slowest of 500 listings in an idle router took 19.4 ms, and the median took 5.5 ms. A\n * longer listing writes a `pump-slow` line, and a run with one near its detection is void.\n */\nexport const LISTING: Quantity = { value: 20, unit: \"ms\" };\n\n/**\n * A network fault is a fault that the injector applies as a filter rule. It is a black hole (both\n * directions, or one), or a reset on the next segment. It can also be an ICMP unreachable for each\n * segment, or a drop of each segment that is longer than a length.\n */\nexport type NetworkFault = \"blackhole\" | \"rst\" | \"reject\" | \"mtu\";\n\n/** These are the options of a network fault: the direction of a black hole, the code of a reject, the length of an MTU drop. */\nexport interface FaultOptions {\n /** This direction is relative to the namespace of the rule: `in` drops what comes from the peer, `out` what goes to it. */\n readonly direction?: \"in\" | \"out\";\n readonly code?: \"host\" | \"net\" | \"port\" | \"admin\";\n /** This length is the longest segment in bytes that an MTU drop lets through. */\n readonly above?: number;\n}\n\n/** This function gives the arguments of the injector for the options of a fault. */\nexport const optionArgs = (options: FaultOptions): string[] => [\n ...(options.direction === undefined ? [] : [\"--direction\", options.direction]),\n ...(options.code === undefined ? [] : [\"--code\", options.code]),\n ...(options.above === undefined ? [] : [\"--above\", String(options.above)]),\n];\n\n/** These are the actions of the injector that lift each network fault. */\nexport const LIFT: Readonly<Record<NetworkFault, string>> = {\n blackhole: \"clear\",\n rst: \"clear-rst\",\n reject: \"clear-reject\",\n mtu: \"clear-mtu\",\n};\n\n/**\n * A fault side is the namespace that gets the rules of a fault: the namespace of the peer of the\n * observer. For the client, it is the namespace of the target (the injector). For the server, it\n * is the namespace of the client (its privileged admin). Rules in the namespace of the observer\n * make its sends fail locally, with EPERM, and the network does not lose them. Thus its\n * retransmissions do not go out. The retransmission tracepoint of the kernel then records none,\n * because it fires only for a retransmission that went out.\n */\nexport type FaultSide = \"target\" | \"client\";\n\n/** A hop of a manifest is one hop of a topology, as the lab started it. */\nexport interface ManifestHop {\n readonly name: string;\n readonly kind: \"router\" | \"proxy\";\n readonly image: string;\n readonly id: string;\n /** These are the settings of the hop: the form and the idle timeout of a router, the files and settings of a proxy. */\n readonly settings: Readonly<Record<string, string>>;\n /** These are the sysctls of the namespace of the hop, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n}\n\n/** A manifest is what a lab records about its machine, its images and its settings. */\nexport interface Manifest {\n readonly kernel: string;\n /** This text is the architecture of the machine, as uname -m gives it: x86_64. */\n readonly architecture: string;\n readonly hz: string | null;\n /**\n * This text tells where the HZ came from: the configuration of the kernel, or a measurement. A\n * host without /proc/config.gz (a hosted runner) gets a measurement of jiffies against the clock.\n */\n readonly hzFrom?: \"config\" | \"measured\";\n readonly boot_id: string;\n readonly docker: string;\n /** This record is the target, and its settings as it read them back. */\n readonly target: {\n readonly image: string;\n /** This name is the image of the harness that the target operates, with its stock base (`Target.harness`). */\n readonly harness?: string;\n readonly base?: string;\n readonly id: string;\n readonly settings: Readonly<Record<string, string>>;\n /** These are the sysctls of its namespace, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n /** These are the files that went into the target, by their path, each with the sha256 of its content. */\n readonly files?: Readonly<Record<string, string>>;\n /** This line is the first line of a target that writes them: a server that tells its settings as it reads them. */\n readonly first?: ProbeEvent;\n };\n readonly harness: { readonly image: string; readonly id: string; readonly packages: string[] };\n /** This record is the client of each run: the TCP client of the harness, a driver, or an image that the cell gives. */\n readonly client: { readonly kind: Client | \"image\"; readonly image: string };\n /** These are the namespaces that the prober watched, by inode. */\n readonly netns: {\n readonly client: number;\n readonly target: number;\n readonly hops?: Readonly<Record<string, number>>;\n };\n /** These are the sysctls of the client namespace, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n /** These are the hops of a topology, in sequence from the client. */\n readonly hops?: readonly ManifestHop[];\n /** These are the parts of a group, with their settings as each part read them back. */\n readonly group?: {\n readonly members: ReadonlyArray<{ readonly name: string; readonly ip: string; readonly id: string; readonly settings: Readonly<Record<string, string>> }>;\n readonly sentinels: ReadonlyArray<{ readonly name: string; readonly ip: string; readonly id: string; readonly settings: Readonly<Record<string, string>> }>;\n readonly observer: { readonly image: string };\n };\n}\n\n/** These are the JSON lines of a program, as they arrive. */\nexport interface Lines<T extends { readonly ev: string }> {\n readonly seen: T[];\n /** These are the other lines that it wrote, for example an error. They explain a program that stopped its output. */\n readonly other: string[];\n consume: (stream: NodeJS.ReadableStream) => void;\n /** This method gives the first line of one of these kinds, or it rejects after `within` ms. */\n until(kinds: readonly string[], within: number, who: string): Promise<T>;\n}\n\n/** This function collects the JSON lines of a program. It gives each line to `also` too, when the caller gives it. */\nexport const lines = <T extends { readonly ev: string }>(also?: (line: T) => void): Lines<T> => {\n const seen: T[] = [];\n const other: string[] = [];\n const waiting: Array<{ kinds: readonly string[]; resolve: (line: T) => void }> = [];\n let partial = \"\";\n const take = (text: string) => {\n if (!text.startsWith(\"{\")) {\n if (text.length > 0) other.push(text);\n return;\n }\n const line = JSON.parse(text) as T;\n seen.push(line);\n also?.(line);\n for (const wait of waiting.splice(0)) {\n if (wait.kinds.includes(line.ev)) wait.resolve(line);\n else waiting.push(wait);\n }\n };\n return {\n seen,\n other,\n consume: (stream) =>\n stream.on(\"data\", (chunk: Buffer | string) => {\n partial += chunk.toString();\n const split = partial.split(\"\\n\");\n partial = split.pop() ?? \"\";\n for (const text of split) take(text.trim());\n }),\n until(kinds, within, who) {\n const found = seen.find((line) => kinds.includes(line.ev));\n if (found !== undefined) return Promise.resolve(found);\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const said = other.length === 0 ? \"\" : `; it wrote: ${other.slice(-3).join(\" | \")}`;\n reject(new Error(`no ${kinds.join(\" or \")} from the ${who} in ${within} ms${said}`));\n }, within);\n waiting.push({\n kinds,\n resolve: (line) => {\n clearTimeout(timer);\n resolve(line);\n },\n });\n });\n },\n };\n};\n\n/** A probe is the client container of one run. */\nexport interface Probe {\n /** This method gives the first event of one of these kinds, or it rejects after `within` ms. */\n until(kinds: readonly string[], within: number): Promise<ProbeEvent>;\n /** This method lets an in-flight probe send its request. */\n go(): Promise<void>;\n /** This method freezes the client with the cgroup freezer: its process stops, and its kernel continues to answer. */\n freeze(): Promise<void>;\n thaw(): Promise<void>;\n /** This method stops the process of the client with SIGKILL. Its kernel then closes its socket with a FIN, or a reset. */\n kill(): Promise<void>;\n readonly events: readonly ProbeEvent[];\n stop(): Promise<void>;\n}\n\n/** An agent is a container that a lab starts in the namespaces of the target, for example a Node process that signals the server. */\nexport interface Agent {\n /** These are the JSON lines that it wrote, as they arrive. */\n readonly lines: Lines<{ readonly ev: string }>;\n /** This method starts a command in the agent, and gives its standard output. */\n exec(argv: readonly string[]): Promise<string>;\n stop(): Promise<void>;\n}\n\n/** A lab is one environment: the target, the client, the hops of a topology, the injectors and the prober. */\nexport interface Lab {\n /** This address is the address that the client connects to: the target, or the first proxy of a topology. */\n readonly targetIp: string;\n /** This port is the port that the client connects to. */\n readonly targetPort: number;\n readonly clientIp: string;\n readonly netns: {\n readonly client: number;\n readonly target: number;\n readonly hops?: Readonly<Record<string, number>>;\n };\n /** This record is where each socket of a topology is, or null for a direct pair. */\n readonly topology: RunTopology | null;\n /** These are the lines of the routers of a topology, in sequence, each with the name of its hop. A direct pair has none. */\n readonly middlebox: readonly MiddleboxEvent[];\n /** These are the lines of a target that writes them (`Target.lines`), in sequence. Another target has none. */\n readonly server: readonly ProbeEvent[];\n /** This method adds a toxic to the Toxiproxy hop of a topology, or removes it with null. The stamps of the injector bracket it. */\n toxic(toxic: Toxic | null): Promise<{ t0: number; t1: number }>;\n /** This method makes the target close the connection of each client, with the command of the target. */\n serverClose(): Promise<void>;\n /**\n * This method starts an agent in the network namespace of the target, and in its process\n * namespace with `pid`. Its command keeps it up, for example `sleep infinity`.\n */\n agent(image: string, command: readonly string[], options?: { readonly pid?: boolean }): Promise<Agent>;\n /** These are all the records that the prober wrote, in sequence. */\n readonly kernel: readonly KernelRecord[];\n /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */\n sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method sets the settings of the target in it before a client connects, and reads them back. */\n configure(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method sets the sysctls of the target namespace through a privileged sidecar there, and reads them back. */\n targetSysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /**\n * This method applies a fault between the client and the target, by default from the namespace\n * of the target. The fault is a black hole, or a reset that answers the next segment of the\n * client. At a proxy of a topology, the rules face the peer of the proxy on one side. The stamps\n * of the injector bracket it.\n */\n fault(\n kind: NetworkFault,\n at?: FaultPlace,\n options?: FaultOptions,\n ): Promise<{ t0: number; t1: number }>;\n /** This method removes a fault. */\n lift(kind: NetworkFault, at?: FaultPlace, options?: FaultOptions): Promise<void>;\n /**\n * This method puts tc netem on the egress of a namespace, for example \"delay 20ms\" or \"loss 5%\",\n * or removes it with null. At a hop, it is the egress of the device of the hop toward one side.\n * The stamps of the injector bracket it.\n */\n netem(spec: string | null, at?: FaultPlace): Promise<{ t0: number; t1: number }>;\n /**\n * This method sets the TSO, GSO and GRO of a namespace. With them off, netem and a length match\n * act on segments, not on GSO super-packets of up to 64 KB. At a hop, it sets the two devices of the hop.\n */\n offload(on: boolean, at?: FaultPlace): Promise<void>;\n /** This method stops the server process in the target with SIGKILL, with the command of the target. */\n kill(): Promise<void>;\n /**\n * This method resolves the neighbors of the path in the two directions, with a ping from each\n * namespace. Without it, the first connection of an environment waits for ARP. Under a netem\n * delay, that wait is one more delayed round, and its round trip and RTO are longer.\n */\n warm(): Promise<void>;\n /** This method runs commands in the target before a client connects, each as argv. */\n setup(commands: ReadonlyArray<readonly string[]>): Promise<void>;\n /**\n * This method stops a black hole at an instant on the monotonic clock of the VM. The injector\n * waits for that instant in the VM, thus the length of a stall does not include the latency of\n * a docker exec. Stamps bracket it, as for a fault.\n */\n heal(at: FaultPlace, when: number): Promise<{ t0: number; t1: number }>;\n /**\n * This method removes what the two kernels learned from their earlier connections. These are\n * the TCP metrics that each kernel keeps for a peer address when a connection closes. These metrics set the\n * first round trip of the next connection, and thus its RTO. A run with these metrics is not\n * independent of the run before it.\n */\n forget(): Promise<void>;\n /** This method freezes the target with the cgroup freezer: its process stops, and its kernel continues to answer. */\n freeze(): Promise<void>;\n thaw(): Promise<void>;\n /**\n * This method sends a signal to the server process of the target (`Target.program`), at once or\n * at an instant of the VM. A sidecar in the network and process namespaces of the target sends\n * it, and the injector stamps it. The lab starts that sidecar at the first signal.\n */\n signal(signal: \"STOP\" | \"CONT\" | \"KILL\", at?: number): Promise<{ t0: number; t1: number }>;\n probe(args: readonly string[]): Promise<Probe>;\n /**\n * This method waits for the next clock line of the prober, which gives the count of lost\n * records. It gives null when no clock line comes in 5 s, because then nothing tells the count.\n */\n settle(): Promise<number | null>;\n manifest(\n sysctls: Readonly<Record<string, string>>,\n settings?: Readonly<Record<string, string>>,\n targetSysctls?: Readonly<Record<string, string>>,\n ): Promise<Manifest>;\n close(): Promise<void>;\n}\n\n/** This function makes a container share the process namespace of another container, for example `container:<id>`. */\nexport const withPid = <T extends Testcontainers.GenericContainer>(container: T, mode: string): T => {\n (container as unknown as { hostConfig: { PidMode?: string } }).hostConfig.PidMode = mode;\n return container;\n};\n\n/** This container class can share the process namespace of another container. */\nconst shared = (Base: typeof Testcontainers.GenericContainer) =>\n class extends Base {\n withPidMode(mode: string): this {\n (this as unknown as { hostConfig: { PidMode?: string } }).hostConfig.PidMode = mode;\n return this;\n }\n };\n\n/** This function gives the content of a file of a proxy, with the address of the next hop for each {{next}}. */\nexport const templated = (content: string, next: string): string => content.replaceAll(\"{{next}}\", next);\n\n/** This function gives the JSON body of a toxic for the API of Toxiproxy. Each toxic of a cell has the name `fault`. */\nexport const toxicBody = (toxic: Toxic): string =>\n JSON.stringify({\n name: \"fault\",\n type: toxic.type,\n stream: toxic.stream,\n toxicity: 1,\n attributes: toxic.attributes,\n });\n\n/**\n * This function gives the routes of a chain: for each router, the networks after it, which the\n * element before it reaches through it. Element 0 is the client, and element k is hop k - 1. Network\n * j is the network between element j and element j + 1.\n */\nexport const routesOf = (\n hops: readonly Pick<Hop, \"kind\">[],\n): Array<{ readonly from: number; readonly via: number; readonly networks: readonly number[] }> =>\n hops.flatMap((hop, at) =>\n hop.kind === \"router\"\n ? [\n {\n from: at,\n via: at + 1,\n networks: Array.from({ length: hops.length - at }, (_, k) => at + 1 + k),\n },\n ]\n : [],\n );\n\n/**\n * This function gives the endpoint that an element connects to: the first element after it that\n * is not a router. Element 0 is the client, element k is hop k - 1, and the last element is the target.\n */\nexport const endpointAfter = (hops: readonly Pick<Hop, \"kind\">[], element: number): number => {\n let at = element + 1;\n while (at <= hops.length && hops[at - 1]!.kind === \"router\") at += 1;\n return at;\n};\n\n// each container here is a holder, a probe or the prober: nothing drains, thus the stop is immediate\nexport const STOP = { timeout: 0 };\n\n/** These are the values of CONFIG_HZ that Linux offers. */\nconst RATES: readonly number[] = [100, 250, 300, 1000];\n\n/**\n * This function gives HZ from two readings of /proc/timer_list, one second apart: its `now at`\n * time in ns and its jiffies. It gives the nearest rate that Linux offers, or null when the\n * measurement is more than 5 % from each of them.\n */\nexport const hzOf = (first: { now: number; jiffies: number }, second: { now: number; jiffies: number }): string | null => {\n const seconds = (second.now - first.now) / 1e9;\n if (!(seconds > 0)) return null;\n const rate = (second.jiffies - first.jiffies) / seconds;\n const nearest = RATES.reduce((best, each) => (Math.abs(each - rate) < Math.abs(best - rate) ? each : best));\n return Math.abs(nearest - rate) / nearest <= 0.05 ? String(nearest) : null;\n};\n\n/** This function measures HZ in a privileged container: jiffies against the time of the kernel, one second apart. */\nconst measuredHz = async (container: StartedTestContainer): Promise<string | null> => {\n const read = \"awk '/^now at/ {now=$3} /^jiffies:/ {print now, $2; exit}' /proc/timer_list\";\n const text = await output(container, [\"sh\", \"-c\", `${read}; sleep 1; ${read}`]).catch(() => \"\");\n const values = text.split(/\\s+/).filter((each) => each !== \"\").map(Number);\n if (values.length !== 4 || values.some((each) => !Number.isFinite(each))) return null;\n return hzOf({ now: values[0]!, jiffies: values[1]! }, { now: values[2]!, jiffies: values[3]! });\n};\n\nexport const output = async (container: StartedTestContainer, argv: string[]): Promise<string> => {\n const result = await container.exec(argv);\n if (result.exitCode !== 0)\n throw new Error(`${argv.join(\" \")} exited ${result.exitCode}: ${result.output.trim()}`);\n return result.stdout.trim();\n};\n\n/** This function gives the inode of the network namespace of a container: net:[4026532583]. */\nexport const netnsOf = async (container: StartedTestContainer): Promise<number> => {\n const link = await output(container, [\"readlink\", \"/proc/self/ns/net\"]);\n const inode = /^net:\\[(\\d+)\\]$/.exec(link)?.[1];\n if (inode === undefined) throw new Error(`not a network namespace: ${link}`);\n return Number(inode);\n};\n\n/** This function gives the image and the command of the client of each run, and its kind for the manifest. */\nexport const clientOf = async (\n kind: Client | ClientImage,\n image: string,\n): Promise<{ clientImage: string; command: readonly string[]; clientKind: Client | \"image\" }> => {\n if (typeof kind === \"object\") return { clientImage: kind.image, command: kind.command, clientKind: \"image\" };\n if (kind === \"ioredis\")\n return {\n clientImage: await nodeClientImage(),\n command: [\"node\", \"/opt/harness/node/client-ioredis.mjs\"],\n clientKind: kind,\n };\n if (kind === \"go-http\") return { clientImage: await goHttpImage(), command: [\"/opt/harness/client-go\"], clientKind: kind };\n if (kind === \"http-agent\" || kind === \"undici\")\n return {\n clientImage: await nodeHttpImage(),\n command: [\"node\", `/opt/harness/node-http/client-${kind === \"undici\" ? \"undici\" : \"agent\"}.mjs`],\n clientKind: kind,\n };\n if (kind === \"mongodb\")\n return {\n clientImage: await mongoClientImage(),\n command: [\"node\", \"/opt/harness/mongo/client-mongo.mjs\"],\n clientKind: kind,\n };\n if (kind === \"procedures-ws\")\n return {\n clientImage: await proceduresImage(),\n command: [\"node\", \"/opt/harness/procedures/client-ws.mjs\"],\n clientKind: kind,\n };\n return { clientImage: image, command: [\"/opt/harness/client\"], clientKind: kind };\n};\n\n/** These are the containers of one hop of a lab, and the addresses that its rules and the measurement read. */\ninterface HopIn {\n readonly hop: Hop;\n /** This container is the hop itself: the router, or the stock proxy. */\n readonly box: StartedTestContainer;\n /** This sidecar shares the namespace of the hop: the injector of its faults and its toxics. */\n readonly injector: StartedTestContainer;\n /** This record is the address of the hop on the network of each of its sides. */\n readonly ip: { readonly client: string; readonly target: string };\n /** These are the peers of the hop: an address on the side of the client, and an address with its port toward the target. */\n readonly peer: { readonly client: string; readonly target: string };\n /** These are the devices of the hop toward each side, as its routes give them. The rules of a router and netem at a hop face one. */\n readonly devices: { readonly client: string; readonly target: string };\n readonly netns: number;\n sysctls: Record<string, string>;\n}\n\n/** This function gives the device of a route from its line in `ip -o route get`: \"10.0.0.2 dev eth0 src …\". */\nexport const deviceOf = (route: string): string => {\n const device = /\\bdev (\\S+)/.exec(route)?.[1];\n if (device === undefined) throw new Error(`no device in the route: ${route}`);\n return device;\n};\n\n/**\n * This function opens one environment:\n * - a chain of new networks, and the stock target on the last one. A direct pair has one network.\n * - a client that holds its own network namespace, with a privileged sidecar in it for its\n * sysctls and its routes. Docker Desktop keeps /proc/sys read-only in a container that operates\n * without privileges.\n * - each hop of the topology, on the network of each of its sides. A hop is a router, or a stock\n * proxy with an injector sidecar in its namespace.\n * - an injector sidecar in the namespace of the target.\n * - the prober, a privileged container that loads the kernel program and watches the namespace of\n * the client, of the target and of each hop.\n *\n * The client of each run is its own container in the namespace of the client. It is the TCP\n * client of the harness, a driver from its own image (ioredis on Node), or an image that the\n * caller gives. Each prints the same lines.\n *\n * The client reaches the networks after a router through that router, by a route that its\n * privileged sidecar adds. A router translates the address of the side of the client, thus the\n * target and each later hop see the router as the peer.\n */\nexport const openLab = async (\n target: Target,\n kind: Client | ClientImage = \"tcp\",\n topology?: Topology,\n): Promise<Lab> => {\n const image = await harnessImage();\n const { clientImage, command, clientKind } = await clientOf(kind, image);\n const hops = topology?.hops ?? [];\n const { GenericContainer, Network, Wait, getContainerRuntimeClient } = await containers();\n const runtime = await getContainerRuntimeClient();\n const opened: StartedTestContainer[] = [];\n const networks: StartedNetwork[] = [];\n // the lines of a target that writes them, on the clock of the VM\n const serverLines = lines<ProbeEvent>();\n const close = async () => {\n for (const container of opened.reverse()) await container.stop(STOP).catch(() => undefined);\n for (const network of networks.reverse()) await network.stop().catch(() => undefined);\n };\n /** This function gives the address of a container on a network, as Docker has it at this time. A container can join a network after its start. */\n const ipOn = async (container: StartedTestContainer, network: StartedNetwork): Promise<string> => {\n const info = await runtime.container.inspect(runtime.container.getById(container.getId()));\n const ip = info.NetworkSettings.Networks[network.getName()]?.IPAddress;\n if (ip === undefined || ip === \"\") throw new Error(`${container.getName()} has no address on ${network.getName()}`);\n return ip;\n };\n const join = (container: StartedTestContainer, network: StartedNetwork) =>\n runtime.container.connectToNetwork(\n runtime.container.getById(container.getId()),\n runtime.network.getById(network.getId()),\n [],\n );\n const subnetOf = async (network: StartedNetwork): Promise<string> => {\n const info = await runtime.network.getById(network.getId()).inspect();\n const subnet = (info as { IPAM?: { Config?: Array<{ Subnet?: string }> } }).IPAM?.Config?.[0]?.Subnet;\n if (subnet === undefined) throw new Error(`${network.getName()} has no subnet`);\n return subnet;\n };\n try {\n // network j joins element j and element j + 1: 0 is the client, i + 1 is hop i, the last is the target\n for (let at = 0; at <= hops.length; at += 1) networks.push(await new Network().start());\n const last = networks[hops.length]!;\n // a stock image, or an image of the harness that holds the script of the server\n const targetImage = await targetImageOf(target);\n let stock = new GenericContainer(targetImage)\n .withNetwork(last)\n .withWaitStrategy(Wait.forLogMessage(target.ready));\n // the files of the target go in before it starts, for example the script of a server on a stock image\n if (target.files !== undefined)\n stock = stock.withCopyContentToContainer(\n Object.entries(target.files).map(([path, content]) => ({ content, target: path })),\n );\n if (target.lines === true) stock = stock.withLogConsumer(serverLines.consume);\n // a holder command keeps the container up when a fin fault kills the server process\n const server = await (\n target.command === undefined ? stock : stock.withCommand([...target.command])\n ).start();\n opened.push(server);\n const serverIp = await ipOn(server, last);\n\n // the hops, from the target to the client: each proxy gets the address of the endpoint after it\n const middlebox: MiddleboxEvent[] = [];\n const inChain: HopIn[] = [];\n const endpointOf = (element: number): string => {\n const at = endpointAfter(hops, element);\n if (at === hops.length + 1) return `${serverIp}:${target.port}`;\n const proxy = inChain[at - 1]!;\n return `${proxy.ip.client}:${(proxy.hop as ProxyHop).port}`;\n };\n for (let at = hops.length - 1; at >= 0; at -= 1) {\n const hop = hops[at]!;\n const [clientSide, targetSide] = [networks[at]!, networks[at + 1]!];\n if (hop.kind === \"router\") {\n const seen = lines<MiddleboxEvent>((line) => middlebox.push({ ...line, hop: hop.name }));\n // the router is privileged: it sets the sysctls of its own namespace, conntrack's among them\n const box = await new GenericContainer(await routerImage())\n .withNetwork(clientSide)\n .withPrivilegedMode()\n .withEnvironment({\n FORM: hop.form,\n IDLE: String(valueIn(hop.idle, \"s\")),\n PUMP: String(valueIn(hop.pump ?? PUMP, \"s\")),\n SLOW: String(valueIn(LISTING, \"s\")),\n })\n .withLogConsumer(seen.consume)\n .start();\n opened.push(box);\n // the router waits for the network of the side of the target, its eth1\n await join(box, targetSide);\n const ready = await seen.until([\"mbx-ready\"], 30_000, `router ${hop.name}`);\n await seen.until(hop.form === \"reset-both\" ? [\"resetter\"] : [\"ct-watch\"], 30_000, `router ${hop.name}`);\n // the injector of a fault at the router: FORWARD rules and netem in its namespace\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${box.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n inChain[at] = {\n hop,\n box,\n injector,\n ip: { client: await ipOn(box, clientSide), target: await ipOn(box, targetSide) },\n peer: { client: \"\", target: endpointOf(at + 1) },\n devices: { client: \"\", target: \"\" },\n netns: await netnsOf(box),\n sysctls: { \"net.netfilter.nf_conntrack_tcp_timeout_established\": String(ready[\"idle\"]) },\n };\n continue;\n }\n const next = endpointOf(at + 1);\n let proxy = new GenericContainer(hop.image).withNetwork(targetSide);\n if (hop.command !== undefined) proxy = proxy.withCommand([...hop.command]);\n if (hop.files !== undefined)\n proxy = proxy.withCopyContentToContainer(\n Object.entries(hop.files).map(([path, content]) => ({ content: templated(content, next), target: path })),\n );\n if (hop.ready !== undefined) proxy = proxy.withWaitStrategy(Wait.forLogMessage(hop.ready));\n const box = await proxy.start();\n opened.push(box);\n await join(box, clientSide);\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${box.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n inChain[at] = {\n hop,\n box,\n injector,\n ip: { client: await ipOn(box, clientSide), target: await ipOn(box, targetSide) },\n peer: { client: \"\", target: next },\n devices: { client: \"\", target: \"\" },\n netns: await netnsOf(injector),\n sysctls: {},\n };\n }\n\n const client = await new GenericContainer(image).withNetwork(networks[0]!).start();\n opened.push(client);\n const admin = await new GenericContainer(image)\n .withNetworkMode(`container:${client.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(admin);\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${server.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n const clientIp = await ipOn(client, networks[0]!);\n // the peer of each hop on the side of the client: the element before it, on the network between them\n for (let at = 0; at < inChain.length; at += 1) {\n const before = at === 0 ? clientIp : inChain[at - 1]!.ip.target;\n inChain[at] = { ...inChain[at]!, peer: { ...inChain[at]!.peer, client: before } };\n }\n\n // a privileged sidecar for each proxy that needs one, for its sysctls and its routes\n const admins = new Map<number, StartedTestContainer>();\n const adminOf = async (element: number): Promise<StartedTestContainer> => {\n if (element === 0) return admin;\n const hop = inChain[element - 1]!;\n if (hop.hop.kind === \"router\") return hop.box;\n let found = admins.get(element);\n if (found === undefined) {\n found = await new GenericContainer(image)\n .withNetworkMode(`container:${hop.box.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(found);\n admins.set(element, found);\n }\n return found;\n };\n for (const route of routesOf(hops)) {\n const where = await adminOf(route.from);\n const via = inChain[route.via - 1]!.ip.client;\n for (const network of route.networks)\n await output(where, [\"ip\", \"route\", \"replace\", await subnetOf(networks[network]!), \"via\", via]);\n }\n for (let at = 0; at < inChain.length; at += 1) {\n const hop = inChain[at]!;\n // the device toward each side, as the routes of the hop give it\n const toward = async (address: string) =>\n deviceOf(await output(hop.injector, [\"ip\", \"-o\", \"route\", \"get\", address.split(\":\")[0]!]));\n inChain[at] = { ...hop, devices: { client: await toward(hop.peer.client), target: await toward(hop.peer.target) } };\n if (hop.hop.sysctls === undefined) continue;\n // a router is privileged and sets its own sysctls. A proxy has a privileged sidecar for them\n const where = await adminOf(at + 1);\n const settings = Object.entries(hop.hop.sysctls).map(([key, value]) => `${key}=${value}`);\n await output(where, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n for (const key of Object.keys(hop.hop.sysctls)) hop.sysctls[key] = await output(where, [\"sysctl\", \"-n\", key]);\n }\n\n const netns = {\n client: await netnsOf(admin),\n target: await netnsOf(injector),\n ...(inChain.length === 0\n ? {}\n : { hops: Object.fromEntries(inChain.map((hop) => [hop.hop.name, hop.netns])) }),\n };\n const records = lines<KernelRecord>();\n const prober = await new GenericContainer(image)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\n \"/opt/harness/prober\",\n String(netns.client),\n String(netns.target),\n ...inChain.map((hop) => String(hop.netns)),\n ])\n .withLogConsumer(records.consume)\n .start();\n opened.push(prober);\n const begun = await records.until([\"begin\", \"fail\"], 30_000, \"prober\");\n if (begun.ev === \"fail\") throw new Error(`the prober failed: ${String(begun[\"why\"])}`);\n const first = endpointAfter(hops, 0);\n const targetIp = first === hops.length + 1 ? serverIp : inChain[first - 1]!.ip.client;\n const targetPort = first === hops.length + 1 ? target.port : (inChain[first - 1]!.hop as ProxyHop).port;\n const targetPeer = inChain.length === 0 ? clientIp : inChain.at(-1)!.ip.target;\n const placed: RunTopology | null =\n inChain.length === 0\n ? null\n : {\n targetPeer,\n hops: Object.fromEntries(\n inChain.map((hop) => [hop.hop.name, { netns: hop.netns, client: hop.peer.client, target: hop.peer.target }]),\n ),\n };\n const paused = runtime.container.getById(server.getId());\n let targetAdmin: StartedTestContainer | undefined;\n let signaller: StartedTestContainer | undefined;\n const hopNamed = (name: string): HopIn => {\n const found = inChain.find((hop) => hop.hop.name === name);\n if (found === undefined) throw new Error(`the topology has no hop ${name}`);\n return found;\n };\n /**\n * This function gives the namespace and the peer of the rules of a fault at a place, and the\n * other arguments of the injector. At a proxy, the rules are INPUT and OUTPUT rules, which act\n * on its sockets. A router forwards past INPUT and OUTPUT, thus its rules are FORWARD rules on\n * its device toward the side (`--via`).\n */\n const hopPlace = (at: FaultPlace): HopPlace => {\n if (!isHopPlace(at)) throw new Error(`a lab of a topology has no part ${JSON.stringify(at)}: a group cell opens a group lab`);\n return at;\n };\n const placeOf = (place: FaultPlace): [StartedTestContainer, string, string[]] => {\n if (place === \"client\") return [admin, targetIp, []];\n if (place === \"target\") return [injector, targetPeer, []];\n const at = hopPlace(place);\n const hop = hopNamed(at.hop);\n const peer = at.toward === \"client\" ? hop.peer.client : hop.peer.target.split(\":\")[0]!;\n const via = hop.hop.kind === \"router\" ? [\"--via\", hop.devices[at.toward]] : [];\n return [hop.injector, peer, via];\n };\n /** This function gives the namespace and the device of netem at a place: an end, or the device of a hop toward a side. */\n const deviceAt = (place: FaultPlace): [StartedTestContainer, string] => {\n if (place === \"client\") return [admin, \"eth0\"];\n if (place === \"target\") return [injector, \"eth0\"];\n const at = hopPlace(place);\n const hop = hopNamed(at.hop);\n return [hop.injector, hop.devices[at.toward]];\n };\n const toxiproxy = (): HopIn & { hop: ProxyHop & { api: { port: number; proxy: string } } } => {\n const found = inChain.find((hop) => hop.hop.kind === \"proxy\" && hop.hop.api !== undefined);\n if (found === undefined) throw new Error(\"the topology has no hop with the API of Toxiproxy\");\n return found as HopIn & { hop: ProxyHop & { api: { port: number; proxy: string } } };\n };\n const stamped = async (where: StartedTestContainer, argv: string[]) => {\n const stamp = JSON.parse((await output(where, argv)).split(\"\\n\").at(-1)!) as { t0: number; t1: number };\n return { t0: stamp.t0, t1: stamp.t1 };\n };\n const proxies = inChain.filter((hop) => hop.hop.kind === \"proxy\");\n\n return {\n targetIp,\n targetPort,\n clientIp,\n netns,\n topology: placed,\n middlebox,\n server: serverLines.seen,\n kernel: records.seen,\n async sysctl(values) {\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n if (settings.length > 0) await output(admin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n const read: Record<string, string> = {};\n for (const key of Object.keys(values))\n read[key] = await output(admin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async targetSysctl(values) {\n const read: Record<string, string> = {};\n if (Object.keys(values).length === 0) return read;\n // privileged, and only when a cell asks for it: otherwise the namespace of the target stays as it is\n targetAdmin ??= await new GenericContainer(image)\n .withNetworkMode(`container:${server.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(targetAdmin);\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n await output(targetAdmin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n for (const key of Object.keys(values))\n read[key] = await output(targetAdmin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async configure(values) {\n const read: Record<string, string> = {};\n if (Object.keys(values).length === 0) return read;\n const how = target.settings;\n if (how === undefined) throw new Error(`${target.image} names no way to set its settings`);\n for (const [name, value] of Object.entries(values)) {\n await output(server, [...how.set, name, value]);\n read[name] = (await output(server, [...how.get, name])).split(\"\\n\").at(-1) ?? \"\";\n }\n return read;\n },\n async fault(kind, at = \"target\", options = {}) {\n const [where, peer, via] = placeOf(at);\n return stamped(where, [\"/opt/harness/inject\", kind, peer, ...optionArgs(options), ...via]);\n },\n async lift(kind, at = \"target\", options = {}) {\n const [where, peer, via] = placeOf(at);\n await output(where, [\"/opt/harness/inject\", LIFT[kind], peer, ...optionArgs(options), ...via]);\n },\n async netem(spec, at = \"target\") {\n const [where, device] = deviceAt(at);\n const args =\n spec === null\n ? [\"/opt/harness/inject\", \"clear-netem\", device]\n : [\"/opt/harness/inject\", \"netem\", device, ...spec.split(/\\s+/)];\n return stamped(where, args);\n },\n async offload(on, at = \"target\") {\n // at a hop, the two devices: a router merges what it receives (GRO) and forwards it whole\n const devices = isHopPlace(at) ? Object.values(hopNamed(at.hop).devices) : [deviceAt(at)[1]];\n const [where] = deviceAt(at);\n for (const device of devices)\n await output(where, [\"/opt/harness/inject\", \"offload\", device, on ? \"on\" : \"off\"]);\n },\n async toxic(toxic) {\n const hop = toxiproxy();\n const api = `127.0.0.1:${hop.hop.api.port}`;\n const path = `/proxies/${hop.hop.api.proxy}/toxics`;\n return stamped(\n hop.injector,\n toxic === null\n ? [\"/opt/harness/inject\", \"http\", \"DELETE\", api, `${path}/fault`]\n : [\"/opt/harness/inject\", \"http\", \"POST\", api, path, toxicBody(toxic)],\n );\n },\n async serverClose() {\n if (target.close === undefined) throw new Error(`${target.image} names no way to close its clients`);\n await output(server, [...target.close]);\n },\n async agent(agentImage, argv, options = {}) {\n const Shared = shared(GenericContainer);\n const seen = lines<{ readonly ev: string }>();\n let started = new Shared(agentImage)\n .withNetworkMode(`container:${server.getId()}`)\n .withCommand([...argv])\n .withLogConsumer(seen.consume);\n if (options.pid === true) started = started.withPidMode(`container:${server.getId()}`);\n const container = await started.start();\n opened.push(container);\n return {\n lines: seen,\n exec: (args) => output(container, [...args]),\n stop: () =>\n container\n .stop(STOP)\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n async kill() {\n if (target.kill === undefined) throw new Error(`${target.image} names no way to kill its server`);\n await output(server, [...target.kill]);\n },\n async warm() {\n await output(admin, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", targetIp]);\n await output(injector, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", targetPeer]);\n for (const hop of proxies)\n await output(hop.injector, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", hop.peer.target.split(\":\")[0]!]);\n },\n async setup(commands) {\n for (const argv of commands) await output(server, [...argv]);\n },\n async heal(at, when) {\n const [where, peer, via] = placeOf(at);\n return stamped(where, [\"/opt/harness/inject\", \"clear\", peer, \"--at\", String(Math.round(when)), ...via]);\n },\n async forget() {\n await output(admin, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n await output(injector, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n for (const hop of proxies) await output(hop.injector, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n },\n freeze: () => paused.pause().then(() => undefined),\n thaw: () => paused.unpause().then(() => undefined),\n async signal(signal, at) {\n if (target.program === undefined) throw new Error(`${target.image} names no server process to signal`);\n // only when a cell asks for it: otherwise no sidecar shares the process namespace of the target\n signaller ??= await withPid(\n new GenericContainer(image).withNetworkMode(`container:${server.getId()}`).withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\"),\n `container:${server.getId()}`,\n ).start();\n if (!opened.includes(signaller)) opened.push(signaller);\n const when = at === undefined ? [] : [\"--at\", String(Math.round(at))];\n return stamped(signaller, [\"/opt/harness/inject\", \"signal\", target.program, signal, ...when]);\n },\n async probe(args) {\n const events = lines<ProbeEvent>();\n const container = await new GenericContainer(clientImage)\n .withNetworkMode(`container:${client.getId()}`)\n .withCommand([...command, ...args])\n .withLogConsumer(events.consume)\n .start();\n return {\n events: events.seen,\n until: (kinds, within) => events.until(kinds, within, \"client\"),\n go: () => output(container, [\"touch\", \"/tmp/go\"]).then(() => undefined),\n freeze: () =>\n runtime.container\n .getById(container.getId())\n .pause()\n .then(() => undefined),\n thaw: () =>\n runtime.container\n .getById(container.getId())\n .unpause()\n .then(() => undefined),\n kill: () =>\n runtime.container\n .getById(container.getId())\n .kill({ signal: \"SIGKILL\" })\n .then(() => undefined),\n // a killed client has no container to stop\n stop: () =>\n container\n .stop(STOP)\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n async settle() {\n // the prober drains its ring each 20 ms, and it writes a clock line each second\n const after = records.seen.length;\n const clock = await new Promise<KernelRecord | null>((resolve) => {\n const started = Date.now();\n const look = () => {\n const line = records.seen.slice(after).find((record) => record.ev === \"clock\");\n if (line !== undefined) resolve(line);\n else if (Date.now() - started > 5_000) resolve(null);\n else setTimeout(look, 50);\n };\n look();\n });\n return clock === null ? null : Number(clock[\"lost\"] ?? 0);\n },\n async manifest(sysctls, settings = {}, targetSysctls = {}) {\n const config = await output(admin, [\"zcat\", \"/proc/config.gz\"]).catch(() => \"\");\n const inspect = async (container: StartedTestContainer) =>\n runtime.container.inspect(runtime.container.getById(container.getId()));\n const hopsOf = async (): Promise<ManifestHop[]> => {\n const out: ManifestHop[] = [];\n for (const hop of inChain) {\n const box = hop.hop;\n out.push({\n name: box.name,\n kind: box.kind,\n image: box.kind === \"router\" ? await routerImage() : box.image,\n id: (await inspect(hop.box)).Image,\n settings:\n box.kind === \"router\"\n ? {\n form: box.form,\n idle: String(valueIn(box.idle, \"s\")),\n pump: String(valueIn(box.pump ?? PUMP, \"s\")),\n slow: String(valueIn(LISTING, \"s\")),\n packages: (await output(hop.box, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort().join(\" \"),\n }\n : { ...box.settings, ...box.files },\n sysctls: hop.sysctls,\n });\n }\n return out;\n };\n return {\n kernel: await output(client, [\"uname\", \"-r\"]),\n architecture: await output(client, [\"uname\", \"-m\"]),\n ...(await (async () => {\n const configured = /^CONFIG_HZ=(\\d+)$/m.exec(config)?.[1];\n if (configured !== undefined) return { hz: configured, hzFrom: \"config\" as const };\n const measured = await measuredHz(admin);\n return measured === null ? { hz: null } : { hz: measured, hzFrom: \"measured\" as const };\n })()),\n boot_id: await output(client, [\"cat\", \"/proc/sys/kernel/random/boot_id\"]),\n docker: `${runtime.info.containerRuntime.serverVersion} on ${runtime.info.containerRuntime.operatingSystem}`,\n target: {\n image: targetImage,\n ...(target.harness === undefined ? {} : { harness: target.harness, base: target.image }),\n id: (await inspect(server)).Image,\n settings,\n sysctls: targetSysctls,\n ...(target.files === undefined\n ? {}\n : {\n files: Object.fromEntries(\n Object.entries(target.files).map(([path, content]) => [path, createHash(\"sha256\").update(content).digest(\"hex\")]),\n ),\n }),\n ...(serverLines.seen[0] === undefined ? {} : { first: serverLines.seen[0] }),\n },\n harness: {\n image,\n id: (await inspect(client)).Image,\n packages: (await output(client, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort(),\n },\n client: { kind: clientKind, image: clientImage },\n netns,\n sysctls,\n ...(inChain.length === 0 ? {} : { hops: await hopsOf() }),\n };\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n};\n","import type { StartedNetwork, StartedTestContainer } from \"testcontainers\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type {\n Cell,\n Group,\n GroupPlace,\n GroupRole,\n KernelRecord,\n ProbeEvent,\n Run,\n RunPart,\n SentinelEvent,\n} from \"./cell.ts\";\nimport type { Manifest } from \"./lab.ts\";\nimport { STOP, containers, harnessImage, lines, netnsOf, nodeClientImage, output, withPid } from \"./lab.ts\";\n\n/**\n * This module is the lab of a replicated group: a primary and its replicas, and the Sentinels\n * that decide which member is the primary. It opens one environment for a group cell, and it does\n * the runs of the cell in it. Each member and each Sentinel is a container from the stock image of\n * the group, in its own network namespace. An injector sidecar shares its network and process\n * namespaces. The client of each run is ioredis in Sentinel mode. An observer subscribes to the\n * events of each Sentinel, and the prober watches the namespace of the client.\n */\n\n/** A stamp is the time of the injector around one commit, on CLOCK_MONOTONIC in nanoseconds. */\nexport interface Stamp {\n readonly t0: number;\n readonly t1: number;\n}\n\n/** A group network fault is a black hole between a part and its peers, or a reset of each segment from them. */\nexport type GroupNetworkFault = \"blackhole\" | \"rst\";\n\n/** A group client is the client container of one run of a group cell. */\nexport interface GroupClient {\n readonly events: readonly ProbeEvent[];\n until(kinds: readonly string[], within: number): Promise<ProbeEvent>;\n stop(): Promise<void>;\n}\n\n/** A group lab is one environment of a group cell. */\nexport interface GroupLab {\n readonly group: Group;\n readonly clientIp: string;\n readonly observerIp: string;\n readonly members: Readonly<Record<string, RunPart>>;\n readonly sentinels: Readonly<Record<string, RunPart>>;\n /** These are the namespaces that the prober watches: the client, and the Sentinels for a calibration. */\n readonly netns: { readonly client: number; readonly watched: readonly number[] };\n /** These are all the records that the prober wrote, in sequence. */\n readonly kernel: readonly KernelRecord[];\n /** These are all the lines of the observer of the Sentinels, in sequence. */\n readonly timeline: readonly SentinelEvent[];\n /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */\n sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method waits until the group is whole and calm, and gives the name of its primary. */\n converge(within?: number): Promise<string>;\n /** This method removes each key from the group, and waits until each replica has the removal. */\n flush(primary: string): Promise<void>;\n /** This method makes each Sentinel forget the state of the group (`SENTINEL RESET`): an aborted failover then holds off no later failover. */\n reset(): Promise<void>;\n /** This method gives the sequence numbers that the group holds, from its primary. */\n holds(primary: string): Promise<number[]>;\n /** This method gives the part that has a role in the group, when `primary` is the primary. */\n partOf(role: GroupRole, primary: string): string;\n /** This method starts the client of a run, with the arguments of `client-sentinel.mjs`. */\n client(args: readonly string[]): Promise<GroupClient>;\n /** This method applies a network fault between a part and some of its peers, at once or at an instant of the VM. */\n fault(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace[\"cuts\"]>, at?: number): Promise<Stamp>;\n /** This method removes a network fault, at once or at an instant of the VM. */\n lift(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace[\"cuts\"]>, at?: number): Promise<Stamp>;\n /** This method sends a signal to the server process of a part, at once or at an instant of the VM. */\n signal(part: string, signal: \"KILL\" | \"STOP\" | \"CONT\", at?: number): Promise<Stamp>;\n /** This method starts the server of a member again, after a fin. */\n restart(member: string): Promise<void>;\n /** This method asks the first Sentinel for a failover of the group. */\n failover(coordinated: boolean): Promise<Stamp>;\n /** This method removes the TCP metrics of the client namespace: each run starts from a kernel that keeps nothing. */\n forget(): Promise<void>;\n /** This method waits for the next clock line of the prober, which gives the count of lost records. */\n settle(): Promise<number | null>;\n manifest(sysctls: Readonly<Record<string, string>>): Promise<Manifest>;\n close(): Promise<void>;\n}\n\n/** This function gives the configuration file of a member: its port, its settings, and the primary that a replica follows. */\nexport const memberConfig = (group: Group, primary: string | null): string =>\n [\n `port ${group.port}`,\n ...Object.entries(group.memberSettings).map(([key, value]) => `${key} ${value}`),\n ...(primary === null ? [] : [`replicaof ${primary} ${group.port}`]),\n \"\",\n ].join(\"\\n\");\n\n/** This function gives the configuration file of a Sentinel: its port, the group that it monitors, and the settings of the group. */\nexport const sentinelConfig = (group: Group, primary: string): string =>\n [\n `port ${group.sentinelPort}`,\n `sentinel monitor ${group.name} ${primary} ${group.port} ${group.quorum}`,\n ...Object.entries(group.sentinelSettings).map(([key, value]) => `sentinel ${key} ${group.name} ${value}`),\n \"\",\n ].join(\"\\n\");\n\n/**\n * This function gives the peers that a fault on a part cuts it from, by their addresses. `all` is\n * each other part and the client, `group` is the other parts, and `clients` is the client. The\n * observer is not a peer, thus the timeline of the group stays whole.\n */\nexport const peersOf = (\n parts: { readonly members: Readonly<Record<string, RunPart>>; readonly sentinels: Readonly<Record<string, RunPart>> },\n client: string,\n part: string,\n cuts: NonNullable<GroupPlace[\"cuts\"]>,\n): string[] => {\n const others = [...Object.entries(parts.members), ...Object.entries(parts.sentinels)]\n .filter(([name]) => name !== part)\n .map(([, at]) => at.ip);\n if (cuts === \"clients\") return [client];\n if (cuts === \"group\") return others;\n return [...others, client];\n};\n\n/** This function reads the flat reply of a command line client into its fields: name, value, name, value. */\nexport const fieldsOf = (text: string): Array<Record<string, string>> => {\n const out: Array<Record<string, string>> = [];\n const words = text.split(\"\\n\").map((line) => line.replace(/\\r$/, \"\"));\n let current: Record<string, string> | null = null;\n for (let at = 0; at + 1 < words.length; at += 2) {\n const [key, value] = [words[at]!, words[at + 1]!];\n // each entry of a list starts again at its name\n if (key === \"name\" || current === null) {\n current = {};\n out.push(current);\n }\n current[key] = value;\n }\n return out;\n};\n\n/** This function reads the INFO of a server into its fields. */\nexport const infoOf = (text: string): Record<string, string> => {\n const out: Record<string, string> = {};\n for (const line of text.split(\"\\n\")) {\n const at = line.indexOf(\":\");\n if (line.startsWith(\"#\") || at === -1) continue;\n out[line.slice(0, at)] = line.slice(at + 1).trim();\n }\n return out;\n};\n\n/**\n * A view of the group is what its parts tell at one time. `healthy` tells if each Sentinel agrees\n * on one primary with no flag but `master`, and knows each replica and each other Sentinel. Each\n * replica must follow that primary with its link up, and the primary must have each replica online,\n * with no failover in progress.\n */\nexport interface GroupView {\n readonly primary: string | null;\n readonly healthy: boolean;\n readonly why: string;\n}\n\n/** This function judges the views of the Sentinels and of the members: the primary that they agree on, if the group is healthy. */\nexport const viewOf = (\n group: Group,\n ips: Readonly<Record<string, string>>,\n sentinels: ReadonlyArray<{ readonly master: Record<string, string>; readonly replicas: ReadonlyArray<Record<string, string>> }>,\n members: Readonly<Record<string, Record<string, string>>>,\n): GroupView => {\n const others = String(group.sentinels.length - 1);\n const replicas = group.members.length - 1;\n const addresses = new Set(sentinels.map((each) => each.master[\"ip\"]));\n if (addresses.size !== 1) return { primary: null, healthy: false, why: `the Sentinels name ${addresses.size} primaries` };\n const ip = [...addresses][0]!;\n const primary = Object.entries(ips).find(([, at]) => at === ip)?.[0] ?? null;\n if (primary === null) return { primary: null, healthy: false, why: `no member has the address ${ip}` };\n for (const [at, each] of sentinels.entries()) {\n if (each.master[\"flags\"] !== \"master\") return { primary, healthy: false, why: `Sentinel ${at} flags the primary ${each.master[\"flags\"]}` };\n if (each.master[\"num-other-sentinels\"] !== others) return { primary, healthy: false, why: `Sentinel ${at} knows ${each.master[\"num-other-sentinels\"]} other Sentinels` };\n const good = each.replicas.filter(\n (replica) => replica[\"flags\"] === \"slave\" && replica[\"master-link-status\"] === \"ok\" && replica[\"master-host\"] === ip,\n );\n if (good.length !== replicas || each.replicas.length !== replicas)\n return { primary, healthy: false, why: `Sentinel ${at} has ${good.length} healthy replicas of ${each.replicas.length}` };\n }\n const info = members[primary] ?? {};\n if (info[\"role\"] !== \"master\") return { primary, healthy: false, why: `${primary} reports the role ${info[\"role\"]}` };\n if (info[\"master_failover_state\"] !== undefined && info[\"master_failover_state\"] !== \"no-failover\")\n return { primary, healthy: false, why: `${primary} is in the failover state ${info[\"master_failover_state\"]}` };\n const online = Object.entries(info).filter(([key, value]) => /^slave\\d+$/.test(key) && value.includes(\"state=online\")).length;\n if (online !== replicas) return { primary, healthy: false, why: `${primary} has ${online} replicas online` };\n for (const name of group.members) {\n if (name === primary) continue;\n const each = members[name] ?? {};\n if (each[\"role\"] !== \"slave\" || each[\"master_host\"] !== ip || each[\"master_link_status\"] !== \"up\")\n return { primary, healthy: false, why: `${name} is not a replica of ${primary} with its link up` };\n }\n return { primary, healthy: true, why: \"healthy\" };\n};\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** This time is the longest that a group takes to converge before a run, or after it. */\nexport const CONVERGE_MS = 240_000;\n\n/**\n * This function opens one environment of a group cell:\n * - one new network, with each member, each Sentinel, the client and the observer on it.\n * - each member: the stock image, its server as the child of a shell, thus a fin can stop the\n * server and the container stays up. The first member is the primary, and the others follow it.\n * - each Sentinel: the stock image, its Sentinel as the child of a shell, which monitors the first member.\n * - an injector sidecar for each part, in its network and process namespaces.\n * - a client that holds its own network namespace, with a privileged sidecar for its sysctls.\n * - the observer of the Sentinels, in a namespace of its own.\n * - the prober, which watches the namespace of the client, and of each Sentinel for a calibration.\n */\nexport const openGroupLab = async (group: Group, options: { readonly watchSentinels?: boolean } = {}): Promise<GroupLab> => {\n const image = await harnessImage();\n const node = await nodeClientImage();\n const { GenericContainer, Network, Wait, getContainerRuntimeClient } = await containers();\n const runtime = await getContainerRuntimeClient();\n const opened: StartedTestContainer[] = [];\n let network: StartedNetwork | undefined;\n const close = async () => {\n for (const container of opened.reverse()) await container.stop(STOP).catch(() => undefined);\n await network?.stop().catch(() => undefined);\n };\n const ipOf = async (container: StartedTestContainer): Promise<string> => {\n const info = await runtime.container.inspect(runtime.container.getById(container.getId()));\n const ip = info.NetworkSettings.Networks[network!.getName()]?.IPAddress;\n if (ip === undefined || ip === \"\") throw new Error(`${container.getName()} has no address on ${network!.getName()}`);\n return ip;\n };\n try {\n network = await new Network().start();\n const boxes = new Map<string, StartedTestContainer>();\n const injectors = new Map<string, StartedTestContainer>();\n const parts = { members: {} as Record<string, RunPart>, sentinels: {} as Record<string, RunPart> };\n /** This function starts one part: the stock image with its configuration, its program as the child of a shell, and its injector. */\n const start = async (name: string, program: string, file: string, content: string, ready: string) => {\n const box = await new GenericContainer(group.image)\n .withNetwork(network!)\n .withNetworkAliases(name)\n .withCopyContentToContainer([{ content, target: file }])\n .withCommand([\"sh\", \"-c\", `${program} ${file} & while :; do sleep 3600; done`])\n .withWaitStrategy(Wait.forLogMessage(ready))\n .start();\n opened.push(box);\n boxes.set(name, box);\n const injector = await withPid(\n new GenericContainer(image).withNetworkMode(`container:${box.getId()}`).withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\"),\n `container:${box.getId()}`,\n ).start();\n opened.push(injector);\n injectors.set(name, injector);\n return { ip: await ipOf(box), netns: await netnsOf(injector) };\n };\n const [first, ...rest] = group.members;\n if (first === undefined) throw new Error(\"a group has no member\");\n parts.members[first] = await start(first, group.programs.server, \"/data/server.conf\", memberConfig(group, null), group.ready.member);\n const primaryIp = parts.members[first].ip;\n for (const name of rest)\n parts.members[name] = await start(name, group.programs.server, \"/data/server.conf\", memberConfig(group, primaryIp), group.ready.member);\n for (const name of group.sentinels)\n parts.sentinels[name] = await start(name, group.programs.sentinel, \"/data/sentinel.conf\", sentinelConfig(group, primaryIp), group.ready.sentinel);\n\n const holder = await new GenericContainer(image).withNetwork(network).start();\n opened.push(holder);\n const admin = await new GenericContainer(image)\n .withNetworkMode(`container:${holder.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(admin);\n const clientIp = await ipOf(holder);\n\n const sentinelAddresses = group.sentinels.map((name) => `${parts.sentinels[name]!.ip}:${group.sentinelPort}`);\n const timeline = lines<SentinelEvent>();\n const observer = await new GenericContainer(node)\n .withNetwork(network)\n .withCommand([\"node\", \"/opt/harness/node/observer.mjs\", sentinelAddresses.join(\",\")])\n .withLogConsumer(timeline.consume)\n .start();\n opened.push(observer);\n const observerIp = await ipOf(observer);\n for (const address of sentinelAddresses)\n await new Promise<void>((resolve, reject) => {\n const started = Date.now();\n const look = () => {\n if (timeline.seen.some((line) => line.ev === \"subscribed\" && line[\"sentinel\"] === address)) resolve();\n else if (Date.now() - started > 30_000) reject(new Error(`the observer never subscribed to ${address}`));\n else setTimeout(look, 50);\n };\n look();\n });\n\n const clientNetns = await netnsOf(admin);\n const watched = [clientNetns, ...(options.watchSentinels === true ? group.sentinels.map((name) => parts.sentinels[name]!.netns) : [])];\n const records = lines<KernelRecord>();\n const prober = await new GenericContainer(image)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\"/opt/harness/prober\", ...watched.map(String)])\n .withLogConsumer(records.consume)\n .start();\n opened.push(prober);\n const begun = await records.until([\"begin\", \"fail\"], 30_000, \"prober\");\n if (begun.ev === \"fail\") throw new Error(`the prober failed: ${String(begun[\"why\"])}`);\n\n const box = (name: string): StartedTestContainer => {\n const found = boxes.get(name);\n if (found === undefined) throw new Error(`the group has no part ${name}`);\n return found;\n };\n const injectorOf = (name: string): StartedTestContainer => {\n const found = injectors.get(name);\n if (found === undefined) throw new Error(`the group has no part ${name}`);\n return found;\n };\n const cli = (name: string, args: readonly string[]): Promise<string> => {\n const port = group.sentinels.includes(name) ? group.sentinelPort : group.port;\n return output(box(name), [group.programs.cli, \"-p\", String(port), ...args]);\n };\n const stamped = async (where: StartedTestContainer, argv: string[]): Promise<Stamp> => {\n const stamp = JSON.parse((await output(where, argv)).split(\"\\n\").at(-1)!) as Stamp;\n return { t0: stamp.t0, t1: stamp.t1 };\n };\n const ips: Record<string, string> = Object.fromEntries(Object.entries(parts.members).map(([name, at]) => [name, at.ip]));\n const view = async (): Promise<GroupView> => {\n const sentinels = [];\n for (const name of group.sentinels) {\n const master = fieldsOf(await cli(name, [\"SENTINEL\", \"MASTER\", group.name]))[0] ?? {};\n const replicas = fieldsOf(await cli(name, [\"SENTINEL\", \"REPLICAS\", group.name]));\n sentinels.push({ master, replicas });\n }\n const members: Record<string, Record<string, string>> = {};\n for (const name of group.members) members[name] = infoOf(await cli(name, [\"INFO\", \"replication\"]));\n return viewOf(group, ips, sentinels, members);\n };\n const after = (at: number | undefined): string[] => (at === undefined ? [] : [\"--at\", String(Math.round(at))]);\n const roles = (role: GroupRole, primary: string): string => {\n if (role === \"primary\") return primary;\n if (role === \"sentinel\") return group.sentinels[0]!;\n const replica = group.members.find((name) => name !== primary);\n if (replica === undefined) throw new Error(\"the group has no replica\");\n return replica;\n };\n\n return {\n group,\n clientIp,\n observerIp,\n members: parts.members,\n sentinels: parts.sentinels,\n netns: { client: clientNetns, watched },\n kernel: records.seen,\n timeline: timeline.seen,\n async sysctl(values) {\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n if (settings.length > 0) await output(admin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n const read: Record<string, string> = {};\n for (const key of Object.keys(values)) read[key] = await output(admin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async converge(within = CONVERGE_MS) {\n const started = Date.now();\n let calm = 0;\n let last: GroupView = { primary: null, healthy: false, why: \"no view yet\" };\n while (Date.now() - started < within) {\n last = await view().catch((error: unknown) => ({ primary: null, healthy: false, why: (error as Error).message }));\n // two healthy views in a row: one view can come in the middle of a change\n calm = last.healthy ? calm + 1 : 0;\n if (calm >= 2 && last.primary !== null) return last.primary;\n await sleep(1_000);\n }\n throw new Error(`the group did not converge in ${within} ms: ${last.why}`);\n },\n async flush(primary) {\n await cli(primary, [\"FLUSHALL\"]);\n const acked = Number(await cli(primary, [\"WAIT\", String(group.members.length - 1), \"5000\"]));\n if (acked !== group.members.length - 1) throw new Error(`${acked} replicas acknowledged the flush`);\n },\n async reset() {\n for (const name of group.sentinels) await cli(name, [\"SENTINEL\", \"RESET\", group.name]);\n },\n async holds(primary) {\n const keys = await cli(primary, [\"--scan\", \"--pattern\", \"w:*\"]);\n return keys\n .split(\"\\n\")\n .map((key) => Number(key.trim().slice(2)))\n .filter((n) => Number.isInteger(n) && n > 0)\n .sort((a, b) => a - b);\n },\n partOf: roles,\n async client(args) {\n const events = lines<ProbeEvent>();\n const container = await new GenericContainer(node)\n .withNetworkMode(`container:${holder.getId()}`)\n .withCommand([\"node\", \"/opt/harness/node/client-sentinel.mjs\", ...args])\n .withLogConsumer(events.consume)\n .start();\n return {\n events: events.seen,\n until: (kinds, within) => events.until(kinds, within, \"client\"),\n // a TERM first: the client writes its done line, then stops\n stop: () =>\n container\n .stop({ timeout: 2 })\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n fault(kind, part, cuts, at) {\n const peers = peersOf(parts, clientIp, part, cuts).join(\",\");\n return stamped(injectorOf(part), [\"/opt/harness/inject\", kind, peers, ...after(at)]);\n },\n lift(kind, part, cuts, at) {\n const peers = peersOf(parts, clientIp, part, cuts).join(\",\");\n return stamped(injectorOf(part), [\"/opt/harness/inject\", kind === \"rst\" ? \"clear-rst\" : \"clear\", peers, ...after(at)]);\n },\n signal(part, signal, at) {\n const program = group.sentinels.includes(part) ? group.programs.sentinel : group.programs.server;\n return stamped(injectorOf(part), [\"/opt/harness/inject\", \"signal\", program, signal, ...after(at)]);\n },\n async restart(member) {\n await output(box(member), [\"sh\", \"-c\", `${group.programs.server} /data/server.conf --daemonize yes`]);\n const started = Date.now();\n while ((await cli(member, [\"PING\"]).catch(() => \"\")) !== \"PONG\") {\n if (Date.now() - started > 30_000) throw new Error(`${member} did not start again`);\n await sleep(200);\n }\n },\n failover(coordinated) {\n const sentinel = group.sentinels[0]!;\n return stamped(injectorOf(sentinel), [\n \"/opt/harness/inject\",\n \"resp\",\n `127.0.0.1:${group.sentinelPort}`,\n \"SENTINEL\",\n \"FAILOVER\",\n group.name,\n ...(coordinated ? [\"COORDINATED\"] : []),\n ]);\n },\n async forget() {\n await output(admin, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n },\n async settle() {\n const from = records.seen.length;\n const clock = await new Promise<KernelRecord | null>((resolve) => {\n const started = Date.now();\n const look = () => {\n const line = records.seen.slice(from).find((record) => record.ev === \"clock\");\n if (line !== undefined) resolve(line);\n else if (Date.now() - started > 5_000) resolve(null);\n else setTimeout(look, 50);\n };\n look();\n });\n return clock === null ? null : Number(clock[\"lost\"] ?? 0);\n },\n async manifest(sysctls) {\n const config = await output(admin, [\"zcat\", \"/proc/config.gz\"]).catch(() => \"\");\n const inspect = async (container: StartedTestContainer) =>\n runtime.container.inspect(runtime.container.getById(container.getId()));\n const memberSettings = async (name: string) => {\n const read: Record<string, string> = {};\n // the reply is the name, then the value: an empty value is the end of the reply\n for (const key of Object.keys(group.memberSettings)) read[key] = (await cli(name, [\"CONFIG\", \"GET\", key])).split(\"\\n\")[1] ?? \"\";\n return read;\n };\n const sentinelSettings = async (name: string) => {\n const master = fieldsOf(await cli(name, [\"SENTINEL\", \"MASTER\", group.name]))[0] ?? {};\n const read: Record<string, string> = { quorum: master[\"quorum\"] ?? \"\" };\n for (const key of Object.keys(group.sentinelSettings)) read[key] = master[key] ?? \"\";\n return read;\n };\n const members = [];\n for (const name of group.members)\n members.push({ name, ip: parts.members[name]!.ip, id: (await inspect(box(name))).Image, settings: await memberSettings(name) });\n const sentinels = [];\n for (const name of group.sentinels)\n sentinels.push({ name, ip: parts.sentinels[name]!.ip, id: (await inspect(box(name))).Image, settings: await sentinelSettings(name) });\n return {\n kernel: await output(holder, [\"uname\", \"-r\"]),\n architecture: await output(holder, [\"uname\", \"-m\"]),\n hz: /^CONFIG_HZ=(\\d+)$/m.exec(config)?.[1] ?? null,\n boot_id: await output(holder, [\"cat\", \"/proc/sys/kernel/random/boot_id\"]),\n docker: `${runtime.info.containerRuntime.serverVersion} on ${runtime.info.containerRuntime.operatingSystem}`,\n target: { image: group.image, id: members[0]!.id, settings: members[0]!.settings, sysctls: {} },\n harness: {\n image,\n id: (await inspect(holder)).Image,\n packages: (await output(holder, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort(),\n },\n client: { kind: \"ioredis\", image: node },\n netns: { client: clientNetns, target: parts.members[first]!.netns },\n sysctls,\n group: { members, sentinels, observer: { image: node } },\n };\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n};\n\n// ---------------------------------------------------------------------------------------------\n// the runs of a group cell\n// ---------------------------------------------------------------------------------------------\n\n/** This count is the number of accepted writes that a run waits for before its fault: the stream is steady then. */\nconst STEADY = 20;\n/** This time is how long a run continues after its outcome, so that the record shows what came next. */\nconst TAIL_MS = 2_000;\n\n/** This function gives the address of a member, with the port of the group. */\nconst addressOf = (lab: GroupLab, member: string): string => `${lab.members[member]!.ip}:${lab.group.port}`;\n\n/** This function gives the latest time that a group cell waits for its outcome, in ms from the fault. */\nexport const deadlineOf = (cell: Cell): number => {\n const { prediction } = cell;\n const seconds =\n prediction.detects === true\n ? valueIn(prediction.high, \"s\")\n : prediction.detects === false && prediction.recovers !== undefined\n ? valueIn(prediction.recovers.high, \"s\")\n : valueIn(prediction.window, \"s\");\n return seconds * 1_000 + 10_000;\n};\n\n/** This function gives the arguments of the client of a group cell. */\nconst clientArgs = (cell: Cell, lab: GroupLab): string[] => {\n const sentinels = cell.group!.sentinels.map((name) => `${lab.sentinels[name]!.ip}:${cell.group!.sentinelPort}`);\n return [\n \"--sentinels\",\n sentinels.join(\",\"),\n \"--name\",\n cell.group!.name,\n \"--interval-ms\",\n String(cell.probe.interval === undefined ? 50 : valueIn(cell.probe.interval, \"ms\")),\n \"--max-s\",\n String(Math.ceil(deadlineOf(cell) / 1_000 + 120)),\n ...(cell.driverOptions === undefined ? [] : [\"--options\", JSON.stringify(cell.driverOptions)]),\n ];\n};\n\n/** This function waits until a condition on the lines of the client holds, or for `within` ms. It gives the first line that matches. */\nconst waitFor = (\n client: GroupClient,\n match: (event: ProbeEvent) => boolean,\n within: number,\n): Promise<ProbeEvent | undefined> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const found = client.events.find(match);\n if (found !== undefined || Date.now() - started > within) resolve(found);\n else setTimeout(look, 20);\n };\n look();\n });\n\n/** This function gives the latest subscription of the observer to each Sentinel, in some lines of the observer. */\nexport const subscriptionsOf = (timeline: readonly SentinelEvent[]): SentinelEvent[] => {\n const latest = new Map<string, SentinelEvent>();\n for (const line of timeline) if (line.ev === \"subscribed\") latest.set(String(line.sentinel), line);\n return [...latest.values()];\n};\n\n/** This function gives the count of lost records in the latest clock line of the prober. */\nconst droppedSoFar = (records: readonly KernelRecord[]): number => {\n for (let at = records.length - 1; at >= 0; at -= 1)\n if (records[at]!.ev === \"clock\") return Number(records[at]![\"lost\"] ?? 0);\n return 0;\n};\n\n/**\n * This function does one run of a group cell:\n * - The group converges, and its keys go. The client connects through the Sentinels, and its\n * stream of writes gets steady.\n * - The fault occurs on its part, as the primary of this run makes the roles. A fault with a\n * hold lifts at its instant in the VM. A stall lifts at its length after the first write into it.\n * - The run waits for its outcome: the first write that a new primary accepts, or the first one\n * after the lift on the same primary. Then it continues for a moment.\n * - Each fault goes, the client stops, and the group converges again. The keys that it then holds\n * tell which writes it kept.\n */\nexport const groupOnce = async (lab: GroupLab, cell: Cell, environment: number, index: number): Promise<Run> => {\n const group = cell.group!;\n const primary = await lab.converge();\n await lab.flush(primary);\n await lab.forget();\n const from = lab.kernel.length;\n const timelineFrom = lab.timeline.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const client = await lab.client(clientArgs(cell, lab));\n const lands = cell.prediction.group?.lands ?? \"new\";\n const place = (cell.faultAt ?? { role: \"primary\" }) as GroupPlace;\n let fault: Run[\"fault\"] = null;\n let heal: Run[\"heal\"] = null;\n let at: string | null = null;\n const undo: Array<() => Promise<unknown>> = [];\n try {\n await client.until([\"ready\"], 60_000);\n const steady = await waitFor(client, (event) => event.ev === \"ack\" && Number(event[\"n\"]) >= STEADY, 30_000);\n if (steady === undefined) throw new Error(`the client did not write ${STEADY} times in 30 s`);\n let healing: Promise<Run[\"heal\"]> | null = null;\n switch (cell.fault) {\n case \"blackhole\":\n case \"partition\":\n case \"rst\": {\n const kind = cell.fault === \"rst\" ? \"rst\" : \"blackhole\";\n const cuts = place.cuts ?? (cell.fault === \"partition\" ? \"group\" : \"all\");\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.fault(kind, part, cuts);\n undo.push(() => lab.lift(kind, part, cuts));\n if (cell.stall !== undefined) {\n // the first write into the fault arms the retransmissions: the stall counts from it\n const t1 = fault.t1;\n const sent = await waitFor(client, (event) => event.ev === \"sent\" && event.t > t1, 5_000);\n if (sent !== undefined) healing = lab.lift(kind, part, cuts, sent.t + valueIn(cell.stall, \"s\") * 1e9);\n } else if (cell.hold !== undefined) {\n healing = lab.lift(kind, part, cuts, fault.t0 + valueIn(cell.hold, \"s\") * 1e9);\n }\n break;\n }\n case \"freeze-process\": {\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.signal(part, \"STOP\");\n undo.push(() => lab.signal(part, \"CONT\"));\n if (cell.hold !== undefined) healing = lab.signal(part, \"CONT\", fault.t0 + valueIn(cell.hold, \"s\") * 1e9);\n break;\n }\n case \"fin\": {\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.signal(part, \"KILL\");\n if (group.members.includes(part)) undo.push(() => lab.restart(part));\n break;\n }\n case \"planned-failover\":\n fault = await lab.failover(cell.coordinated === true);\n break;\n default:\n throw new Error(`a group cell has no fault ${cell.fault}`);\n }\n // the injector holds the lift in the VM: its exec returns at the instant, and the stamps come with it\n const healed =\n healing === null\n ? Promise.resolve(null)\n : healing.then((stamp) => {\n heal = stamp;\n // the lift came: nothing is left to undo\n undo.length = 0;\n return stamp;\n });\n // a run that stops early lifts the fault itself, and the late lift then has nothing to remove\n healed.catch(() => undefined);\n const t0 = fault.t0;\n const old = addressOf(lab, primary);\n const deadline = deadlineOf(cell);\n const outcome =\n lands === \"new\"\n ? await waitFor(\n client,\n (event) => event.ev === \"ack\" && event.t > t0 && typeof event[\"at\"] === \"string\" && event[\"at\"] !== old,\n deadline,\n )\n : await (async () => {\n await healed;\n const after = (heal as Run[\"heal\"])?.t0 ?? t0;\n return waitFor(client, (event) => event.ev === \"ack\" && event.t > after, deadline);\n })();\n if (outcome !== undefined) await sleep(TAIL_MS);\n await healed;\n } finally {\n // each fault that is still on goes, and a killed server starts again\n for (const step of undo.reverse()) await step().catch(() => undefined);\n await client.stop();\n }\n const settled = await lab.settle();\n const lost = settled === null ? null : settled - lostBefore;\n const after = await lab.converge().catch(() => null);\n const holds = after === null ? null : await lab.holds(after);\n // a failover that a Sentinel aborted holds off its next attempt for 2 × failover-timeout. The\n // next run must not inherit that hold-off: each Sentinel forgets the state of the group, and the\n // next converge waits until each one knows the group again\n if (lab.timeline.slice(timelineFrom).some((line) => line.ev === \"sentinel\" && String(line.event).startsWith(\"-failover-abort\")))\n await lab.reset();\n return {\n environment,\n index,\n events: [...client.events],\n fault,\n ...(heal === null ? {} : { heal }),\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.members[primary]!.netns,\n lost,\n group: {\n members: lab.members,\n sentinels: lab.sentinels,\n primary,\n at,\n // the subscription of the observer to each Sentinel came before the run: its address names\n // the socket of each Sentinel's writes, which a calibration reads\n timeline: [...subscriptionsOf(lab.timeline.slice(0, timelineFrom)), ...lab.timeline.slice(timelineFrom)],\n after,\n holds,\n },\n };\n};\n","import type { StartedNetwork, StartedTestContainer } from \"testcontainers\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, GroupPlace, GroupRole, KernelRecord, ProbeEvent, ReplicaSet, Run, RunPart, SentinelEvent } from \"./cell.ts\";\nimport type { GroupClient, GroupNetworkFault, GroupView, Stamp } from \"./group.ts\";\nimport { CONVERGE_MS, deadlineOf } from \"./group.ts\";\nimport type { Manifest } from \"./lab.ts\";\nimport { STOP, containers, harnessImage, lines, mongoClientImage, netnsOf, output, withPid } from \"./lab.ts\";\n\n/**\n * This module is the lab of a replica set: a replicated group with no external authority, whose\n * members elect their own primary. It opens one environment for a replica set cell, for the runs\n * of the cell. Each member is a container from the stock image of the set, in its own network\n * namespace. An injector sidecar shares its network and process namespaces.\n *\n * The client of each run is the MongoDB driver, which finds the primary with its own monitors. The\n * timeline comes from the log of each member and from the lines of the client. The prober watches\n * the namespace of the client.\n */\n\n/**\n * A member line is one log line of a member, as mongod writes it in JSON. `real` is its time on\n * the wall clock in ms, and `t` is its time on the monotonic clock. The run gives `t` from the\n * clock lines of its client.\n */\nexport interface MemberLine extends SentinelEvent {\n readonly ev: \"mongod\";\n readonly member: string;\n readonly real: number;\n readonly id: number;\n readonly c: string;\n readonly msg: string;\n readonly attr: Readonly<Record<string, unknown>>;\n}\n\n/** These are the components of the log of mongod that a run keeps: replication, its heartbeats, the elections, the rollbacks and the commands. */\nexport const KEPT: ReadonlySet<string> = new Set([\"REPL\", \"REPL_HB\", \"ELECTION\", \"ROLLBACK\", \"COMMAND\"]);\n\n/** This function reads one log line of a member, or gives null for a line of another component or a line that is not JSON. */\nexport const memberLineOf = (member: string, text: string): MemberLine | null => {\n if (!text.startsWith(\"{\")) return null;\n let line: { t?: { $date?: string }; c?: string; id?: number; msg?: string; attr?: Record<string, unknown> };\n try {\n line = JSON.parse(text) as typeof line;\n } catch {\n return null;\n }\n const real = Date.parse(line.t?.$date ?? \"\");\n if (!Number.isFinite(real) || !KEPT.has(line.c ?? \"\")) return null;\n return { ev: \"mongod\", t: 0, member, real, id: Number(line.id ?? 0), c: line.c ?? \"\", msg: line.msg ?? \"\", attr: line.attr ?? {} };\n};\n\n/**\n * This function gives the offset of the wall clock from the monotonic clock, in ns, from the clock\n * lines of a client. Each line pairs its stamp with the wall clock in whole ms, which truncates.\n * Thus each pair is the offset or less, and the largest pair is the nearest. It is null without a clock line.\n */\nexport const clockOffsetOf = (events: readonly ProbeEvent[]): number | null => {\n const offsets = events.filter((event) => event.ev === \"clocks\").map((event) => Number(event[\"real\"]) * 1e6 - event.t);\n return offsets.length === 0 ? null : Math.max(...offsets);\n};\n\n/** This function puts the log lines of the members on the monotonic clock, with the offset of a client. */\nexport const onMonotonic = (log: readonly MemberLine[], offset: number | null): MemberLine[] =>\n log.map((line) => ({ ...line, t: offset === null ? 0 : Math.round(line.real * 1e6 - offset) }));\n\n/** A cut is the peers that a network fault on a part cuts it from, by their addresses, and the direction of the rules. */\nexport interface Cut {\n readonly peers: readonly string[];\n readonly direction?: \"out\";\n}\n\n/**\n * This function gives the peers that a fault on a member of a replica set cuts it from. `all` is\n * each other member and the client, `group` is the other members, and `clients` is the client.\n * `one` is the first other member in the sequence of the set, and `one-way` is the same member for\n * the segments from the part only.\n */\nexport const cutOf = (\n set: ReplicaSet,\n members: Readonly<Record<string, RunPart>>,\n client: string,\n part: string,\n cuts: NonNullable<GroupPlace[\"cuts\"]>,\n): Cut => {\n const others = set.members.filter((name) => name !== part).map((name) => members[name]!.ip);\n if (cuts === \"clients\") return { peers: [client] };\n if (cuts === \"group\") return { peers: others };\n if (cuts === \"one\") return { peers: others.slice(0, 1) };\n if (cuts === \"one-way\") return { peers: others.slice(0, 1), direction: \"out\" };\n return { peers: [...others, client] };\n};\n\n/** This function gives the command of the server of each member. The server is the child of a shell, thus a fin can stop it and the container stays up. */\nexport const memberCommand = (set: ReplicaSet): string =>\n `${set.programs.server} --replSet ${set.name} --bind_ip_all --port ${set.port} ${(set.args ?? []).join(\" \")}`.trim();\n\n/** This function gives the script of the shell that starts the set: each member by its address, and the settings of the set. */\nexport const initiateScript = (set: ReplicaSet, ips: Readonly<Record<string, string>>): string => {\n const config = {\n _id: set.name,\n members: set.members.map((name, at) => ({ _id: at, host: `${ips[name]}:${set.port}` })),\n settings: set.settings,\n };\n return `print(JSON.stringify(rs.initiate(${JSON.stringify(config)})))`;\n};\n\n/** This script prints the view of a member: itself, if it accepts writes, and the state of each member as it sees them. */\nexport const STATUS_SCRIPT: string =\n \"const s = rs.status(); const h = db.hello(); print(JSON.stringify({ me: h.me, writable: h.isWritablePrimary === true, \" +\n \"members: s.members.map((m) => ({ name: m.name, state: m.stateStr, health: m.health, optime: m.optimeDate ? m.optimeDate.getTime() : null })) }))\";\n\n/** A status is the view of one member, as `STATUS_SCRIPT` prints it. */\nexport interface Status {\n readonly me: string;\n readonly writable: boolean;\n readonly members: ReadonlyArray<{ readonly name: string; readonly state: string; readonly health: number; readonly optime: number | null }>;\n}\n\n/**\n * This function judges the view of a member: the primary of the set, and if the set is healthy.\n * The set is healthy when the member is the one primary and accepts writes. Each other member must\n * be a healthy secondary at the optime of the primary.\n */\nexport const statusViewOf = (set: ReplicaSet, ips: Readonly<Record<string, string>>, status: Status): GroupView => {\n const byAddress = new Map(set.members.map((name) => [`${ips[name]}:${set.port}`, name]));\n const primaries = status.members.filter((member) => member.state === \"PRIMARY\");\n if (primaries.length !== 1) return { primary: null, healthy: false, why: `the set has ${primaries.length} primaries` };\n const primary = byAddress.get(primaries[0]!.name) ?? null;\n if (primary === null) return { primary: null, healthy: false, why: `no member has the address ${primaries[0]!.name}` };\n if (status.me !== primaries[0]!.name || !status.writable) return { primary, healthy: false, why: `${primary} does not accept writes yet` };\n if (status.members.length !== set.members.length) return { primary, healthy: false, why: `the set has ${status.members.length} members` };\n for (const member of status.members) {\n if (member.health !== 1) return { primary, healthy: false, why: `${member.name} is not healthy` };\n if (member.state !== \"PRIMARY\" && member.state !== \"SECONDARY\") return { primary, healthy: false, why: `${member.name} is ${member.state}` };\n if (member.optime !== primaries[0]!.optime) return { primary, healthy: false, why: `${member.name} is behind the primary` };\n }\n return { primary, healthy: true, why: \"healthy\" };\n};\n\n/** A replica set lab is one environment of a replica set cell. */\nexport interface ReplicaSetLab {\n readonly set: ReplicaSet;\n readonly clientIp: string;\n readonly members: Readonly<Record<string, RunPart>>;\n readonly netns: { readonly client: number };\n /** These are all the records that the prober wrote, in sequence. */\n readonly kernel: readonly KernelRecord[];\n /** These are the log lines of the members that a run keeps, in the sequence of their arrival, with `t` at 0. */\n readonly log: readonly MemberLine[];\n /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */\n sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method waits until the set is whole and calm, and gives the name of its primary. */\n converge(within?: number): Promise<string>;\n /** This method removes each write from the set, and waits until each member has the removal. */\n flush(primary: string): Promise<void>;\n /**\n * This method lets each secondary stand for election again (`rs.freeze(0)`). A step-down holds its\n * old primary out of the elections for `STEP_DOWN.seconds`. Without it, a run inherits that hold.\n */\n unfreeze(primary: string): Promise<void>;\n /** This method gives the sequence numbers that the set holds, from its primary. */\n holds(primary: string): Promise<number[]>;\n /** This method gives the member that has a role in the set, when `primary` is the primary. */\n partOf(role: GroupRole, primary: string): string;\n /** This method starts the client of a run, with the arguments of `client-mongo.mjs`. */\n client(args: readonly string[]): Promise<GroupClient>;\n /** This method applies a network fault between a member and some of its peers, at once or at an instant of the VM. */\n fault(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace[\"cuts\"]>, at?: number): Promise<Stamp>;\n /** This method removes a network fault, at once or at an instant of the VM. */\n lift(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace[\"cuts\"]>, at?: number): Promise<Stamp>;\n /** This method sends a signal to the server process of a member, at once or at an instant of the VM. */\n signal(part: string, signal: \"KILL\" | \"STOP\" | \"CONT\", at?: number): Promise<Stamp>;\n /** This method starts the server of a member again, after a fin. */\n restart(member: string): Promise<void>;\n /** This method sends replSetStepDown to the primary. */\n stepDown(primary: string): Promise<Stamp>;\n /** This method removes the TCP metrics of the client namespace: each run starts from a kernel that keeps nothing. */\n forget(): Promise<void>;\n /** This method waits for the next clock line of the prober, which gives the count of lost records. */\n settle(): Promise<number | null>;\n manifest(sysctls: Readonly<Record<string, string>>): Promise<Manifest>;\n close(): Promise<void>;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** These are the arguments of replSetStepDown: the time that the old primary cannot stand again, and the wait for a secondary to catch up. */\nexport const STEP_DOWN = { seconds: 60, catchUp: 10 } as const;\n\n/**\n * This function opens one environment of a replica set cell:\n * - one new network, with each member, the client and the sender of a step-down on it.\n * - each member: the stock image, its server as the child of a shell. A log consumer reads its\n * JSON log. The first member starts the set with the address of each member, and the lab\n * waits for a primary.\n * - an injector sidecar for each member, in its network and process namespaces.\n * - a client that holds its own network namespace, with a privileged sidecar for its sysctls.\n * - the prober, which watches the namespace of the client.\n */\nexport const openReplicaSetLab = async (set: ReplicaSet): Promise<ReplicaSetLab> => {\n const image = await harnessImage();\n const mongo = await mongoClientImage();\n const { GenericContainer, Network, Wait, getContainerRuntimeClient } = await containers();\n const runtime = await getContainerRuntimeClient();\n const opened: StartedTestContainer[] = [];\n let network: StartedNetwork | undefined;\n const close = async () => {\n for (const container of opened.reverse()) await container.stop(STOP).catch(() => undefined);\n await network?.stop().catch(() => undefined);\n };\n const ipOf = async (container: StartedTestContainer): Promise<string> => {\n const info = await runtime.container.inspect(runtime.container.getById(container.getId()));\n const ip = info.NetworkSettings.Networks[network!.getName()]?.IPAddress;\n if (ip === undefined || ip === \"\") throw new Error(`${container.getName()} has no address on ${network!.getName()}`);\n return ip;\n };\n try {\n network = await new Network().start();\n const boxes = new Map<string, StartedTestContainer>();\n const injectors = new Map<string, StartedTestContainer>();\n const members: Record<string, RunPart> = {};\n const log: MemberLine[] = [];\n for (const name of set.members) {\n let partial = \"\";\n const box = await new GenericContainer(set.image)\n .withNetwork(network)\n .withNetworkAliases(name)\n .withCommand([\"sh\", \"-c\", `${memberCommand(set)} & echo $! > /tmp/server.pid; while :; do sleep 3600; done`])\n .withWaitStrategy(Wait.forLogMessage(set.ready))\n .withLogConsumer((stream) =>\n stream.on(\"data\", (chunk: Buffer | string) => {\n partial += chunk.toString();\n const split = partial.split(\"\\n\");\n partial = split.pop() ?? \"\";\n for (const text of split) {\n const line = memberLineOf(name, text.trim());\n if (line !== null) log.push(line);\n }\n }),\n )\n .start();\n opened.push(box);\n boxes.set(name, box);\n const injector = await withPid(\n new GenericContainer(image).withNetworkMode(`container:${box.getId()}`).withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\"),\n `container:${box.getId()}`,\n ).start();\n opened.push(injector);\n injectors.set(name, injector);\n members[name] = { ip: await ipOf(box), netns: await netnsOf(injector) };\n }\n const ips: Record<string, string> = Object.fromEntries(Object.entries(members).map(([name, at]) => [name, at.ip]));\n\n const holder = await new GenericContainer(image).withNetwork(network).start();\n opened.push(holder);\n const admin = await new GenericContainer(image).withNetworkMode(`container:${holder.getId()}`).withPrivilegedMode().start();\n opened.push(admin);\n const clientIp = await ipOf(holder);\n // the sender of a step-down is a client of its own, on the network but not in the watched namespace\n const commander = await new GenericContainer(mongo)\n .withNetwork(network)\n .withCommand([\"sh\", \"-c\", \"while :; do sleep 3600; done\"])\n .start();\n opened.push(commander);\n\n const clientNetns = await netnsOf(admin);\n const records = lines<KernelRecord>();\n const prober = await new GenericContainer(image)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\"/opt/harness/prober\", String(clientNetns)])\n .withLogConsumer(records.consume)\n .start();\n opened.push(prober);\n const begun = await records.until([\"begin\", \"fail\"], 30_000, \"prober\");\n if (begun.ev === \"fail\") throw new Error(`the prober failed: ${String(begun[\"why\"])}`);\n\n const box = (name: string): StartedTestContainer => {\n const found = boxes.get(name);\n if (found === undefined) throw new Error(`the set has no member ${name}`);\n return found;\n };\n const injectorOf = (name: string): StartedTestContainer => {\n const found = injectors.get(name);\n if (found === undefined) throw new Error(`the set has no member ${name}`);\n return found;\n };\n /** This function starts a script of the shell on a member, against its own server only. */\n const shell = (name: string, script: string): Promise<string> =>\n output(box(name), [\n set.programs.shell,\n \"--quiet\",\n \"--norc\",\n `mongodb://127.0.0.1:${set.port}/?directConnection=true&serverSelectionTimeoutMS=3000`,\n \"--eval\",\n script,\n ]);\n const lastLine = (text: string): string => text.split(\"\\n\").filter((line) => line.trim() !== \"\").at(-1) ?? \"\";\n const stamped = async (where: StartedTestContainer, argv: string[]): Promise<Stamp> => {\n const stamp = JSON.parse(lastLine(await output(where, argv))) as Stamp;\n return { t0: stamp.t0, t1: stamp.t1 };\n };\n const after = (at: number | undefined): string[] => (at === undefined ? [] : [\"--at\", String(Math.round(at))]);\n let known: string = set.members[0]!;\n /** This function gives the view of the set: from the member that was the primary last, then from each other member. */\n const view = async (): Promise<GroupView> => {\n let last: GroupView = { primary: null, healthy: false, why: \"no member answered\" };\n for (const name of [known, ...set.members.filter((each) => each !== known)]) {\n const read = await shell(name, STATUS_SCRIPT)\n .then((text) => statusViewOf(set, ips, JSON.parse(lastLine(text)) as Status))\n .catch((error: unknown) => ({ primary: null, healthy: false, why: `${name}: ${(error as Error).message.slice(0, 200)}` }));\n if (read.primary !== null && read.primary === name) return read;\n last = read;\n }\n return last;\n };\n const converge = async (within = CONVERGE_MS): Promise<string> => {\n const started = Date.now();\n let calm = 0;\n let last: GroupView = { primary: null, healthy: false, why: \"no view yet\" };\n while (Date.now() - started < within) {\n last = await view();\n // two healthy views in a row: one view can come in the middle of a change\n calm = last.healthy ? calm + 1 : 0;\n if (last.primary !== null) known = last.primary;\n if (calm >= 2 && last.primary !== null) return last.primary;\n await sleep(1_000);\n }\n throw new Error(`the set did not converge in ${within} ms: ${last.why}`);\n };\n\n // the first member starts the set, with the address of each member as the client reaches it\n const initiated = await shell(set.members[0]!, initiateScript(set, ips));\n if (!lastLine(initiated).includes('\"ok\":1')) throw new Error(`rs.initiate failed: ${lastLine(initiated)}`);\n await converge();\n\n return {\n set,\n clientIp,\n members,\n netns: { client: clientNetns },\n kernel: records.seen,\n log,\n async sysctl(values) {\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n if (settings.length > 0) await output(admin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n const read: Record<string, string> = {};\n for (const key of Object.keys(values)) read[key] = await output(admin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n converge,\n async flush(primary) {\n const script =\n `const r = db.getSiblingDB(\"cells\").w.deleteMany({}, { writeConcern: { w: ${set.members.length}, wtimeout: 10000 } }); ` +\n \"print(JSON.stringify({ ok: r.acknowledged === true }))\";\n const reply = lastLine(await shell(primary, script));\n if (!reply.includes('\"ok\":true')) throw new Error(`the flush failed: ${reply}`);\n },\n async unfreeze(primary) {\n for (const name of set.members) {\n if (name === primary) continue;\n const reply = lastLine(await shell(name, \"print(JSON.stringify(rs.freeze(0)))\"));\n if (!reply.includes('\"ok\":1')) throw new Error(`${name} did not unfreeze: ${reply}`);\n }\n },\n async holds(primary) {\n const script = 'print(JSON.stringify(db.getSiblingDB(\"cells\").w.find({}, { n: 1, _id: 0 }).toArray().map((d) => d.n)))';\n const held = JSON.parse(lastLine(await shell(primary, script))) as unknown[];\n return held\n .map(Number)\n .filter((n) => Number.isInteger(n) && n > 0)\n .sort((a, b) => a - b);\n },\n partOf(role, primary) {\n if (role === \"primary\") return primary;\n if (role === \"sentinel\") throw new Error(\"a replica set has no Sentinel\");\n const secondary = set.members.find((name) => name !== primary);\n if (secondary === undefined) throw new Error(\"the set has no secondary\");\n return secondary;\n },\n async client(args) {\n const events = lines<ProbeEvent>();\n const container = await new GenericContainer(mongo)\n .withNetworkMode(`container:${holder.getId()}`)\n .withCommand([\"node\", \"/opt/harness/mongo/client-mongo.mjs\", ...args])\n .withLogConsumer(events.consume)\n .start();\n return {\n events: events.seen,\n until: (kinds, within) => events.until(kinds, within, \"client\"),\n // a TERM first: the client writes its done line, then stops\n stop: () =>\n container\n .stop({ timeout: 2 })\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n fault(kind, part, cuts, at) {\n const cut = cutOf(set, members, clientIp, part, cuts);\n const direction = cut.direction === undefined ? [] : [\"--direction\", cut.direction];\n return stamped(injectorOf(part), [\"/opt/harness/inject\", kind, cut.peers.join(\",\"), ...direction, ...after(at)]);\n },\n lift(kind, part, cuts, at) {\n const cut = cutOf(set, members, clientIp, part, cuts);\n const direction = cut.direction === undefined ? [] : [\"--direction\", cut.direction];\n return stamped(injectorOf(part), [\"/opt/harness/inject\", kind === \"rst\" ? \"clear-rst\" : \"clear\", cut.peers.join(\",\"), ...direction, ...after(at)]);\n },\n signal(part, signal, at) {\n return stamped(injectorOf(part), [\"/opt/harness/inject\", \"signal\", set.programs.server, signal, ...after(at)]);\n },\n async restart(member) {\n // the restarted server writes to the output of the container, thus its log consumer reads it\n await output(box(member), [\"sh\", \"-c\", `${memberCommand(set)} > /proc/1/fd/1 2>&1 & echo $! > /tmp/server.pid`]);\n const started = Date.now();\n while (!(await shell(member, \"print(db.hello().ok)\").catch(() => \"\")).includes(\"1\")) {\n if (Date.now() - started > 60_000) throw new Error(`${member} did not start again`);\n await sleep(500);\n }\n },\n stepDown(primary) {\n return stamped(commander, [\n \"node\",\n \"/opt/harness/mongo/stepdown-mongo.mjs\",\n `${members[primary]!.ip}:${set.port}`,\n String(STEP_DOWN.seconds),\n String(STEP_DOWN.catchUp),\n ]);\n },\n async forget() {\n await output(admin, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n },\n async settle() {\n const from = records.seen.length;\n const clock = await new Promise<KernelRecord | null>((resolve) => {\n const started = Date.now();\n const look = () => {\n const line = records.seen.slice(from).find((record) => record.ev === \"clock\");\n if (line !== undefined) resolve(line);\n else if (Date.now() - started > 5_000) resolve(null);\n else setTimeout(look, 50);\n };\n look();\n });\n return clock === null ? null : Number(clock[\"lost\"] ?? 0);\n },\n async manifest(sysctls) {\n const config = await output(admin, [\"zcat\", \"/proc/config.gz\"]).catch(() => \"\");\n const inspect = async (container: StartedTestContainer) => runtime.container.inspect(runtime.container.getById(container.getId()));\n const script = \"print(JSON.stringify({ version: db.version(), settings: rs.conf().settings }))\";\n const list = [];\n for (const name of set.members) {\n const read = JSON.parse(lastLine(await shell(name, script))) as { version: string; settings: Record<string, unknown> };\n const settings: Record<string, string> = { version: read.version };\n for (const key of Object.keys(set.settings)) settings[key] = String(read.settings[key] ?? \"\");\n list.push({ name, ip: members[name]!.ip, id: (await inspect(box(name))).Image, settings });\n }\n return {\n kernel: await output(holder, [\"uname\", \"-r\"]),\n architecture: await output(holder, [\"uname\", \"-m\"]),\n hz: /^CONFIG_HZ=(\\d+)$/m.exec(config)?.[1] ?? null,\n boot_id: await output(holder, [\"cat\", \"/proc/sys/kernel/random/boot_id\"]),\n docker: `${runtime.info.containerRuntime.serverVersion} on ${runtime.info.containerRuntime.operatingSystem}`,\n target: { image: set.image, id: list[0]!.id, settings: list[0]!.settings, sysctls: {} },\n harness: {\n image,\n id: (await inspect(holder)).Image,\n packages: (await output(holder, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort(),\n },\n client: { kind: \"mongodb\", image: mongo },\n netns: { client: clientNetns, target: members[set.members[0]!]!.netns },\n sysctls,\n group: { members: list, sentinels: [], observer: { image: mongo } },\n };\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n};\n\n// ---------------------------------------------------------------------------------------------\n// the runs of a replica set cell\n// ---------------------------------------------------------------------------------------------\n\n/** This count is the number of accepted writes that a run waits for before its fault: the stream is steady then. */\nconst STEADY = 20;\n/** This time is how long a run continues after its outcome, so that the record shows what came next. */\nconst TAIL_MS = 2_000;\n\n/** This function gives the arguments of the client of a replica set cell: each member, and the stream of writes. */\nexport const replicaSetArgs = (cell: Cell, members: Readonly<Record<string, RunPart>>): string[] => {\n const set = cell.replicaSet!;\n const hosts = set.members.map((name) => `${members[name]!.ip}:${set.port}`);\n return [\n members[set.members[0]!]!.ip,\n String(set.port),\n \"--mode\",\n \"stream\",\n \"--hosts\",\n hosts.join(\",\"),\n \"--set\",\n set.name,\n \"--interval-ms\",\n String(cell.probe.interval === undefined ? 50 : valueIn(cell.probe.interval, \"ms\")),\n \"--max-s\",\n String(Math.ceil(deadlineOf(cell) / 1_000 + (cell.hold === undefined ? 0 : valueIn(cell.hold, \"s\")) + 120)),\n \"--w\",\n String(cell.writeConcern ?? \"majority\"),\n ...(cell.driverOptions === undefined ? [] : [\"--options\", JSON.stringify(cell.driverOptions)]),\n ];\n};\n\n/** This function waits until a condition on the lines of the client holds, or for `within` ms. It gives the first line that matches. */\nconst waitFor = (client: GroupClient, match: (event: ProbeEvent) => boolean, within: number): Promise<ProbeEvent | undefined> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const found = client.events.find(match);\n if (found !== undefined || Date.now() - started > within) resolve(found);\n else setTimeout(look, 20);\n };\n look();\n });\n\n/** This function gives the count of lost records in the latest clock line of the prober. */\nconst droppedSoFar = (records: readonly KernelRecord[]): number => {\n for (let at = records.length - 1; at >= 0; at -= 1) if (records[at]!.ev === \"clock\") return Number(records[at]![\"lost\"] ?? 0);\n return 0;\n};\n\n/**\n * This function does one run of a replica set cell:\n * - The set converges, and its writes go. The client finds the primary with its monitors, and its\n * stream of writes gets steady.\n * - The fault occurs on its member, as the primary of this run makes the roles. A fault with a\n * hold lifts at its instant in the VM. A stall lifts at its length after the first write into\n * it. A step-down is replSetStepDown to the primary.\n * - The run waits for its outcome: the first accepted write of a write sent after the commit of\n * the fault. It is on a new primary, or on any primary. Then the run waits for the end of the\n * hold, thus the record shows what the set did in the whole hold.\n * - Each fault goes, the client stops, and the set converges again. The writes that it then holds\n * tell which writes it kept. The log lines of the members go onto the monotonic clock.\n */\nexport const replicaSetOnce = async (lab: ReplicaSetLab, cell: Cell, environment: number, index: number): Promise<Run> => {\n const set = cell.replicaSet!;\n const primary = await lab.converge();\n await lab.flush(primary);\n // each secondary can win an election: no hold of a step-down in an earlier run is left\n await lab.unfreeze(primary);\n await lab.forget();\n const from = lab.kernel.length;\n const logFrom = lab.log.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const client = await lab.client(replicaSetArgs(cell, lab.members));\n const lands = cell.prediction.group?.lands ?? \"new\";\n const place = (cell.faultAt ?? { role: \"primary\" }) as GroupPlace;\n let fault: Run[\"fault\"] = null;\n let heal: Run[\"heal\"] = null;\n let at: string | null = null;\n const undo: Array<() => Promise<unknown>> = [];\n try {\n await client.until([\"ready\"], 60_000);\n const steady = await waitFor(client, (event) => event.ev === \"ack\" && Number(event[\"n\"]) >= STEADY, 30_000);\n if (steady === undefined) throw new Error(`the client did not write ${STEADY} times in 30 s`);\n let healing: Promise<Run[\"heal\"]> | null = null;\n switch (cell.fault) {\n case \"blackhole\":\n case \"partition\":\n case \"rst\": {\n const kind = cell.fault === \"rst\" ? \"rst\" : \"blackhole\";\n const cuts = place.cuts ?? (cell.fault === \"partition\" ? \"group\" : \"all\");\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.fault(kind, part, cuts);\n undo.push(() => lab.lift(kind, part, cuts));\n if (cell.stall !== undefined) {\n // the first write into the fault arms the retransmissions: the stall counts from it. A\n // write that was in flight at the commit holds the stream, and then the stall counts from\n // the commit. The measurement then voids the run\n const t1 = fault.t1;\n const sent = await waitFor(client, (event) => event.ev === \"sent\" && event.t > t1, 5_000);\n healing = lab.lift(kind, part, cuts, (sent?.t ?? t1) + valueIn(cell.stall, \"s\") * 1e9);\n } else if (cell.hold !== undefined) {\n healing = lab.lift(kind, part, cuts, fault.t0 + valueIn(cell.hold, \"s\") * 1e9);\n }\n break;\n }\n case \"freeze-process\": {\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.signal(part, \"STOP\");\n undo.push(() => lab.signal(part, \"CONT\"));\n if (cell.hold !== undefined) healing = lab.signal(part, \"CONT\", fault.t0 + valueIn(cell.hold, \"s\") * 1e9);\n break;\n }\n case \"fin\": {\n const part = lab.partOf(place.role, primary);\n at = part;\n fault = await lab.signal(part, \"KILL\");\n undo.push(() => lab.restart(part));\n break;\n }\n case \"step-down\":\n at = primary;\n fault = await lab.stepDown(primary);\n break;\n default:\n throw new Error(`a replica set cell has no fault ${cell.fault}`);\n }\n // the injector holds the lift in the VM: its exec returns at the instant, and the stamps come with it\n const healed =\n healing === null\n ? Promise.resolve(null)\n : healing.then((stamp) => {\n heal = stamp;\n // the lift came: nothing is left to undo\n undo.length = 0;\n return stamp;\n });\n // a run that stops early lifts the fault itself, and the late lift then has nothing to remove\n healed.catch(() => undefined);\n const t1 = fault.t1;\n const old = `${lab.members[primary]!.ip}:${set.port}`;\n const sentAt = (n: number): number => client.events.find((event) => event.ev === \"sent\" && Number(event[\"n\"]) === n)?.t ?? -Infinity;\n const outcome = await waitFor(\n client,\n (event) => event.ev === \"ack\" && sentAt(Number(event[\"n\"])) > t1 && (lands === \"same\" || (typeof event[\"at\"] === \"string\" && event[\"at\"] !== old)),\n deadlineOf(cell),\n );\n if (outcome !== undefined) await sleep(TAIL_MS);\n // the whole hold: a prediction that no failover occurs reads the set in each moment of it\n if (healing !== null) {\n await healed.catch(() => undefined);\n await sleep(TAIL_MS);\n }\n } finally {\n // each fault that is still on goes, and a killed server starts again\n for (const step of undo.reverse()) await step().catch(() => undefined);\n await client.stop();\n }\n const settled = await lab.settle();\n const lost = settled === null ? null : settled - lostBefore;\n const after = await lab.converge().catch(() => null);\n const holds = after === null ? null : await lab.holds(after);\n return {\n environment,\n index,\n events: [...client.events],\n fault,\n ...(heal === null ? {} : { heal }),\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.members[primary]!.netns,\n lost,\n group: {\n members: lab.members,\n sentinels: {},\n primary,\n at,\n timeline: onMonotonic(lab.log.slice(logFrom), clockOffsetOf(client.events)),\n after,\n holds,\n },\n };\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, FaultPlace, KernelRecord, ProbeEvent, Run } from \"./cell.ts\";\nimport { isHopPlace } from \"./cell.ts\";\nimport { longestOf, settled as driverSettled } from \"./driver.ts\";\nimport { groupOnce, openGroupLab } from \"./group.ts\";\nimport type { FaultOptions, Lab, Manifest, Probe } from \"./lab.ts\";\nimport { openLab } from \"./lab.ts\";\nimport type { Measured } from \"./observe.ts\";\nimport { measure, socketOf } from \"./observe.ts\";\nimport { openReplicaSetLab, replicaSetOnce } from \"./replset.ts\";\nimport type { Verdict } from \"./verdict.ts\";\nimport { STEPS, noiseOf, verdictOf } from \"./verdict.ts\";\n\n/** A cell result is a cell with its manifests, its runs, their measurements and its verdict. */\nexport interface CellResult {\n readonly cell: Cell;\n readonly manifests: readonly Manifest[];\n readonly runs: readonly Run[];\n /** These are the warm-up runs, one for each environment before its counted runs. The verdict does not count them. */\n readonly warmups: readonly Run[];\n readonly measured: readonly Measured[];\n readonly verdict: Verdict;\n /** These times are when its first environment opened and its last environment closed, on the wall clock. */\n readonly at: { readonly started: string; readonly ended: string };\n}\n\n/**\n * This function measures and judges the raw records of a cell, with the code of the harness at\n * this time. `runCell` uses it during a cell. A holder of the raw records of a cell can use it\n * again later. Thus the records get a new analysis after a change to observe.ts or to the verdict.\n */\nexport const judge = (\n cell: Cell,\n runs: readonly Run[],\n): { measured: Measured[]; verdict: Verdict } => {\n // a warm-up run is a record, not a sample: the verdict never counts it\n const measured = runs.filter((run) => run.warmup !== true).map((run) => measure(cell, run));\n return { measured, verdict: verdictOf(cell.prediction, measured, noiseOf(cell)) };\n};\n\n/** This function gives the count of lost records in the latest clock line of the prober, since it began. */\nconst droppedSoFar = (records: readonly KernelRecord[]): number => {\n for (let at = records.length - 1; at >= 0; at -= 1)\n if (records[at]!.ev === \"clock\") return Number(records[at]![\"lost\"] ?? 0);\n return 0;\n};\n\n/** This function gives the argv of the client for a cell, with a deadline far after all that it waits for. */\nconst probeArgs = (cell: Cell, lab: Lab): string[] => {\n const { prediction, probe } = cell;\n const longest =\n (prediction.detects === true && prediction.driver !== undefined\n ? longestOf(cell)\n : prediction.detects\n ? valueIn(prediction.high, \"s\")\n : valueIn(prediction.window, \"s\")) + (cell.idleBefore === undefined ? 0 : valueIn(cell.idleBefore, \"s\"));\n const args = [lab.targetIp, String(lab.targetPort), \"--mode\", probe.mode];\n if (probe.ws !== undefined) {\n // a WebSocket client gets each of its settings as a flag, and it has no default\n const { ws } = probe;\n const reconnects = prediction.detects === true ? (prediction.reconnects?.waits ?? []) : [];\n const waits = reconnects.reduce((sum, wait) => sum + valueIn(wait.high, \"s\"), 0);\n args.push(\"--websocket\", ws.socket);\n args.push(\"--heartbeat\", ws.heartbeat === null ? \"off\" : \"on\");\n if (ws.heartbeat !== null)\n args.push(\n \"--heartbeat-interval-ms\",\n String(valueIn(ws.heartbeat.interval, \"ms\")),\n \"--heartbeat-timeout-ms\",\n String(valueIn(ws.heartbeat.timeout, \"ms\")),\n \"--heartbeat-misses\",\n String(ws.heartbeat.misses),\n );\n args.push(\"--reconnect\", ws.reconnect === null ? \"off\" : \"on\");\n if (ws.reconnect !== null)\n args.push(\n \"--reconnect-initial-ms\",\n String(valueIn(ws.reconnect.initialDelay, \"ms\")),\n \"--reconnect-multiplier\",\n String(ws.reconnect.multiplier),\n \"--reconnect-max-ms\",\n String(valueIn(ws.reconnect.maxDelay, \"ms\")),\n \"--reconnect-attempts\",\n String(ws.reconnect.attempts),\n \"--reconnect-jitter\",\n String(ws.reconnect.jitter),\n );\n args.push(\"--request-timeout-ms\", String(valueIn(ws.requestTimeout, \"ms\")));\n args.push(\"--connection-timeout-ms\", String(valueIn(ws.connectionTimeout, \"ms\")));\n if (ws.hold !== undefined) args.push(\"--hold-ms\", String(valueIn(ws.hold, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil((longest + waits) * 2 + 10)));\n return args;\n }\n if (probe.http !== undefined) {\n // an HTTP client sets its socket options from its own settings, and sends its own requests\n const { http } = probe;\n const gap = http.gap === undefined ? 0 : valueIn(http.gap, \"s\");\n args.push(\"--method\", http.method);\n if (http.gap !== undefined) args.push(\"--gap-ms\", String(valueIn(http.gap, \"ms\")));\n if (http.hold !== undefined) args.push(\"--hold-ms\", String(valueIn(http.hold, \"ms\")));\n if (http.bytes !== undefined) args.push(\"--bytes\", String(http.bytes));\n for (const [name, value] of Object.entries(http.options ?? {})) args.push(`--${name}`, String(value));\n // the client waits after its second request, thus its own close comes after the window\n args.push(\"--linger-ms\", String(LINGER_MS));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + gap + 10)));\n return args;\n }\n if (probe.request !== undefined) args.push(\"--request\", probe.request);\n if (probe.count !== undefined) args.push(\"--count\", String(probe.count));\n if (probe.interval !== undefined) args.push(\"--interval-ms\", String(valueIn(probe.interval, \"ms\")));\n if (probe.driver?.name === \"mongodb\") {\n // the MongoDB driver: its replica set, its operation, its write concern and its own options\n args.push(\"--set\", probe.driver.set);\n if (probe.driver.op !== undefined) args.push(\"--op\", probe.driver.op);\n if (cell.writeConcern !== undefined) args.push(\"--w\", String(cell.writeConcern));\n if (cell.before === true) args.push(\"--before\");\n if (cell.driverOptions !== undefined) args.push(\"--options\", JSON.stringify(cell.driverOptions));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n }\n if (probe.driver !== undefined) {\n // a driver sets its socket options from its own options: the keepAlive of ioredis\n args.push(\"--keepalive-ms\", String(valueIn(probe.driver.keepAlive, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n }\n if (probe.keepalive) args.push(\"--keepalive\");\n if (probe.userTimeout !== undefined)\n args.push(\"--user-timeout-ms\", String(valueIn(probe.userTimeout, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n};\n\n/**\n * This function waits for an error on the socket of the observer, or for its end of file\n * (CLOSE_WAIT, state 8). The prober records both. A cell whose observer is the server, or a proxy\n * of a topology, waits for that. A target with its own detector can also drop the connection\n * itself (`dropped`). The promise resolves after `within` ms at the latest.\n */\nconst observerError = (\n cell: Cell,\n partial: () => Run,\n within: number,\n dropped: () => boolean = () => false,\n): Promise<void> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const run = partial();\n const at = socketOf(cell, run);\n const seen =\n at !== undefined &&\n run.kernel.some(\n (record) =>\n record.netns === at.netns &&\n record.cookie === at.cookie &&\n ((record.ev === \"error\" && (record.err ?? 0) !== 0) ||\n (record.ev === \"state\" && (record as { readonly new?: number }).new === 8)),\n );\n if (seen || dropped() || Date.now() - started > within) resolve();\n else setTimeout(look, 50);\n };\n look();\n });\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * This time is the pause between the client's first reply and the fault. It is more than Linux's\n * TCP_DELACK_MAX of 200 ms. Thus the delayed ACK of the reply is out before the fault.\n */\nexport const QUIET_MS = 250;\n\n/**\n * This time is how long an HTTP client waits after its second request, before it exits. The exit\n * closes its sockets, thus that close comes after the window of the cell.\n */\nexport const LINGER_MS = 1000;\n\n/** These are the lines that end the workload of an HTTP client: the end of its second request, its deadline, or its failure. */\nconst HTTP_DONE = [\"done\", \"deadline\", \"fail\"];\n\n/**\n * This function waits for the request that an in-flight client sends on the signal: its second\n * send, after the PING that made it ready. Its stamp is when the request first met the fault.\n */\nconst requestSent = (probe: Probe, within: number): Promise<ProbeEvent> =>\n new Promise((resolve, reject) => {\n const started = Date.now();\n const look = () => {\n const sent = probe.events.filter((event) => event.ev === \"sent\")[1];\n if (sent !== undefined) resolve(sent);\n else if (Date.now() - started > within)\n reject(new Error(`the client sent no request within ${within} ms of being told to`));\n else setTimeout(look, 1);\n };\n look();\n });\n\n/** This time is how long a run of a driver cell continues after its outcome, so that the record shows what came next. */\nconst TAIL_MS = 1_000;\n\n/** This function waits for the outcome of a run of a driver cell, or for `within` ms. The outcome is the end of its band and of each later band. */\nconst driverOutcome = async (cell: Cell, current: () => Run, within: number): Promise<void> => {\n const started = Date.now();\n while (Date.now() - started < within) {\n if (driverSettled(cell, current())) return;\n await sleep(250);\n }\n};\n\n/** This function gives the longest wait of a run of a driver cell for its outcome, in ms. */\nconst driverWithin = (cell: Cell): number => (longestOf(cell) * 1.2 + 10) * 1_000;\n\n/**\n * This function does one run of a cell whose fault holds before the client starts (`Cell.before`).\n * The fault is a network fault at its place. The client then starts into it, and the run waits for\n * each band of the driver. Then the fault lifts.\n */\nconst beforeOnce = async (lab: Lab, cell: Cell, environment: number, index: number): Promise<Run> => {\n if (cell.fault !== \"rst\" && cell.fault !== \"blackhole\" && cell.fault !== \"unreachable\")\n throw new Error(`${cell.name}: a fault before the client starts is a network fault, not ${cell.fault}`);\n await lab.forget();\n const from = lab.kernel.length;\n const middleboxFrom = lab.middlebox.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const faultAt: FaultPlace = cell.faultAt ?? \"target\";\n const kind = cell.fault === \"unreachable\" ? \"reject\" : cell.fault;\n const options: FaultOptions = cell.fault === \"unreachable\" ? { code: cell.icmp ?? \"host\" } : {};\n const fault = await lab.fault(kind, faultAt, options);\n let probe: Probe | undefined;\n try {\n probe = await lab.probe(probeArgs(cell, lab));\n const started = probe;\n const current = (): Run => ({\n environment,\n index,\n events: started.events,\n fault,\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost: 0,\n });\n await driverOutcome(cell, current, driverWithin(cell));\n await sleep(TAIL_MS);\n } finally {\n await lab.lift(kind, faultAt, options);\n await probe?.stop();\n }\n const settled = await lab.settle();\n return {\n environment,\n index,\n events: [...(probe?.events ?? [])],\n fault,\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost: settled === null ? null : settled - lostBefore,\n ...(lab.topology === null ? {} : { topology: lab.topology, middlebox: lab.middlebox.slice(middleboxFrom) }),\n };\n};\n/** This function waits for the first ping of the heartbeat of the client at the instant `after` or later: its first ping into a stall. */\nconst pingSent = (probe: Probe, after: number, within: number): Promise<ProbeEvent> =>\n new Promise((resolve, reject) => {\n const started = Date.now();\n const look = () => {\n const ping = probe.events.find((event) => event.ev === \"ping\" && event.t >= after);\n if (ping !== undefined) resolve(ping);\n else if (Date.now() - started > within) reject(new Error(`the client sent no ping into the stall within ${within} ms`));\n else setTimeout(look, 1);\n };\n look();\n });\n\n/**\n * This function waits until the client wrote `count` lines `dial`, one before each connection\n * that it tries, or until its deadline. It resolves after `within` ms at the latest.\n */\nconst dials = (probe: Probe, count: number, within: number): Promise<void> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const tried = probe.events.filter((event) => event.ev === \"dial\").length;\n const ended = probe.events.some((event) => event.ev === \"deadline\");\n if (tried >= count || ended || Date.now() - started > within) resolve();\n else setTimeout(look, 20);\n };\n look();\n });\n\n/** This function gives the netem specification of a cell: its latency, then the netem of its fault when it has one. */\nconst netemSpec = (cell: Cell, fault: string | undefined): string | null => {\n const parts = [\n ...(cell.latency === undefined ? [] : [`delay ${valueIn(cell.latency, \"ms\")}ms`]),\n ...(fault === undefined ? [] : [fault]),\n ];\n return parts.length === 0 ? null : parts.join(\" \");\n};\n\n/**\n * This function does one run. The client connects and gets its reply, the fault occurs, and an\n * in-flight client sends into it. The harness then waits for the observer to see the fault, or for\n * the silent window to end. The observer is the client, or the socket of the target for a cell on\n * the server side. It can also be a socket of a proxy of a topology.\n *\n * A stall stops by itself, at its length after the send of the request, and the\n * run waits for the reply. Then the harness removes the fault, thus the environment can serve the\n * next run. The run keeps the records of the prober from its start to its end. The frozen process\n * is the peer of the observer: the target, or the client for the server.\n *\n * A dry run does all of this but the fault: the warm-up of a cell whose fault ends its environment.\n */\nconst once = async (\n lab: Lab,\n cell: Cell,\n environment: number,\n index: number,\n dry = false,\n): Promise<Run> => {\n if (cell.before === true) return beforeOnce(lab, cell, environment, index);\n // each run starts from kernels that keep nothing from the runs before it\n await lab.forget();\n const from = lab.kernel.length;\n const middleboxFrom = lab.middlebox.length;\n const serverFrom = lab.server.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const probe = await lab.probe(probeArgs(cell, lab));\n const server = cell.observer === \"server\";\n const peer = server\n ? { freeze: () => probe.freeze(), thaw: () => probe.thaw() }\n : { freeze: () => lab.freeze(), thaw: () => lab.thaw() };\n // a client that stopped before the signal to send tells why in its last lines\n const go = () =>\n probe.go().catch((error: unknown) => {\n const last = probe.events.slice(-3).map((event) => JSON.stringify(event));\n throw new Error(`${(error as Error).message}; the client's last lines: ${last.join(\" \")}`);\n });\n const placed = lab.topology === null ? {} : { topology: lab.topology };\n /** This function gives the run as the records show it so far: the waits read it. */\n const partial = (): Run => ({\n environment,\n index,\n events: probe.events,\n fault: null,\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost: 0,\n ...placed,\n });\n let fault: Run[\"fault\"] = null;\n let heal: { t0: number; t1: number } | null = null;\n // a target with a named process freezes by SIGSTOP, with stamps, and a hold thaws it at its instant\n const signalled = !server && cell.target.program !== undefined;\n let thawing: Promise<{ t0: number; t1: number }> | null = null;\n try {\n await probe.until([\"ready\"], 15_000);\n if (dry) {\n if (cell.probe.mode !== \"idle\") await go();\n await sleep(500);\n } else {\n // the in-flight data of a run on the server side is the reply of the server. The request goes\n // out first, and the fault occurs while the server holds it (the timeout of BLPOP). A fin with\n // a request in flight kills the server while it holds the request, so it goes first too\n const replyInFlight =\n cell.probe.mode === \"inflight\" &&\n ((server && cell.fault !== \"mtu-blackhole\") || cell.fault === \"fin\");\n if (replyInFlight) {\n // the client sends one sample after the file appears. The request reaches the server well\n // in the pause, and the reply waits for the timeout of the request\n await go();\n await sleep(200);\n } else {\n // The client acknowledges its first reply with a delayed ACK, 40 to 200 ms later\n // (TCP_DELACK_MIN to TCP_DELACK_MAX). A fault that comes first meets that ACK, not the\n // request or the idle time that the cell measures. So the fault waits until the ACK is out.\n await sleep(QUIET_MS);\n }\n const faultAt: FaultPlace = cell.faultAt ?? (server ? \"client\" : \"target\");\n // the direction of a simplex drop, relative to the namespace of the rule: in the target, or at\n // a proxy toward the client, the segments to the server come in; in the client, they go out\n const towardServerComesIn =\n faultAt === \"target\" || (isHopPlace(faultAt) && faultAt.toward === \"client\");\n const direction: FaultOptions[\"direction\"] =\n cell.drops === undefined\n ? undefined\n : (cell.drops === \"to-server\") === towardServerComesIn\n ? \"in\"\n : \"out\";\n const options: FaultOptions = {\n ...(direction === undefined ? {} : { direction }),\n ...(cell.icmp === undefined ? {} : { code: cell.icmp }),\n ...(cell.mtu === undefined ? {} : { above: cell.mtu }),\n };\n // a toxic of a cell whose fault is another one goes first: a slow close, then the close\n const toxicFirst = cell.toxic !== undefined && cell.fault !== \"toxic\";\n if (toxicFirst) await lab.toxic(cell.toxic!);\n // netem at the target shares its qdisc with the latency of the cell. At a hop, it is alone\n const netemAt: FaultPlace = cell.faultAt ?? \"target\";\n const netemOn = (fault: string | undefined) =>\n netemAt === \"target\" ? lab.netem(netemSpec(cell, fault), \"target\") : lab.netem(fault ?? null, netemAt);\n // an MTU drop goes where the long reply arrives: the namespace of the client\n const apply = async (): Promise<Run[\"fault\"]> => {\n switch (cell.fault) {\n case \"freeze-process\": {\n if (signalled) {\n const stamp = await lab.signal(\"STOP\");\n if (cell.hold !== undefined) {\n thawing = lab.signal(\"CONT\", stamp.t0 + valueIn(cell.hold, \"s\") * 1e9).then((thawed) => {\n heal = thawed;\n return thawed;\n });\n thawing.catch(() => undefined);\n }\n return stamp;\n }\n await peer.freeze();\n return null;\n }\n case \"fin\":\n await (server ? probe.kill() : lab.kill());\n return null;\n case \"unreachable\":\n return lab.fault(\"reject\", faultAt, { code: cell.icmp ?? \"host\" });\n case \"simplex\":\n return lab.fault(\"blackhole\", faultAt, options);\n case \"mtu-blackhole\":\n return lab.fault(\"mtu\", cell.faultAt ?? \"client\", { above: cell.mtu ?? 1200 });\n case \"netem\":\n return netemOn(cell.netem);\n case \"rst\":\n return lab.fault(\"rst\", faultAt);\n // the router or the proxy of the topology is the fault, or a calibration has none, or the\n // server holds the reply by itself: nothing to inject\n case \"middlebox-forget\":\n case \"proxy-idle\":\n case \"app-stall\":\n case \"none\":\n return null;\n case \"toxic\":\n if (cell.toxic === undefined) throw new Error(`${cell.name} names no toxic`);\n return lab.toxic(cell.toxic);\n case \"server-close\":\n await lab.serverClose();\n return null;\n default:\n // a stall is a black hole that stops by itself\n return lab.fault(\"blackhole\", faultAt);\n }\n };\n const lift = async (): Promise<void> => {\n switch (cell.fault) {\n case \"freeze-process\":\n if (signalled) {\n if (thawing !== null) await thawing.catch(() => lab.signal(\"CONT\"));\n else await lab.signal(\"CONT\");\n return undefined;\n }\n return peer.thaw();\n case \"fin\":\n case \"middlebox-forget\":\n case \"proxy-idle\":\n case \"server-close\":\n case \"app-stall\":\n case \"none\":\n return undefined;\n case \"unreachable\":\n return lab.lift(\"reject\", faultAt, { code: cell.icmp ?? \"host\" });\n case \"simplex\":\n return lab.lift(\"blackhole\", faultAt, options);\n case \"mtu-blackhole\":\n return lab.lift(\"mtu\", cell.faultAt ?? \"client\", { above: cell.mtu ?? 1200 });\n case \"netem\":\n await netemOn(undefined);\n return undefined;\n case \"rst\":\n return lab.lift(\"rst\", faultAt);\n case \"toxic\":\n await lab.toxic(null);\n return undefined;\n default:\n return heal === null ? lab.lift(\"blackhole\", faultAt) : undefined;\n }\n };\n fault = await apply();\n try {\n // an idle time before the request: a router forgets the flow in it, or a keepalive keeps it\n if (cell.idleBefore !== undefined && cell.probe.mode !== \"idle\")\n await sleep(valueIn(cell.idleBefore, \"ms\"));\n // a client in a gap sends its second request by itself, a gap after its first reply\n if ((cell.probe.mode === \"inflight\" || cell.probe.mode === \"stream\") && !replyInFlight) await go();\n const { prediction } = cell;\n const http = cell.probe.http !== undefined;\n if (prediction.detects !== null && prediction.driver !== undefined) {\n // a driver cell: each of its bands ends, or its silent window passes from the fault\n if (prediction.detects) {\n await driverOutcome(cell, () => ({ ...partial(), fault }), driverWithin(cell));\n } else {\n await sleep(valueIn(prediction.window, \"s\") * 1_000);\n }\n await sleep(TAIL_MS);\n } else if (cell.probe.mode === \"gap\") {\n // the run ends when the second request does: its outcome is a part of the record\n const gap = cell.probe.http?.gap === undefined ? 0 : valueIn(cell.probe.http.gap, \"s\");\n const window = valueIn(prediction.detects ? prediction.high : prediction.window, \"s\");\n await probe.until(HTTP_DONE, (gap + window) * 2_000 + 10_000).catch(() => undefined);\n } else if (cell.fault === \"stall\") {\n if (cell.stall === undefined) throw new Error(`${cell.name} says no stall's length`);\n // the injector holds the end in the VM until the length of the stall after the send. An idle\n // client sends only the pings of its heartbeat: the stall counts from its first ping into it\n const window = valueIn(prediction.detects ? prediction.high : prediction.window, \"s\");\n const sent =\n cell.probe.mode === \"idle\"\n ? await pingSent(probe, fault?.t1 ?? 0, window * 1_000)\n : await requestSent(probe, 5_000);\n heal = await lab.heal(faultAt, sent.t + valueIn(cell.stall, \"s\") * 1e9);\n await probe.until([\"data\", \"error\", \"eof\", \"drop\"], window * 1_000).catch(() => undefined);\n } else if (prediction.detects === null) {\n // a distribution: the stream ends by itself, or the connection does\n const window = valueIn(prediction.window, \"s\") * 1_000;\n await probe.until([\"done\", \"error\", \"eof\", \"deadline\"], window).catch(() => undefined);\n } else if (prediction.detects) {\n const within = valueIn(prediction.high, \"s\") * 2_000 + 10_000;\n if (cell.observer !== undefined && cell.observer !== \"client\") {\n // a target with its own detector writes a drop line when it drops the connection itself\n await observerError(cell, partial, within, () => lab.server.slice(serverFrom).some((line) => line.ev === \"drop\"));\n } else if (http && cell.probe.mode !== \"idle\") {\n // an HTTP client can send the request again on a new connection: its outcome ends the run\n await probe.until(HTTP_DONE, within).catch(() => undefined);\n } else {\n // an HTTP client writes `closed` when its pool closes an idle connection by itself, and\n // a program with its own detector writes `drop` when it drops the connection\n await probe.until([\"error\", \"eof\", \"deadline\", \"drop\", ...(http ? [\"closed\"] : [])], within).catch(() => undefined);\n }\n } else {\n await sleep(valueIn(prediction.window, \"s\") * 1_000);\n }\n } finally {\n await lift();\n if (toxicFirst) await lab.toxic(null);\n }\n // a thawed target answers the request that it held: the client sees the late reply. An idle\n // client sent no request\n if (cell.fault === \"freeze-process\" && !server && cell.probe.mode !== \"idle\")\n await probe.until([\"data\"], 5_000).catch(() => undefined);\n // the client tries its new connections after the fault lifted: one `dial` line for each try\n const expected = cell.prediction.detects === true ? cell.prediction.reconnects : undefined;\n if (expected !== undefined) {\n const waits = expected.waits.reduce((sum, wait) => sum + valueIn(wait.high, \"s\"), 0);\n await dials(probe, 1 + expected.waits.length, waits * 2_000 + 5_000);\n }\n }\n } finally {\n await probe.stop();\n }\n // the prober drains its ring each 20 ms. Its next clock line closes the records of the run\n const settled = await lab.settle();\n const lost = settled === null ? null : settled - lostBefore;\n return {\n environment,\n index,\n events: [...probe.events],\n fault,\n ...(cell.fault === \"stall\" || thawing !== null ? { heal } : {}),\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost,\n ...placed,\n ...(lab.topology === null ? {} : { middlebox: lab.middlebox.slice(middleboxFrom) }),\n ...(cell.target.lines === true ? { server: lab.server.slice(serverFrom) } : {}),\n };\n};\n\n/** This function divides a count of runs across environments, as equally as possible. */\nexport const spread = (runs: number, environments: number): number[] =>\n Array.from(\n { length: environments },\n (_, at) => Math.floor(runs / environments) + (at < runs % environments ? 1 : 0),\n ).filter((count) => count > 0);\n\n/** This function tells if the fault of a cell ends its environment: a server that stopped cannot serve the next run. */\nconst endsEnvironment = (cell: Cell): boolean =>\n cell.fault === \"fin\" && (cell.observer === undefined || cell.observer === \"client\");\n\n/** This function says one line for a run. */\nconst line = (cell: Cell, run: Run, read: Measured): string =>\n `${cell.name}: env ${run.environment} ${run.warmup === true ? \"warm-up\" : `run ${run.index}`} ${read.outcome.kind}` +\n `${read.elapsed === null ? \"\" : ` ${read.elapsed.value} s`}` +\n `${read.void === null ? \"\" : ` (void: ${read.void})`}`;\n\n/**\n * This function does the part of the runs of a cell in one environment, and records its manifest.\n * A warm-up goes first, and the verdict does not count it. On a loaded machine, a new environment is\n * sometimes slow at first: its first exec, its first client, its first fault. The warm-up\n * of a cell whose fault ends the environment is a dry run, without the fault.\n */\nconst inEnvironment = async (\n cell: Cell,\n environment: number,\n count: number,\n say: (line: string) => void,\n): Promise<{ manifest: Manifest; runs: Run[]; warmup: Run }> => {\n if (cell.group !== undefined) return inGroupEnvironment(cell, environment, count, say);\n if (cell.replicaSet !== undefined) return inReplicaSetEnvironment(cell, environment, count, say);\n const lab = await openLab(cell.target, cell.client ?? cell.probe.driver?.name ?? \"tcp\", cell.topology);\n try {\n // the settings of the target first: Redis applies tcp-keepalive to a client when it accepts it\n const settings = await lab.configure(cell.targetSettings ?? {});\n const targetSysctls = await lab.targetSysctl(cell.targetSysctls ?? {});\n if (cell.targetSetup !== undefined) await lab.setup(cell.targetSetup);\n // netem and a length match act on segments only when TSO, GSO and GRO are off on the two ends,\n // and on the hop that the fault is at\n if (cell.fault === \"mtu-blackhole\" || cell.fault === \"netem\") {\n await lab.offload(false, \"target\");\n await lab.offload(false, \"client\");\n if (isHopPlace(cell.faultAt)) await lab.offload(false, cell.faultAt);\n }\n // a latency is on the path before the client connects, thus its round trip and RTO include it\n const latency = netemSpec(cell, undefined);\n if (latency !== null) await lab.netem(latency, \"target\");\n // the first connection must not wait for ARP: under a delay, that is a longer first round trip\n await lab.warm();\n const manifest = await lab.manifest(await lab.sysctl(cell.sysctls), settings, targetSysctls);\n const warmed = await once(lab, cell, environment, -1, endsEnvironment(cell));\n const warmup: Run = { ...warmed, warmup: true };\n say(line(cell, warmup, measure(cell, warmup)));\n const runs: Run[] = [];\n for (let index = 0; index < count; index += 1) {\n const run = await once(lab, cell, environment, index);\n runs.push(run);\n say(line(cell, run, measure(cell, run)));\n }\n return { manifest, runs, warmup };\n } finally {\n await lab.close();\n }\n};\n\n/**\n * This function does the part of the runs of a group cell in one environment. The warm-up is a\n * whole run with its fault, because no fault of a group ends its environment. A server that a\n * fin stopped starts again, and the group converges before each run.\n */\nconst inGroupEnvironment = async (\n cell: Cell,\n environment: number,\n count: number,\n say: (line: string) => void,\n): Promise<{ manifest: Manifest; runs: Run[]; warmup: Run }> => {\n const lab = await openGroupLab(cell.group!, { watchSentinels: cell.calibrates === \"sentinels\" });\n try {\n const manifest = await lab.manifest(await lab.sysctl(cell.sysctls));\n const warmed = await groupOnce(lab, cell, environment, -1);\n const warmup: Run = { ...warmed, warmup: true };\n say(line(cell, warmup, measure(cell, warmup)));\n const runs: Run[] = [];\n for (let index = 0; index < count; index += 1) {\n const run = await groupOnce(lab, cell, environment, index);\n runs.push(run);\n say(line(cell, run, measure(cell, run)));\n }\n return { manifest, runs, warmup };\n } finally {\n await lab.close();\n }\n};\n\n/**\n * This function does the part of the runs of a replica set cell in one environment. The warm-up\n * is a whole run with its fault, as for a group under Sentinels. The set converges before each run.\n */\nconst inReplicaSetEnvironment = async (\n cell: Cell,\n environment: number,\n count: number,\n say: (line: string) => void,\n): Promise<{ manifest: Manifest; runs: Run[]; warmup: Run }> => {\n const lab = await openReplicaSetLab(cell.replicaSet!);\n try {\n const manifest = await lab.manifest(await lab.sysctl(cell.sysctls));\n const warmed = await replicaSetOnce(lab, cell, environment, -1);\n const warmup: Run = { ...warmed, warmup: true };\n say(line(cell, warmup, measure(cell, warmup)));\n const runs: Run[] = [];\n for (let index = 0; index < count; index += 1) {\n const run = await replicaSetOnce(lab, cell, environment, index);\n runs.push(run);\n say(line(cell, run, measure(cell, run)));\n }\n return { manifest, runs, warmup };\n } finally {\n await lab.close();\n }\n};\n\n/**\n * This function does the runs of a cell. They go across new environments, and each environment records\n * its manifest. Then the verdict judges the runs. A median across the edge of the band extends\n * the cell from 11 runs to 22, and then to 44, each extension in new environments. With a\n * concurrency of more than one, that many environments operate at the same time, each with its\n * own prober. The runs then go across at least as many environments.\n *\n * Each environment does a warm-up first, and the result keeps it apart from the counted runs.\n */\nexport const runCell = async (\n cell: Cell,\n say: (line: string) => void = () => {},\n): Promise<CellResult> => {\n const started = new Date().toISOString();\n const manifests: Manifest[] = [];\n const runs: Run[] = [];\n const warmups: Run[] = [];\n const concurrency = Math.max(1, cell.concurrency ?? 1);\n let target: number = cell.runs;\n let next = 0;\n for (;;) {\n const counts = spread(target - runs.length, Math.max(cell.environments, concurrency));\n for (let at = 0; at < counts.length; at += concurrency) {\n const batch = counts.slice(at, at + concurrency).map((count) => ({ count, env: next++ }));\n const done = await Promise.all(\n batch.map(({ count, env }) => inEnvironment(cell, env, count, say)),\n );\n for (const each of done) {\n manifests.push(each.manifest);\n // each run carries the HZ of its environment, which the rule of a late timer reads\n const hz = each.manifest.hz === null ? undefined : Number(each.manifest.hz);\n const withHz = (run: Run): Run => (hz === undefined ? run : { ...run, hz });\n runs.push(...each.runs.map(withHz));\n warmups.push(withHz(each.warmup));\n }\n }\n const { measured, verdict } = judge(cell, runs);\n const larger = STEPS.find((step) => step > runs.length);\n if (verdict.decision !== \"extend\" || larger === undefined) {\n const at = { started, ended: new Date().toISOString() };\n return { cell, manifests, runs, warmups, measured, verdict, at };\n }\n target = larger;\n }\n};\n"],"mappings":"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"}