@mark1russell7/harness 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/build/index.d.ts +34 -2
- package/build/index.js +41 -7
- package/build/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -203,6 +203,8 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
|
|
|
203
203
|
- The second view is the stamps of the program: from its `ping` line to its `drop` line. The steps are the later pings, each from the reference.
|
|
204
204
|
- The lines of a client name the socket by its local address (`local`), and the lines of a server by its peer (`peer`). For the server, the lines are the lines of the target. A line whose address is null is about a try that did not open.
|
|
205
205
|
- A prediction can say how the program drops (`drop`). A close frame (`frame`) is a write, and the socket stays ESTABLISHED after it. A close (`close`) closes the socket in δ_abs: a FIN or an abort. Another drop is a finding. A FIN of the peer that answers a close frame is no close by the program. The run gives the time from the `drop` line to the close by the program (`left`).
|
|
206
|
+
- The client process ends at the end of each run, and its kernel then closes each socket that is still open. A prediction can say that the program is that client process (`exits`). Then a close in δ_abs of that end is no close by the program: `left` stays null, and `exited` gives the time.
|
|
207
|
+
- The end of the process is the first change out of ESTABLISHED of another socket of the client after its last `dial` line (`exitOf`). The program dropped none of these sockets.
|
|
206
208
|
- A heartbeat is a timer of the program. The retransmit timer of its ping fires in the same interval. Thus a run whose retransmit timer was two jiffies or more behind its wheel slot is void: the VM stopped a vCPU.
|
|
207
209
|
- **The reconnect** (`reconnectOf`). A prediction with `reconnects` also reads what the client did after the detection.
|
|
208
210
|
- Each wait goes from a `reconnecting` line, where the program starts its timer, to its next `dial` line, where it makes the socket. A wait outside its band, widened by δ_abs, is a finding. A try that did not come is a finding too.
|
|
@@ -263,7 +265,7 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
|
|
|
263
265
|
- **An HTTP client** (`Verdict.requests`). The verdict counts each outcome of the second request over the counted runs. A measurement-only cell also gives them in its reason.
|
|
264
266
|
- **The reconnect** (`Verdict.reconnect`). A wait outside its band is a finding of its run, thus the cell fails. The verdict gives the spread of each wait, of the first SYN, of the first ESTABLISHED and of the work before each connect(2). Its reason says the medians.
|
|
265
267
|
- **A call in flight** (`Verdict.calls`). The verdict counts each outcome of the call in flight of a WebSocket client, and its reason says them.
|
|
266
|
-
- **The socket after a drop** (`Verdict.left`). The verdict gives the spread of the time from a drop to the close of its socket by the program. It also counts the runs where the program did not close it.
|
|
268
|
+
- **The socket after a drop** (`Verdict.left`). The verdict gives the spread of the time from a drop to the close of its socket by the program. It also counts the runs where the program did not close it. For the runs where the socket stayed open until the end of the client process, it gives the spread of that end (`exited`).
|
|
267
269
|
|
|
268
270
|
The distribution test needs a simulator, which does not exist at this time.
|
|
269
271
|
|
package/build/index.d.ts
CHANGED
|
@@ -468,6 +468,12 @@ type Prediction = {
|
|
|
468
468
|
* ESTABLISHED at once (`close`).
|
|
469
469
|
*/
|
|
470
470
|
readonly drop?: "frame" | "close";
|
|
471
|
+
/**
|
|
472
|
+
* This flag tells that the program of the observer is the client process, which ends at the
|
|
473
|
+
* end of the run. Its kernel then closes each socket that is still open. A close of the
|
|
474
|
+
* socket at that end is not a close by the program, thus `left` stays null for it (`exited`).
|
|
475
|
+
*/
|
|
476
|
+
readonly exits?: true;
|
|
471
477
|
} | {
|
|
472
478
|
readonly detects: false;
|
|
473
479
|
readonly window: Quantity;
|
|
@@ -923,12 +929,29 @@ export declare const programLines: (cell: Cell, run: Run) => readonly ProbeEvent
|
|
|
923
929
|
* the peer (CLOSE_WAIT) is not such a close. It is null when the socket did not close itself.
|
|
924
930
|
*/
|
|
925
931
|
export declare const leftAfter: (socket: readonly KernelRecord[], line: ProbeEvent) => Quantity | null;
|
|
932
|
+
/**
|
|
933
|
+
* This function gives the end of the client process in a run, as the kernel stamps it. The process
|
|
934
|
+
* ends after its last try, and its kernel then closes each socket that is still open, at one
|
|
935
|
+
* instant. Thus the end is the first change out of ESTABLISHED of another socket of the client
|
|
936
|
+
* after its last `dial` line. The program dropped none of these sockets. It is undefined when the
|
|
937
|
+
* record has no such change, for example when the last try did not open.
|
|
938
|
+
*/
|
|
939
|
+
export declare const exitOf: (run: Run, cookie: number | undefined) => number | undefined;
|
|
940
|
+
/**
|
|
941
|
+
* This function separates a close of the socket by the program from the end of the client process.
|
|
942
|
+
* `left` is the time from the drop line to the close, and `exit` is the end of the process. A close
|
|
943
|
+
* in δ_abs of that end is the end, not the program: `left` becomes null, and `exited` keeps the time.
|
|
944
|
+
*/
|
|
945
|
+
export declare const closeOrExit: (line: ProbeEvent, left: Quantity | null, exit: number | undefined, delta?: Quantity) => {
|
|
946
|
+
readonly left: Quantity | null;
|
|
947
|
+
readonly exited?: Quantity;
|
|
948
|
+
};
|
|
926
949
|
/**
|
|
927
950
|
* This function tells how a drop differs from the way that its prediction gives, or gives null.
|
|
928
951
|
* A close closes the socket in δ_abs. A close frame is a write, and the program does not close
|
|
929
952
|
* the socket at the drop. A peer that answers the close frame can close it, and that is no finding.
|
|
930
953
|
*/
|
|
931
|
-
export declare const dropFinding: (expected: "frame" | "close", drop: ProgramEvent, left: Quantity | null, delta?: Quantity) => string | null;
|
|
954
|
+
export declare const dropFinding: (expected: "frame" | "close", drop: ProgramEvent, left: Quantity | null, delta?: Quantity, exited?: Quantity) => string | null;
|
|
932
955
|
/** This function gives each ping of the program on the connection of the run, with the kernel's stamp of its write. */
|
|
933
956
|
export declare const pingsOf: (lines: readonly ProbeEvent[], socket: readonly KernelRecord[], local: unknown, delta?: Quantity) => ProgramEvent[];
|
|
934
957
|
/**
|
|
@@ -1239,9 +1262,15 @@ interface Measured {
|
|
|
1239
1262
|
/**
|
|
1240
1263
|
* This time is from the drop line of the program to the close of its socket by its own side: a
|
|
1241
1264
|
* FIN or an abort. A prediction that says how the program drops (`drop`) gets it. It is null
|
|
1242
|
-
* when the socket did not close itself in the run.
|
|
1265
|
+
* when the socket did not close itself in the run. A prediction can say that the client process
|
|
1266
|
+
* ends with the run (`exits`). Then a close at that end is no close by the program, and `left` is null.
|
|
1243
1267
|
*/
|
|
1244
1268
|
readonly left?: Quantity | null;
|
|
1269
|
+
/**
|
|
1270
|
+
* This time is from the drop line of the program to the end of the client process. A socket gets
|
|
1271
|
+
* it when it stayed open until that end. The kernel closed it then, not the program (`exits`).
|
|
1272
|
+
*/
|
|
1273
|
+
readonly exited?: Quantity;
|
|
1245
1274
|
/** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */
|
|
1246
1275
|
readonly void: string | null;
|
|
1247
1276
|
/** This text tells what in the run disagrees with the prediction, other than its timing, or null. */
|
|
@@ -1421,10 +1450,13 @@ interface Verdict {
|
|
|
1421
1450
|
/**
|
|
1422
1451
|
* This record is the time from a drop of the program to the close of its socket by its own side,
|
|
1423
1452
|
* over the counted runs. It also counts the runs where the socket did not close itself.
|
|
1453
|
+
* `exited` is the spread of the end of the client process, for the runs where the socket stayed
|
|
1454
|
+
* open until that end (`Measured.exited`).
|
|
1424
1455
|
*/
|
|
1425
1456
|
readonly left?: {
|
|
1426
1457
|
readonly spread: Spread | null;
|
|
1427
1458
|
readonly stayed: number;
|
|
1459
|
+
readonly exited?: Spread | null;
|
|
1428
1460
|
};
|
|
1429
1461
|
readonly why: string;
|
|
1430
1462
|
}
|
package/build/index.js
CHANGED
|
@@ -166,12 +166,15 @@ const withProgram = (verdict, counted) => {
|
|
|
166
166
|
const outcomes = {};
|
|
167
167
|
for (const call of calls) outcomes[call.outcome] = (outcomes[call.outcome] ?? 0) + 1;
|
|
168
168
|
const gone = lefts.flatMap((each) => each === null ? [] : [each]);
|
|
169
|
+
const ends = counted.flatMap((run) => run.exited === void 0 ? [] : [run.exited]);
|
|
169
170
|
const left = lefts.length === 0 ? void 0 : {
|
|
170
171
|
spread: spreadOf(gone),
|
|
171
|
-
stayed: lefts.length - gone.length
|
|
172
|
+
stayed: lefts.length - gone.length,
|
|
173
|
+
...ends.length === 0 ? {} : { exited: spreadOf(ends) }
|
|
172
174
|
};
|
|
175
|
+
const ended = ends.length === 0 ? "" : `: in ${ends.length} the socket stayed open until the client process ended, median ${ms(spreadOf(ends))} after the drop line`;
|
|
173
176
|
const said = [
|
|
174
|
-
...left === void 0 ? [] : [`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}`}.`],
|
|
177
|
+
...left === void 0 ? [] : [`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}${ended}`}.`],
|
|
175
178
|
...reconnect === void 0 ? [] : [`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.`],
|
|
176
179
|
...calls.length === 0 ? [] : [`Calls in flight: ${Object.entries(outcomes).map(([outcome, n]) => `${n} ${outcome}`).join("; ")}.`]
|
|
177
180
|
];
|
|
@@ -500,15 +503,41 @@ const leftAfter = (socket, line) => {
|
|
|
500
503
|
return left === void 0 ? null : between$3(line.t, left.mono);
|
|
501
504
|
};
|
|
502
505
|
/**
|
|
506
|
+
* This function gives the end of the client process in a run, as the kernel stamps it. The process
|
|
507
|
+
* ends after its last try, and its kernel then closes each socket that is still open, at one
|
|
508
|
+
* instant. Thus the end is the first change out of ESTABLISHED of another socket of the client
|
|
509
|
+
* after its last `dial` line. The program dropped none of these sockets. It is undefined when the
|
|
510
|
+
* record has no such change, for example when the last try did not open.
|
|
511
|
+
*/
|
|
512
|
+
const exitOf = (run, cookie) => {
|
|
513
|
+
const last = run.events.filter((line) => line.ev === "dial").at(-1);
|
|
514
|
+
if (last === void 0) return void 0;
|
|
515
|
+
const dropped = new Set(run.events.filter((line) => line.ev === "drop").map((line) => line["local"]));
|
|
516
|
+
return run.kernel.find((record) => record.netns === run.netns && record.cookie !== cookie && record.ev === "state" && oldState$2(record) === 1 && (newState$2(record) === 4 || newState$2(record) === 7) && record.mono >= last.t && !dropped.has(record.src))?.mono;
|
|
517
|
+
};
|
|
518
|
+
/**
|
|
519
|
+
* This function separates a close of the socket by the program from the end of the client process.
|
|
520
|
+
* `left` is the time from the drop line to the close, and `exit` is the end of the process. A close
|
|
521
|
+
* in δ_abs of that end is the end, not the program: `left` becomes null, and `exited` keeps the time.
|
|
522
|
+
*/
|
|
523
|
+
const closeOrExit = (line, left, exit, delta = NOISE) => {
|
|
524
|
+
if (left === null || exit === void 0) return { left };
|
|
525
|
+
const at = line.t + valueIn(left, "s") * 1e9;
|
|
526
|
+
return Math.abs(at - exit) <= valueIn(delta, "s") * 1e9 ? {
|
|
527
|
+
left: null,
|
|
528
|
+
exited: left
|
|
529
|
+
} : { left };
|
|
530
|
+
};
|
|
531
|
+
/**
|
|
503
532
|
* This function tells how a drop differs from the way that its prediction gives, or gives null.
|
|
504
533
|
* A close closes the socket in δ_abs. A close frame is a write, and the program does not close
|
|
505
534
|
* the socket at the drop. A peer that answers the close frame can close it, and that is no finding.
|
|
506
535
|
*/
|
|
507
|
-
const dropFinding = (expected, drop, left, delta = NOISE) => {
|
|
536
|
+
const dropFinding = (expected, drop, left, delta = NOISE, exited) => {
|
|
508
537
|
const noise = valueIn(delta, "s");
|
|
509
538
|
const soon = left !== null && valueIn(left, "s") <= noise;
|
|
510
539
|
if (expected === "close") {
|
|
511
|
-
if (left === null) return
|
|
540
|
+
if (left === null) return `the program did not close its socket after the drop, where a close is at once${exited === void 0 ? "" : ": it stayed open until the client process ended"}`;
|
|
512
541
|
return soon ? null : `the program closed its socket ${left.value} s after the drop, not at once`;
|
|
513
542
|
}
|
|
514
543
|
if (drop.record !== "write") return "the drop wrote no close frame on its socket";
|
|
@@ -1822,8 +1851,12 @@ const measureSocket = (cell, run) => {
|
|
|
1822
1851
|
const words = outcome.kind === "error" ? outcome.errno : outcome.kind === "closed" ? "its own close" : outcome.kind === "dropped" ? "a drop by its own program" : "end of file";
|
|
1823
1852
|
const reconnect = prediction.reconnects === void 0 ? void 0 : reconnectOf(run, ended, cookie, drop?.line.t ?? ended);
|
|
1824
1853
|
const late = reconnect === void 0 || prediction.reconnects === void 0 ? null : reconnectFinding(prediction.reconnects, reconnect, noiseOf(cell));
|
|
1825
|
-
const
|
|
1826
|
-
const
|
|
1854
|
+
const closed = prediction.drop === void 0 || drop === void 0 ? void 0 : leftAfter(socket, drop.line);
|
|
1855
|
+
const { left, exited } = closed === void 0 || drop === void 0 || prediction.exits !== true ? {
|
|
1856
|
+
left: closed,
|
|
1857
|
+
exited: void 0
|
|
1858
|
+
} : closeOrExit(drop.line, closed, exitOf(run, cookie), noiseOf(cell));
|
|
1859
|
+
const how = prediction.drop === void 0 || drop === void 0 || left === void 0 ? null : dropFinding(prediction.drop, drop, left, noiseOf(cell), exited);
|
|
1827
1860
|
const found = [
|
|
1828
1861
|
matches ? null : `it ended with ${words}, not ${prediction.errno}`,
|
|
1829
1862
|
late,
|
|
@@ -1851,6 +1884,7 @@ const measureSocket = (cell, run) => {
|
|
|
1851
1884
|
steps,
|
|
1852
1885
|
...reconnect === void 0 ? {} : { reconnect },
|
|
1853
1886
|
...left === void 0 ? {} : { left },
|
|
1887
|
+
...exited === void 0 ? {} : { exited },
|
|
1854
1888
|
...cell.probe.ws !== void 0 && inflight ? { call: callOf(events, request, ended) } : {},
|
|
1855
1889
|
void: voided,
|
|
1856
1890
|
finding
|
|
@@ -4723,6 +4757,6 @@ const runCell = async (cell, say = () => {}) => {
|
|
|
4723
4757
|
}
|
|
4724
4758
|
};
|
|
4725
4759
|
//#endregion
|
|
4726
|
-
export { CONVERGE_MS, INJECT, KEPT, LINGER_MS, LISTING, MEMBER_MARKS, NOISE, PUMP, QUIET_MS, RELATIVE, REPLICA_SET_STAGES, STAGES, STATUS_SCRIPT, STEPS, STEP_DOWN, callOf, clockOffsetOf, crossingsOf, cutOf, deadlineOf, delayVoids, deviceOf, dropFinding, dropOf, endpointAfter, errorsOf, fieldOf, fieldsOf, firstUnanswered, goHttpImage, groupOnce, groupVerdictOf, harnessImage, hzOf, infoOf, initiateScript, isHopPlace, judge, lagsOf, leftAfter, matches, measure, measureDriver, measureGroup, medianInterval, memberCommand, memberConfig, memberLineOf, mongoClientImage, nodeClientImage, nodeHttpImage, noiseOf, onMonotonic, openGroupLab, openLab, openReplicaSetLab, peersOf, pingsOf, proceduresImage, programLines, quantileOf, reconnectFinding, reconnectOf, referenceOf, reorderingOf, replicaSetArgs, replicaSetOnce, replicaSetStagesOf, requestOf, requestWords, routerImage, routesOf, runCell, sentinelConfig, socketAt, socketOf, socketsOf, spread, spreadOf, stagesOf, statusViewOf, subscriptionsOf, targetImageOf, templated, toxicBody, verdictOf, viewOf, writesOf };
|
|
4760
|
+
export { CONVERGE_MS, INJECT, KEPT, LINGER_MS, LISTING, MEMBER_MARKS, NOISE, PUMP, QUIET_MS, RELATIVE, REPLICA_SET_STAGES, STAGES, STATUS_SCRIPT, STEPS, STEP_DOWN, callOf, clockOffsetOf, closeOrExit, crossingsOf, cutOf, deadlineOf, delayVoids, deviceOf, dropFinding, dropOf, endpointAfter, errorsOf, exitOf, fieldOf, fieldsOf, firstUnanswered, goHttpImage, groupOnce, groupVerdictOf, harnessImage, hzOf, infoOf, initiateScript, isHopPlace, judge, lagsOf, leftAfter, matches, measure, measureDriver, measureGroup, medianInterval, memberCommand, memberConfig, memberLineOf, mongoClientImage, nodeClientImage, nodeHttpImage, noiseOf, onMonotonic, openGroupLab, openLab, openReplicaSetLab, peersOf, pingsOf, proceduresImage, programLines, quantileOf, reconnectFinding, reconnectOf, referenceOf, reorderingOf, replicaSetArgs, replicaSetOnce, replicaSetStagesOf, requestOf, requestWords, routerImage, routesOf, runCell, sentinelConfig, socketAt, socketOf, socketsOf, spread, spreadOf, stagesOf, statusViewOf, subscriptionsOf, targetImageOf, templated, toxicBody, verdictOf, viewOf, writesOf };
|
|
4727
4761
|
|
|
4728
4762
|
//# sourceMappingURL=index.js.map
|