Review a multi-region failover control plane

src/failover-types.tsTypeScript
@@ -0,0 +1,76 @@
1+export type Region = "east" | "west";
2+export type FailoverPhase = "planned" | "source-fenced" | "target-writable" | "traffic-shifted" | "complete";
3+
4+export interface HealthSample {
5+ observerId: string;
6+ healthy: boolean;
7+ sampledAt: Date;
8+}
9+
10+export interface FailoverState {
11+ activeRegion: Region;
12+ epoch: number;
13+ operationId?: string;
14+ phase?: FailoverPhase;
15+ sourceSafePosition: number;
16+}
17+
18+export interface FailoverCommandIdentity {
19+ operationId: string;
20+}
21+
22+export interface FailoverDependencies {
23+ health: {
24+ recent(region: Region): Promise<HealthSample[]>;
25+ };
26+ lease: {
27+ acquire(operationId: string): Promise<{
28+ fencingToken: number;
29+ release(): Promise<void>;
30+ }>;
31+ };
32+ replication: {
33+ status(region: Region): Promise<{
34+ lagSeconds: number;
35+ appliedPosition: number;
36+ }>;
37+ };
38+ regions: {
39+ setReadOnly(
40+ region: Region,
41+ fencingToken: number,
42+ ): Promise<{ finalSafePosition: number }>;
43+ setWritable(region: Region, fencingToken: number): Promise<void>;
44+ setReadOnlyForOperation(
45+ region: Region,
46+ fencingToken: number,
47+ identity: FailoverCommandIdentity,
48+ ): Promise<{ finalSafePosition: number }>;
49+ setWritableForOperation(
50+ region: Region,
51+ fencingToken: number,
52+ identity: FailoverCommandIdentity,
53+ ): Promise<void>;
54+ };
55+ traffic: {
56+ shift(region: Region, fencingToken: number): Promise<void>;
57+ shiftForOperation(
58+ region: Region,
59+ fencingToken: number,
60+ identity: FailoverCommandIdentity,
61+ ): Promise<void>;
62+ };
63+ state: {
64+ load(): Promise<FailoverState>;
65+ save(state: FailoverState, fencingToken: number): Promise<void>;
66+ };
67+ operationLock: {
68+ runExclusive<T>(work: () => Promise<T>): Promise<T>;
69+ };
70+ ids: {
71+ next(): string;
72+ };
73+ telemetry: {
74+ replicationLag(seconds: number): void;
75+ };
76+}