Skip to content

Commit b96795f

Browse files
committed
Refactor chaos
1 parent 3ec5323 commit b96795f

File tree

15 files changed

+1175
-1133
lines changed

15 files changed

+1175
-1133
lines changed

chaos/db.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { ChaosProvider } from "@chaos/provider";
2+
import type { WorkerManager } from "@workers/manager";
3+
4+
export type DbChaosConfig = {
5+
minLockTime: number;
6+
maxLockTime: number;
7+
lockInterval: number;
8+
};
9+
10+
export class DbChaos implements ChaosProvider {
11+
config: DbChaosConfig;
12+
activeLocks = new Map<string, Promise<void>>();
13+
interval?: NodeJS.Timeout;
14+
15+
constructor(config: DbChaosConfig) {
16+
this.config = config;
17+
}
18+
19+
start(workers: WorkerManager): Promise<void> {
20+
const { minLockTime, maxLockTime, lockInterval } = this.config;
21+
console.log(
22+
`Starting DB Chaos:
23+
Locking for ${minLockTime}ms - ${maxLockTime}ms
24+
Interval: ${lockInterval}ms`,
25+
);
26+
this.interval = setInterval(() => {
27+
for (const worker of workers.getAll()) {
28+
const duration = Math.floor(
29+
minLockTime + Math.random() * (maxLockTime - minLockTime),
30+
);
31+
32+
const lockKey = `${worker.name}-${worker.installationId}`;
33+
34+
// Only lock if not already locked
35+
if (!this.activeLocks.has(lockKey)) {
36+
console.log(
37+
`[db-chaos] Locking ${worker.name} database for ${duration}ms`,
38+
);
39+
40+
// Call the lockDB method on the worker and track it
41+
const lockPromise = worker.worker
42+
.lockDB(duration)
43+
.catch((err: unknown) => {
44+
console.warn(err);
45+
})
46+
.finally(() => {
47+
this.activeLocks.delete(lockKey);
48+
});
49+
50+
this.activeLocks.set(lockKey, lockPromise);
51+
}
52+
}
53+
}, lockInterval);
54+
55+
return Promise.resolve();
56+
}
57+
58+
async stop() {
59+
console.log("Stopping DB Chaos");
60+
if (this.interval) {
61+
clearInterval(this.interval);
62+
}
63+
64+
// Wait for all the existing locks to complete
65+
await Promise.allSettled(Array.from(this.activeLocks.values()));
66+
}
67+
}

chaos/network.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import type { ChaosProvider } from "@chaos/provider";
2+
import type { DockerContainer } from "network-stability/container";
3+
4+
export type NetworkChaosConfig = {
5+
delayMin: number; // Minimum delay in ms
6+
delayMax: number; // Maximum delay in ms
7+
jitterMin: number; // Minimum jitter in ms
8+
jitterMax: number; // Maximum jitter in ms
9+
lossMin: number; // Minimum packet loss percentage (0-100)
10+
lossMax: number; // Maximum packet loss percentage (0-100)
11+
interval: number; // How often to apply chaos in ms
12+
};
13+
14+
// import type { Worker } from "@workers/manager";
15+
16+
export class NetworkChaos implements ChaosProvider {
17+
config: NetworkChaosConfig;
18+
interval?: NodeJS.Timeout;
19+
nodes: DockerContainer[];
20+
21+
constructor(config: NetworkChaosConfig, nodes: DockerContainer[]) {
22+
this.config = config;
23+
this.nodes = nodes;
24+
}
25+
26+
start(): Promise<void> {
27+
console.log(`Starting network chaos:
28+
Nodes: ${this.nodes.map((node) => node.name).join(", ")}
29+
Delay: ${this.config.delayMin}ms - ${this.config.delayMax}ms
30+
Jitter: ${this.config.jitterMin}ms - ${this.config.jitterMax}ms
31+
Loss: ${this.config.lossMin}% - ${this.config.lossMax}%
32+
Interval: ${this.config.interval}ms`);
33+
34+
validateContainers(this.nodes);
35+
this.clearAll();
36+
37+
this.interval = setInterval(() => {
38+
for (const node of this.nodes) {
39+
this.applyToNode(node);
40+
}
41+
}, this.config.interval);
42+
43+
return Promise.resolve();
44+
}
45+
46+
private applyToNode(node: DockerContainer) {
47+
const { delayMin, delayMax, jitterMin, jitterMax, lossMin, lossMax } =
48+
this.config;
49+
const delay = Math.floor(delayMin + Math.random() * (delayMax - delayMin));
50+
const jitter = Math.floor(
51+
jitterMin + Math.random() * (jitterMax - jitterMin),
52+
);
53+
const loss = lossMin + Math.random() * (lossMax - lossMin);
54+
55+
try {
56+
node.addJitter(delay, jitter);
57+
node.addLoss(loss);
58+
} catch (err) {
59+
console.warn(`[chaos] Error applying netem on ${node.name}:`, err);
60+
}
61+
}
62+
63+
clearAll() {
64+
for (const node of this.nodes) {
65+
try {
66+
node.clearLatency();
67+
} catch (err) {
68+
console.warn(`[chaos] Error clearing latency on ${node.name}:`, err);
69+
}
70+
}
71+
}
72+
73+
stop(): Promise<void> {
74+
if (this.interval) {
75+
clearInterval(this.interval);
76+
}
77+
78+
this.clearAll();
79+
80+
return Promise.resolve();
81+
}
82+
}
83+
84+
const validateContainers = (allNodes: DockerContainer[]) => {
85+
for (const node of allNodes) {
86+
try {
87+
// Test if container exists by trying to get its IP
88+
if (!node.ip || !node.veth) {
89+
throw new Error(`Container ${node.name} has no IP address`);
90+
}
91+
} catch {
92+
throw new Error(
93+
`Docker container ${node.name} is not running. Network chaos requires local multinode setup (./dev/up).`,
94+
);
95+
}
96+
}
97+
};

chaos/provider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { type WorkerManager } from "@workers/manager";
2+
3+
export interface ChaosProvider {
4+
start(workers: WorkerManager): Promise<void>;
5+
stop(): Promise<void>;
6+
}

chaos/streams.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { ChaosProvider } from "@chaos/provider";
2+
import { typeofStream } from "@workers/main";
3+
import type { WorkerManager } from "@workers/manager";
4+
5+
export class StreamsChaos implements ChaosProvider {
6+
workers?: WorkerManager;
7+
start(workers: WorkerManager) {
8+
console.log("Starting StreamsChaos");
9+
this.workers = workers;
10+
for (const worker of workers.getAll()) {
11+
worker.worker.startStream(typeofStream.Message);
12+
}
13+
14+
return Promise.resolve();
15+
}
16+
17+
stop() {
18+
if (this.workers) {
19+
for (const worker of this.workers.getAll()) {
20+
worker.worker.stopStreams();
21+
}
22+
}
23+
24+
return Promise.resolve();
25+
}
26+
}

0 commit comments

Comments
 (0)