@mark1russell7/harness 0.4.0 → 0.5.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/build/index.d.ts CHANGED
@@ -35,8 +35,16 @@ import "testcontainers";
35
35
  * A fault at a router of the topology (`Cell.faultAt`) acts on what the router forwards toward
36
36
  * one side. It is a black hole, a reset or an ICMP unreachable. It can also be a simplex drop, an
37
37
  * MTU drop or netem.
38
+ *
39
+ * These faults are of a replicated group (`Cell.group`), on the part that `Cell.faultAt` names:
40
+ * - `blackhole` drops each segment between the part and each of its peers. `rst` answers each
41
+ * segment from a peer with a reset, thus the part refuses each connection.
42
+ * - `partition` is a black hole between the part and some of its peers only (`GroupPlace.cuts`).
43
+ * - `freeze-process` stops the server process of the part with SIGSTOP, and SIGCONT thaws it. Its
44
+ * kernel stays up. `fin` stops the process with SIGKILL.
45
+ * - `planned-failover` asks a Sentinel for a failover: `SENTINEL FAILOVER <group>`.
38
46
  */
39
- type Fault = "blackhole" | "freeze-process" | "rst" | "stall" | "fin" | "unreachable" | "simplex" | "mtu-blackhole" | "netem" | "middlebox-forget" | "proxy-idle" | "toxic" | "server-close" | "none";
47
+ type Fault = "blackhole" | "freeze-process" | "rst" | "stall" | "fin" | "unreachable" | "simplex" | "mtu-blackhole" | "netem" | "middlebox-forget" | "proxy-idle" | "toxic" | "server-close" | "partition" | "planned-failover" | "none";
40
48
  /**
41
49
  * A middlebox form tells what a router does with a flow that it forgot, when a late packet comes:
42
50
  * - `reset-both` sends a reset to the two ends when it forgets the flow.
@@ -108,6 +116,57 @@ interface Topology {
108
116
  readonly name: string;
109
117
  readonly hops: readonly Hop[];
110
118
  }
119
+ /**
120
+ * A group is a replicated group under an external failover authority, as data. It is a primary and
121
+ * its replicas, and the Sentinels that decide which member is the primary. Each member and each
122
+ * Sentinel is a container from the same stock image, pinned by digest. Each has its own network
123
+ * namespace, thus a fault can act on one part. The client finds the primary through the
124
+ * Sentinels, and the parts and the client share one network.
125
+ */
126
+ interface Group {
127
+ /** This name is the name of the group that the Sentinels monitor, for example `mymaster`. */
128
+ readonly name: string;
129
+ /** This image is the stock image of each member and each Sentinel, pinned by digest. */
130
+ readonly image: string;
131
+ /** These are the programs of the image: the server, the Sentinel and the command line client. */
132
+ readonly programs: {
133
+ readonly server: string;
134
+ readonly sentinel: string;
135
+ readonly cli: string;
136
+ };
137
+ readonly port: number;
138
+ readonly sentinelPort: number;
139
+ /** These are the names of the members. The first one is the primary when the environment starts. */
140
+ readonly members: readonly string[];
141
+ readonly sentinels: readonly string[];
142
+ /** This count is how many Sentinels must agree that the primary is down. */
143
+ readonly quorum: number;
144
+ /** These are the settings of each member, as its configuration file spells them. The harness adds `replicaof` for each replica. */
145
+ readonly memberSettings: Readonly<Record<string, string>>;
146
+ /**
147
+ * These are the settings of each Sentinel for the group, as `sentinel <setting> <group> <value>`
148
+ * spells them: `down-after-milliseconds`, `failover-timeout` and `parallel-syncs`.
149
+ */
150
+ readonly sentinelSettings: Readonly<Record<string, string>>;
151
+ /** These lines tell that a member and a Sentinel serve. */
152
+ readonly ready: {
153
+ readonly member: string;
154
+ readonly sentinel: string;
155
+ };
156
+ }
157
+ /** 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. */
158
+ type GroupRole = "primary" | "replica" | "sentinel";
159
+ /**
160
+ * A group place is the part of a group that a fault acts on. A network fault cuts the part from
161
+ * some of its peers:
162
+ * - `all`: each other part of the group, and each client. This is the default.
163
+ * - `group`: the other members and the Sentinels, but not the clients.
164
+ * - `clients`: the clients, but not the group.
165
+ */
166
+ interface GroupPlace {
167
+ readonly role: GroupRole;
168
+ readonly cuts?: "all" | "group" | "clients";
169
+ }
111
170
  /** A toxic is one toxic of Toxiproxy, as its API takes it. Its toxicity is 1. */
