@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/README.md +56 -3
- package/build/index.d.ts +466 -8
- package/build/index.js +1089 -13
- package/build/index.js.map +1 -1
- package/image/native/src/bin/inject.rs +182 -4
- package/image/node/Dockerfile +3 -2
- package/image/node/client-sentinel.mjs +106 -0
- package/image/node/observer.mjs +39 -0
- package/package.json +6 -4
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
|
-
|
|
370
|
-
|
|
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
|