Skip to content

Commit a44ea29

Browse files
committed
Refactor chaos
1 parent 3ec5323 commit a44ea29

File tree

15 files changed

+1171
-1134
lines changed

15 files changed

+1171
-1134
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: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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+
36+
this.interval = setInterval(() => {
37+
for (const node of this.nodes) {
38+
this.applyToNode(node);
39+
}
40+
}, this.config.interval);
41+
42+
return Promise.resolve();
43+
}
44+
45+
private applyToNode(node: DockerContainer) {
46+
const { delayMin, delayMax, jitterMin, jitterMax, lossMin, lossMax } =
47+
this.config;
48+
const delay = Math.floor(delayMin + Math.random() * (delayMax - delayMin));
49+
const jitter = Math.floor(
50+
jitterMin + Math.random() * (jitterMax - jitterMin),
51+
);
52+
const loss = lossMin + Math.random() * (lossMax - lossMin);
53+
54+
try {
55+
node.addJitter(delay, jitter);
56+
node.addLoss(loss);
57+
} catch (err) {
58+
console.warn(`[chaos] Error applying netem on ${node.name}:`, err);
59+
}
60+
}
61+
62+
stop(): Promise<void> {
63+
if (this.interval) {
64+
clearInterval(this.interval);
65+
}
66+
67+
for (const node of this.nodes) {
68+
try {
69+
node.clearLatency();
70+
} catch (err) {
71+
console.warn(`[chaos] Error clearing latency on ${node.name}:`, err);
72+
}
73+
}
74+
75+
return Promise.resolve();
76+
}
77+
}
78+
79+
const validateContainers = (allNodes: DockerContainer[]) => {
80+
for (const node of allNodes) {
81+
try {
82+
// Test if container exists by trying to get its IP
83+
if (!node.ip || !node.veth) {
84+
throw new Error(`Container ${node.name} has no IP address`);
85+
}
86+
} catch {
87+
throw new Error(
88+
`Docker container ${node.name} is not running. Network chaos requires local multinode setup (./dev/up).`,
89+
);
90+
}
91+
}
92+
};

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)