112
171
  interface Toxic {
113
172
  readonly type: "latency" | "timeout" | "reset_peer" | "slow_close" | "bandwidth" | "limit_data" | "slicer";
@@ -176,12 +235,56 @@ interface ProxyTime {
176
235
  readonly quantile: number;
177
236
  readonly time: Quantity;
178
237
  }
238
+ /**
239
+ * A stage is one step of the timeline of a group after a fault, by the event that a Sentinel
240
+ * publishes for it. The stages are the subjective down, the objective down, the start of a
241
+ * failover and the election. Then the selection of a replica, the promotion, the switch to the new
242
+ * primary, and the conversion of the old primary into a replica.
243
+ */
244
+ type Stage = "+sdown" | "+odown" | "+try-failover" | "+elected-leader" | "+selected-slave" | "+promoted-slave" | "+switch-master" | "+convert-to-slave";
245
+ /** A stage band is when the first Sentinel announces a stage, from the reference of the cell. */
246
+ interface StageBand {
247
+ readonly stage: Stage;
248
+ readonly low: Quantity;
249
+ readonly high: Quantity;
250
+ }
251
+ /**
252
+ * A group prediction tells what a group does after the fault, beside the band of its cell:
253
+ * - `lands` is the primary that the first accepted write after the fault lands on: a new one, or the same one.
254
+ * - `failover` tells if a Sentinel announces a switch to a new primary in the window.
255
+ * - `stages` are the bands of the stages of the timeline. Each band includes the error of the
256
+ * stamps of the observer (`stamps`).
257
+ * - `errors` are the errors that the client must meet on the way, each with the band of its
258
+ * first occurrence. An example is the error of the offline queue when it gives up.
259
+ */
260
+ interface GroupPrediction {
261
+ readonly lands: "new" | "same";
262
+ readonly failover: boolean;
263
+ readonly stages: readonly StageBand[];
264
+ readonly stamps?: Quantity;
265
+ readonly errors?: ReadonlyArray<{
266
+ readonly name: string;
267
+ readonly low: Quantity;
268
+ readonly high: Quantity;
269
+ }>;
270
+ }
271
+ /**
272
+ * A middlebox expectation tells what the router of a topology does with the flow in the window of
273
+ * a silent prediction. Without it, the router keeps the flow. With `forgets`, the router forgets
274
+ * the flow in that band after the last segment of the client, and nothing detects it.
275
+ */
276
+ interface MiddleboxExpectation {
277
+ readonly forgets: {
278
+ readonly low: Quantity;
279
+ readonly high: Quantity;
280
+ };
281
+ }
179
282
  /**
180
283
  * A prediction is what a cell predicts. Either a detection occurs in an interval [T_lo, T_hi]
181
284
  * after the reference event, or nothing detects the fault in a window. Nothing detects a stall
182
285
  * that is shorter than the give-up, and the connection recovers from it. The prediction then also
183
286
  * tells when. A silent prediction can also tell when the reply to the request comes back
184
- * (`replies`).
287
+ * (`replies`), and what a router does with the flow (`middlebox`).
185
288
  *
186
289
  * A band can name the first round trip that it assumes (`rtt`), and the proxy times that it
187
290
  * includes (`through`).
@@ -190,12 +293,15 @@ interface ProxyTime {
190
293
  * prediction is `detects: null` with the reason, and its runs are measurements only. A
191
294
  * measurement-only cell that names a reference (`from`) also records the time from it to the end
192
295
  * of the connection, or to the last data.
296
+ *
297
+ * For a group cell, the band is the first write that a primary accepts after the fault, and its
298
+ * errno is `OK`. `group` tells which primary, and the stages of the group on the way.
193
299
  */
194
300
  type Prediction = {
195
301
  readonly detects: true;
196
302
  /** This name is the timer of the machine that fires first, or the event that detects the fault, with its errno. */
197
303
  readonly timer: string;
198
- /** This name is the errno of the detection, or EOF for an end of file without an error. */
304
+ /** This name is the errno of the detection, or EOF for an end of file without an error. It is OK for a write that a primary accepts. */
199
305
  readonly errno: string;
200
306
  readonly from: Reference;
201
307
  /** This time is the closed form, less the most that the timer can fire early. */
@@ -204,18 +310,22 @@ type Prediction = {
204
310
  readonly high: Quantity;
205
311
  readonly rtt?: RttRange;
206
312
  readonly through?: readonly ProxyTime[];
313
+ readonly group?: GroupPrediction;
207
314
  } | {
208
315
  readonly detects: false;
209
316
  readonly window: Quantity;
210
317
  readonly recovers?: Recovers;
211
318
  readonly replies?: Replies;
319
+ readonly middlebox?: MiddleboxExpectation;
212
320
  readonly rtt?: RttRange;
213
321
  readonly through?: readonly ProxyTime[];
322
+ readonly group?: GroupPrediction;
214
323
  } | {
215
324
  readonly detects: null;
216
325
  readonly window: Quantity;
217
326
  readonly why: string;
218
327
  readonly from?: Reference;
328
+ readonly group?: GroupPrediction;
219
329
  };
220
330
  /** A target is the server that a cell connects to: a stock image. */
221
331
  interface Target {
@@ -347,8 +457,23 @@ interface Cell {
347
457
  * This name is the proxy hop whose own time a calibration cell measures. The time of a request
348
458
  * (`up`) starts at its arrival on the socket of the proxy toward the client. It stops at the
349
459
  * write on the socket toward the target. The time of its reply (`down`) goes the other way.
460
+ *
461
+ * For a group cell, `sentinels` makes the prober watch the namespace of each Sentinel too. Each
462
+ * stamp of the observer then gets the write of its Sentinel that it read (`lags`).
350
463
  */
351
464
  readonly calibrates?: string;
465
+ /**
466
+ * This group replaces the target: a replicated group under its Sentinels. The client is then
467
+ * ioredis in Sentinel mode, and it writes a stream of sequence numbers. `target` stays for the
468
+ * record, and the group does not read it.
469
+ */
470
+ readonly group?: Group;
471
+ /** This time is how long a fault on a group holds after its commit. The injector then lifts it at that instant, on the clock of the VM. */
472
+ readonly hold?: Quantity;
473
+ /** This flag makes a planned failover coordinated: `SENTINEL FAILOVER <group> COORDINATED`. */
474
+ readonly coordinated?: boolean;
475
+ /** These are options of ioredis over its defaults, for the client of a group. A cell at the defaults has none. */
476
+ readonly driverOptions?: Readonly<Record<string, unknown>>;
352
477
  /** This count is the number of runs of the cell (11), across new environments (two at least). */
353
478
  readonly runs: number;
354
479
  readonly environments: number;
@@ -359,16 +484,22 @@ interface Cell {
359
484
  */
360
485
  readonly concurrency?: number;
361
486
  }
487
+ /** A hop place is a hop of a topology, with the side that the rules of a fault face. */
488
+ interface HopPlace {
489
+ readonly hop: string;
490
+ readonly toward: "client" | "target";
491
+ }
362
492
  /**
363
493
  * A fault place is the namespace that gets the rules of a fault. "target" and "client" are the two
364
494
  * ends. A hop place is a hop, with the side that the rules face. That side is the next hop toward
365
495
  * the client, or the next hop toward the target. The rules of a proxy act on its sockets. The
366
496
  * rules of a router act on what it forwards, by its device toward that side: the FORWARD chain.
497
+ *
498
+ * A group place is a part of a group, by its role.
367
499
  */
368
- type FaultPlace = "target" | "client" | {
369
- readonly hop: string;
370
- readonly toward: "client" | "target";
371
- };
500
+ type FaultPlace = "target" | "client" | HopPlace | GroupPlace;
501
+ /** This function tells if a fault place is a hop of a topology. */
502
+ export declare const isHopPlace: (place: FaultPlace | undefined) => place is HopPlace;
372
503
  /** A middlebox event is one line of the router: a change of a conntrack entry, or a reset that it sent. `t` is CLOCK_MONOTONIC in nanoseconds. */
373
504
  interface MiddleboxEvent {
374
505
  readonly ev: string;
@@ -416,6 +547,40 @@ interface Run {
416
547
  readonly topology?: RunTopology;
417
548
  /** These are the lines of the routers of a topology from the start to the end of the run. */
418
549
  readonly middlebox?: readonly MiddleboxEvent[];
550
+ /** This record is the group of a group cell: its parts, and what it did in the run. */
551
+ readonly group?: RunGroup;
552
+ }
553
+ /** A Sentinel event is one line of the observer of a group: an event that a Sentinel published, on the clock of the VM. */
554
+ interface SentinelEvent {
555
+ readonly ev: string;
556
+ readonly t: number;
557
+ /** This text is the address and port of the Sentinel. */
558
+ readonly sentinel?: string;
559
+ /** This name is the event, for example `+switch-master`. */
560
+ readonly event?: string;
561
+ /** This text is the detail of the event, as the Sentinel wrote it. */
562
+ readonly detail?: string;
563
+ readonly [field: string]: unknown;
564
+ }
565
+ /** A part is one member or Sentinel of a group, as a run found it: its address and its network namespace. */
566
+ interface RunPart {
567
+ readonly ip: string;
568
+ readonly netns: number;
569
+ }
570
+ /** A run group is the group of a run: its parts, the primary before and after, the timeline, and the writes that it kept. */
571
+ interface RunGroup {
572
+ readonly members: Readonly<Record<string, RunPart>>;
573
+ readonly sentinels: Readonly<Record<string, RunPart>>;
574
+ /** This name is the member that was the primary at the start of the run. */
575
+ readonly primary: string;
576
+ /** This name is the part that got the fault, or null for a fault on the whole group. */
577
+ readonly at: string | null;
578
+ /** These are the lines of the observer of the Sentinels from the start to the end of the run. */
579
+ readonly timeline: readonly SentinelEvent[];
580
+ /** This name is the member that was the primary when the group converged after the run, or null when it did not converge. */
581
+ readonly after: string | null;
582
+ /** These are the sequence numbers that the group holds after the run, when it converged, or null. */
583
+ readonly holds: readonly number[] | null;
419
584
  }
420
585
  /**
421
586
  * A run topology tells where each socket of a run is, in a topology:
@@ -433,6 +598,91 @@ interface RunTopology {
433
598
  }>>;
434
599
  }
435
600
  //#endregion
601
+ //#region src/timeline.d.ts
602
+ /**
603
+ * This module measures a run of a group cell. It gives the first write that a primary accepted
604
+ * after the fault, and the timeline of the group as its Sentinels announced it. It also gives what
605
+ * became of each write.
606
+ */
607
+ /** These are the stages of a timeline, in their sequence after a fault. */
608
+ export declare const STAGES: readonly Stage[];
609
+ /** 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. */
610
+ interface ErrorOnTheWay {
611
+ readonly name: string;
612
+ readonly first: Quantity;
613
+ readonly count: number;
614
+ }
615
+ /**
616
+ * A writes record tells what became of the writes of a run, by their sequence numbers:
617
+ * - `old` counts the writes that the primary of the start of the run accepted after the fault.
618
+ * - `lost` are the writes that a primary accepted and the group did not keep. They are the cost
619
+ * of a split brain. It is null when the group did not converge after the run.
620
+ * - `unknown` are the writes that the client sent and did not see accepted: an error, or no answer.
621
+ * `held` tells if the group kept each one, or null when it did not converge.
622
+ */
623
+ interface Writes {
624
+ readonly sent: number;
625
+ readonly acked: number;
626
+ readonly old: number;
627
+ readonly lost: readonly number[] | null;
628
+ readonly unknown: ReadonlyArray<{
629
+ readonly n: number;
630
+ readonly held: boolean | null;
631
+ }>;
632
+ }
633
+ /** A group measurement is what one run of a group cell measured, beside its time. */
634
+ interface GroupMeasured {
635
+ /** These are the member that was the primary at the start, and the member of the first accepted write after the fault. */
636
+ readonly from: string;
637
+ readonly to: string | null;
638
+ /** These are the times of each stage from the fault: the first announcement by any Sentinel. A stage that no Sentinel announced is absent. */
639
+ readonly stages: Partial<Record<Stage, Quantity>>;
640
+ readonly writes: Writes;
641
+ readonly errors: readonly ErrorOnTheWay[];
642
+ /** These are the delays of the stamps of the observer after the writes of the Sentinels, for a calibration. */
643
+ readonly lags?: readonly Quantity[];
644
+ }
645
+ /** This function gives the time of each stage of a timeline from an instant: the first announcement of each, after the instant. */
646
+ export declare const stagesOf: (timeline: readonly SentinelEvent[], from: number) => Partial<Record<Stage, Quantity>>;
647
+ /** This function gives what became of each write of a run, from the lines of the client and the keys that the group kept. */
648
+ export declare const writesOf: (run: Run, after: number, primary: string) => Writes;
649
+ /** This function gives the errors that the client met after an instant: each kind, its first time, and its count. */
650
+ export declare const errorsOf: (events: readonly ProbeEvent[], after: number) => ErrorOnTheWay[];
651
+ /**
652
+ * This function gives the delay of each stamp of the observer after the write of its Sentinel that
653
+ * the observer read. That write is the latest write of the Sentinel's socket toward the observer,
654
+ * at the stamp or before it. The prober records each write (`tx`) on the clock of the stamps.
655
+ */
656
+ export declare const lagsOf: (run: Run, kernel: readonly KernelRecord[]) => Quantity[];
657
+ /** This type is the measurement of observe.ts, which a group run fills. */
658
+ interface GroupReading {
659
+ readonly outcome: {
660
+ readonly kind: "answered";
661
+ } | {
662
+ readonly kind: "silent";
663
+ };
664
+ readonly elapsed: Quantity | null;
665
+ readonly socket: Quantity | null;
666
+ readonly noticed: Quantity | null;
667
+ readonly void: string | null;
668
+ readonly finding: string | null;
669
+ readonly group: GroupMeasured;
670
+ }
671
+ /**
672
+ * This function reads one run of a group cell.
673
+ * - The reference is the start of the commit of the fault, as the injector stamped it. For a
674
+ * stall, it is the first write into the fault, as for the stall of a direct pair.
675
+ * - The outcome is the first write that a primary accepted after the fault. The prediction says
676
+ * which primary: a new one, or the same one after the lift.
677
+ * - Its time is the last segment with data that the client's socket to that primary received
678
+ * before the client saw the acceptance. The stamp of the client is the fallback.
679
+ * - A run is void when the prober lost records, or when the client did not become ready. It is
680
+ * also void when a Sentinel entered its TILT mode, or when the election split its votes. A band
681
+ * from the fault whose commit took longer than δ_abs is void too. So is a band whose hold lifted
682
+ * more than δ_abs late.
683
+ */
684
+ export declare const measureGroup: (cell: Cell, run: Run) => GroupReading;
685
+ //#endregion
436
686
  //#region src/observe.d.ts
437
687
  /**
438
688
  * An outcome tells how the wait of a run ended. The socket failed with an errno, or it read the
@@ -494,6 +744,8 @@ interface Measured {
494
744
  readonly reordering?: Reordering;
495
745
  /** These are the times of the proxy of a calibration cell, one for each request and its reply. */
496
746
  readonly crossings?: Crossings;
747
+ /** This record is what a run of a group cell measured: the stages of its timeline, and what became of each write. */
748
+ readonly group?: GroupMeasured;
497
749
  /** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */
498
750
  readonly void: string | null;
499
751
  /** This text tells what in the run disagrees with the prediction, other than its timing, or null. */
@@ -623,8 +875,59 @@ interface Verdict {
623
875
  readonly up: Spread | null;
624
876
  readonly down: Spread | null;
625
877
  };
878
+ /** This record is what the group of a group cell did over the counted runs: its stages, and its writes. */
879
+ readonly group?: GroupVerdict;
626
880
  readonly why: string;
627
881
  }
882
+ /**
883
+ * A stage verdict is the test of one stage of the timeline of a group against its band. The band
884
+ * is [T_lo − δ_abs, T_hi + the error of the stamps + δ_abs]. A run that did not announce the stage
885
+ * is outside too.
886
+ */
887
+ interface StageVerdict {
888
+ readonly stage: Stage;
889
+ readonly band: Interval;
890
+ readonly median: Quantity | null;
891
+ readonly outside: ReadonlyArray<{
892
+ readonly run: string;
893
+ readonly why: string;
894
+ }>;
895
+ readonly decision: "holds" | "fails";
896
+ }
897
+ /**
898
+ * A group verdict sums what the group of a group cell did in its counted runs:
899
+ * - `stages` tests each stage that the prediction gives a band.
900
+ * - `medians` is the median of each stage that a run announced, from the fault.
901
+ * - `lost` counts the writes that a primary accepted and the group did not keep, in each run.
902
+ * - `old` counts the writes that the old primary accepted after the fault, in each run.
903
+ * - `unknown` counts the writes whose outcome the client did not see, and how many the group kept.
904
+ * - `errors` are the errors on the way: the runs that met each, and the median of its first time.
905
+ * - `lags` is the spread of the delays of the stamps, for a calibration.
906
+ */
907
+ interface GroupVerdict {
908
+ readonly stages: readonly StageVerdict[];
909
+ readonly medians: Partial<Record<Stage, Quantity>>;
910
+ readonly lost: {
911
+ readonly runs: number;
912
+ readonly total: number;
913
+ readonly median: number | null;
914
+ readonly max: number | null;
915
+ };
916
+ readonly old: {
917
+ readonly median: number | null;
918
+ readonly max: number | null;
919
+ };
920
+ readonly unknown: {
921
+ readonly total: number;
922
+ readonly held: number;
923
+ };
924
+ readonly errors: ReadonlyArray<{
925
+ readonly name: string;
926
+ readonly runs: number;
927
+ readonly first: Quantity;
928
+ }>;
929
+ readonly lags?: Spread | null;
930
+ }
628
931
  /** This function gives the value at quantile p of sorted values, by nearest rank: x_(⌈p·n⌉). */
629
932
  export declare const quantileOf: (sorted: readonly number[], p: number) => number;
630
933
  /** This function gives the spread of a set of times, or null for none. */
@@ -648,6 +951,8 @@ export declare const medianInterval: (sorted: readonly number[]) => [number, num
648
951
  * which does not exist at this time.
649
952
  */
650
953
  export declare const verdictOf: (prediction: Prediction, runs: readonly Measured[]) => Verdict;
954
+ /** This function sums what the group of a group cell did in its counted runs, and tests each stage that has a band. */
955
+ export declare const groupVerdictOf: (prediction: Prediction, counted: readonly Measured[]) => GroupVerdict;
651
956
  //#endregion
652
957
  //#region src/lab.d.ts
653
958
  /** This function gives the image of the harness. */
@@ -738,6 +1043,24 @@ interface Manifest {
738
1043
  readonly sysctls: Readonly<Record<string, string>>;
739
1044
  /** These are the hops of a topology, in sequence from the client. */
740
1045
  readonly hops?: readonly ManifestHop[];
1046
+ /** These are the parts of a group, with their settings as each part read them back. */
1047
+ readonly group?: {
1048
+ readonly members: ReadonlyArray<{
1049
+ readonly name: string;
1050
+ readonly ip: string;
1051
+ readonly id: string;
1052
+ readonly settings: Readonly<Record<string, string>>;
1053
+ }>;
1054
+ readonly sentinels: ReadonlyArray<{
1055
+ readonly name: string;
1056
+ readonly ip: string;
1057
+ readonly id: string;
1058
+ readonly settings: Readonly<Record<string, string>>;
1059
+ }>;
1060
+ readonly observer: {
1061
+ readonly image: string;
1062
+ };
1063
+ };
741
1064
  }
742
1065
  /** These are the JSON lines of a program, as they arrive. */
743
1066
  interface Lines<T extends {
@@ -919,6 +1242,141 @@ export declare const deviceOf: (route: string) => string;
919
1242
  */
920
1243
  export declare const openLab: (target: Target, kind?: Client | ClientImage, topology?: Topology) => Promise<Lab>;
921
1244
  //#endregion
1245
+ //#region src/group.d.ts
1246
+ /**
1247
+ * This module is the lab of a replicated group: a primary and its replicas, and the Sentinels
1248
+ * that decide which member is the primary. It opens one environment for a group cell, and it does
1249
+ * the runs of the cell in it. Each member and each Sentinel is a container from the stock image of
1250
+ * the group, in its own network namespace. An injector sidecar shares its network and process
1251
+ * namespaces. The client of each run is ioredis in Sentinel mode. An observer subscribes to the
1252
+ * events of each Sentinel, and the prober watches the namespace of the client.
1253
+ */
1254
+ /** A stamp is the time of the injector around one commit, on CLOCK_MONOTONIC in nanoseconds. */
1255
+ interface Stamp {
1256
+ readonly t0: number;
1257
+ readonly t1: number;
1258
+ }
1259
+ /** A group network fault is a black hole between a part and its peers, or a reset of each segment from them. */
1260
+ type GroupNetworkFault = "blackhole" | "rst";
1261
+ /** A group client is the client container of one run of a group cell. */
1262
+ interface GroupClient {
1263
+ readonly events: readonly ProbeEvent[];
1264
+ until(kinds: readonly string[], within: number): Promise<ProbeEvent>;
1265
+ stop(): Promise<void>;
1266
+ }
1267
+ /** A group lab is one environment of a group cell. */
1268
+ interface GroupLab {
1269
+ readonly group: Group;
1270
+ readonly clientIp: string;
1271
+ readonly observerIp: string;
1272
+ readonly members: Readonly<Record<string, RunPart>>;
1273
+ readonly sentinels: Readonly<Record<string, RunPart>>;
1274
+ /** These are the namespaces that the prober watches: the client, and the Sentinels for a calibration. */
1275
+ readonly netns: {
1276
+ readonly client: number;
1277
+ readonly watched: readonly number[];
1278
+ };
1279
+ /** These are all the records that the prober wrote, in sequence. */
1280
+ readonly kernel: readonly KernelRecord[];
1281
+ /** These are all the lines of the observer of the Sentinels, in sequence. */
1282
+ readonly timeline: readonly SentinelEvent[];
1283
+ /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */
1284
+ sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;
1285
+ /** This method waits until the group is whole and calm, and gives the name of its primary. */
1286
+ converge(within?: number): Promise<string>;
1287
+ /** This method removes each key from the group, and waits until each replica has the removal. */
1288
+ flush(primary: string): Promise<void>;
1289
+ /** This method makes each Sentinel forget the state of the group (`SENTINEL RESET`): an aborted failover then holds off no later failover. */
1290
+ reset(): Promise<void>;
1291
+ /** This method gives the sequence numbers that the group holds, from its primary. */
1292
+ holds(primary: string): Promise<number[]>;
1293
+ /** This method gives the part that has a role in the group, when `primary` is the primary. */
1294
+ partOf(role: GroupRole, primary: string): string;
1295
+ /** This method starts the client of a run, with the arguments of `client-sentinel.mjs`. */
1296
+ client(args: readonly string[]): Promise<GroupClient>;
1297
+ /** This method applies a network fault between a part and some of its peers, at once or at an instant of the VM. */
1298
+ fault(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace["cuts"]>, at?: number): Promise<Stamp>;
1299
+ /** This method removes a network fault, at once or at an instant of the VM. */
1300
+ lift(kind: GroupNetworkFault, part: string, cuts: NonNullable<GroupPlace["cuts"]>, at?: number): Promise<Stamp>;
1301
+ /** This method sends a signal to the server process of a part, at once or at an instant of the VM. */
1302
+ signal(part: string, signal: "KILL" | "STOP" | "CONT", at?: number): Promise<Stamp>;
1303
+ /** This method starts the server of a member again, after a fin. */
1304
+ restart(member: string): Promise<void>;
1305
+ /** This method asks the first Sentinel for a failover of the group. */
1306
+ failover(coordinated: boolean): Promise<Stamp>;
1307
+ /** This method removes the TCP metrics of the client namespace: each run starts from a kernel that keeps nothing. */
1308
+ forget(): Promise<void>;
1309
+ /** This method waits for the next clock line of the prober, which gives the count of lost records. */
1310
+ settle(): Promise<number | null>;
1311
+ manifest(sysctls: Readonly<Record<string, string>>): Promise<Manifest>;
1312
+ close(): Promise<void>;
1313
+ }
1314
+ /** This function gives the configuration file of a member: its port, its settings, and the primary that a replica follows. */
1315
+ export declare const memberConfig: (group: Group, primary: string | null) => string;
1316
+ /** This function gives the configuration file of a Sentinel: its port, the group that it monitors, and the settings of the group. */
1317
+ export declare const sentinelConfig: (group: Group, primary: string) => string;
1318
+ /**
1319
+ * This function gives the peers that a fault on a part cuts it from, by their addresses. `all` is
1320
+ * each other part and the client, `group` is the other parts, and `clients` is the client. The
1321
+ * observer is not a peer, thus the timeline of the group stays whole.
1322
+ */
1323
+ export declare const peersOf: (parts: {
1324
+ readonly members: Readonly<Record<string, RunPart>>;
1325
+ readonly sentinels: Readonly<Record<string, RunPart>>;
1326
+ }, client: string, part: string, cuts: NonNullable<GroupPlace["cuts"]>) => string[];
1327
+ /** This function reads the flat reply of a command line client into its fields: name, value, name, value. */
1328
+ export declare const fieldsOf: (text: string) => Array<Record<string, string>>;
1329
+ /** This function reads the INFO of a server into its fields. */
1330
+ export declare const infoOf: (text: string) => Record<string, string>;
1331
+ /**
1332
+ * A view of the group is what its parts tell at one time. `healthy` tells if each Sentinel agrees
1333
+ * on one primary with no flag but `master`, and knows each replica and each other Sentinel. Each
1334
+ * replica must follow that primary with its link up, and the primary must have each replica online,
1335
+ * with no failover in progress.
1336
+ */
1337
+ interface GroupView {
1338
+ readonly primary: string | null;
1339
+ readonly healthy: boolean;
1340
+ readonly why: string;
1341
+ }
1342
+ /** This function judges the views of the Sentinels and of the members: the primary that they agree on, if the group is healthy. */
1343
+ export declare const viewOf: (group: Group, ips: Readonly<Record<string, string>>, sentinels: ReadonlyArray<{
1344
+ readonly master: Record<string, string>;
1345
+ readonly replicas: ReadonlyArray<Record<string, string>>;
1346
+ }>, members: Readonly<Record<string, Record<string, string>>>) => GroupView;
1347
+ /** This time is the longest that a group takes to converge before a run, or after it. */
1348
+ export declare const CONVERGE_MS = 24e4;
1349
+ /**
1350
+ * This function opens one environment of a group cell:
1351
+ * - one new network, with each member, each Sentinel, the client and the observer on it.
1352
+ * - each member: the stock image, its server as the child of a shell, thus a fin can stop the
1353
+ * server and the container stays up. The first member is the primary, and the others follow it.
1354
+ * - each Sentinel: the stock image, its Sentinel as the child of a shell, which monitors the first member.
1355
+ * - an injector sidecar for each part, in its network and process namespaces.
1356
+ * - a client that holds its own network namespace, with a privileged sidecar for its sysctls.
1357
+ * - the observer of the Sentinels, in a namespace of its own.
1358
+ * - the prober, which watches the namespace of the client, and of each Sentinel for a calibration.
1359
+ */
1360
+ export declare const openGroupLab: (group: Group, options?: {
1361
+ readonly watchSentinels?: boolean;
1362
+ }) => Promise<GroupLab>;
1363
+ /** This function gives the latest time that a group cell waits for its outcome, in ms from the fault. */
1364
+ export declare const deadlineOf: (cell: Cell) => number;
1365
+ /** This function gives the latest subscription of the observer to each Sentinel, in some lines of the observer. */
1366
+ export declare const subscriptionsOf: (timeline: readonly SentinelEvent[]) => SentinelEvent[];
1367
+ /**
1368
+ * This function does one run of a group cell:
1369
+ * - The group converges, and its keys go. The client connects through the Sentinels, and its
1370
+ * stream of writes gets steady.
1371
+ * - The fault occurs on its part, as the primary of this run makes the roles. A fault with a
1372
+ * hold lifts at its instant in the VM. A stall lifts at its length after the first write into it.
1373
+ * - The run waits for its outcome: the first write that a new primary accepts, or the first one
1374
+ * after the lift on the same primary. Then it continues for a moment.
1375
+ * - Each fault goes, the client stops, and the group converges again. The keys that it then holds
1376
+ * tell which writes it kept.
1377
+ */
1378
+ export declare const groupOnce: (lab: GroupLab, cell: Cell, environment: number, index: number) => Promise<Run>;
1379
+ //#endregion
922
1380
  //#region src/run.d.ts
923
1381
  /** A cell result is a cell with its manifests, its runs, their measurements and its verdict. */
924
1382
  interface CellResult {
@@ -957,5 +1415,5 @@ export declare const spread: (runs: number, environments: number) => number[];
957
1415
  */
958
1416
  export declare const runCell: (cell: Cell, say?: (line: string) => void) => Promise<CellResult>;
959
1417
  //#endregion
960
- export type { Agent, Cell, CellResult, Client, ClientImage, Crossings, Decision, Fault, FaultPlace, FaultSide, Hop, Interval, KernelRecord, Lab, Manifest, ManifestHop, Measured, MiddleboxEvent, MiddleboxForm, NetworkFault, Observer, Outcome, Prediction, Probe, ProbeEvent, ProxyHop, ProxyTime, Recovers, Reference, Reordering, Replies, RouterHop, RttRange, Run, RunTopology, SocketPlace, Spread, Target, Topology, Toxic, Verdict };
1418
+ export type { Agent, Cell, CellResult, Client, ClientImage, Crossings, Decision, ErrorOnTheWay, Fault, FaultPlace, FaultSide, Group, GroupClient, GroupLab, GroupMeasured, GroupNetworkFault, GroupPlace, GroupPrediction, GroupReading, GroupRole, GroupVerdict, GroupView, Hop, HopPlace, Interval, KernelRecord, Lab, Manifest, ManifestHop, Measured, MiddleboxEvent, MiddleboxExpectation, MiddleboxForm, NetworkFault, Observer, Outcome, Prediction, Probe, ProbeEvent, ProxyHop, ProxyTime, Recovers, Reference, Reordering, Replies, RouterHop, RttRange, Run, RunGroup, RunPart, RunTopology, SentinelEvent, SocketPlace, Spread, Stage, StageBand, StageVerdict, Stamp, Target, Topology, Toxic, Verdict, Writes };
961
1419
  //# sourceMappingURL=index.d.ts.map