Set jitter

This commit is contained in:
Andras Schmelczer 2025-02-25 22:54:04 +00:00
parent 0b5f3f3921
commit f8b6718a22
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
2 changed files with 49 additions and 27 deletions

View file

@ -4,7 +4,7 @@ import { assert } from "../utils/assert";
import type { RelativePath, SyncSettings } from "sync-client"; import type { RelativePath, SyncSettings } from "sync-client";
import { LogLevel } from "sync-client"; import { LogLevel } from "sync-client";
import { MockClient } from "./mock-client"; import { MockClient } from "./mock-client";
import chalk from "chalk"; import { sleep } from "../utils/sleep";
export class MockAgent extends MockClient { export class MockAgent extends MockClient {
private readonly writtenContents: string[] = []; private readonly writtenContents: string[] = [];
@ -14,8 +14,8 @@ export class MockAgent extends MockClient {
public constructor( public constructor(
initialSettings: Partial<SyncSettings>, initialSettings: Partial<SyncSettings>,
public readonly name: string, public readonly name: string,
private readonly color: string, private readonly doDeletes: boolean,
private readonly doDeletes: boolean private readonly jitterScaleInSeconds: number
) { ) {
super(initialSettings); super(initialSettings);
} }
@ -23,11 +23,18 @@ export class MockAgent extends MockClient {
public async init(): Promise<void> { public async init(): Promise<void> {
await super.init(); await super.init();
this.client.logger.addOnMessageListener((message) => { this.client.fetchImplementation = async (
const formatted = chalk.hex(this.color)( input: string | URL | globalThis.Request,
`[${this.name}] ${message.timestamp.toISOString()} ${message.level} ${message.message}` init?: RequestInit
); ): Promise<Response> => {
await sleep(Math.random() * this.jitterScaleInSeconds * 1000);
const response = await fetch(input, init);
await sleep(Math.random() * this.jitterScaleInSeconds * 1000);
return response;
};
this.client.logger.addOnMessageListener((message) => {
const formatted = `[${this.name}] ${message.timestamp.toISOString()} ${message.level} ${message.message}`;
switch (message.level) { switch (message.level) {
case LogLevel.ERROR: case LogLevel.ERROR:
console.error(formatted); console.error(formatted);

View file

@ -7,16 +7,17 @@ async function runTest({
agentCount, agentCount,
concurrency, concurrency,
iterations, iterations,
doDeletes doDeletes,
jitterScaleInSeconds
}: { }: {
agentCount: number; agentCount: number;
concurrency: number; concurrency: number;
iterations: number; iterations: number;
doDeletes: boolean; doDeletes: boolean;
jitterScaleInSeconds: number;
}): Promise<void> { }): Promise<void> {
console.info( const settings = `with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}, jitterScaleInSeconds ${jitterScaleInSeconds}`;
`Running test with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}` console.info(`Running test ${settings}`);
);
const initialSettings: Partial<SyncSettings> = { const initialSettings: Partial<SyncSettings> = {
isSyncEnabled: true, isSyncEnabled: true,
@ -29,7 +30,12 @@ async function runTest({
const clients: MockAgent[] = []; const clients: MockAgent[] = [];
for (let i = 0; i < agentCount; i++) { for (let i = 0; i < agentCount; i++) {
clients.push( clients.push(
new MockAgent(initialSettings, `agent-${i}`, "#ff0000", doDeletes) new MockAgent(
initialSettings,
`agent-${i}`,
doDeletes,
jitterScaleInSeconds
)
); );
} }
@ -42,8 +48,15 @@ async function runTest({
await sleep(100); await sleep(100);
} }
console.info("Stopping agents");
// Each agent can have unpushed changes which might conflict with eachother so each has to resolve the conflicts & push, and
for (const client of clients) {
await client.finish();
}
// then we need a second pass to ensure that all agents pull the same state.
for (const client of clients) { for (const client of clients) {
// todo: make it less hacky
await client.finish(); await client.finish();
} }
@ -65,33 +78,35 @@ async function runTest({
console.info(`Content check for ${client.name} passed`); console.info(`Content check for ${client.name} passed`);
}); });
console.info( console.info(`Test passed with ${settings}`);
`Test passed with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}`
);
} catch (err) { } catch (err) {
console.error( console.error(`Test failed with ${settings}`);
`Test failed with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}`
);
throw err; throw err;
} }
} }
async function runTests(): Promise<void> { async function runTests(): Promise<void> {
const agentCounts = [2, 10]; const agentCounts = [2, 10];
const jitterScaleInSeconds = [0.5, 3, 0];
const concurrencies = [1, 16]; const concurrencies = [1, 16];
const iterations = [50, 300]; const iterations = [50, 300];
const doDeletes = [false, true]; const doDeletes = [false, true];
for (const agentCount of agentCounts) { for (const agentCount of agentCounts) {
for (const concurrency of concurrencies) { for (const concurrency of concurrencies) {
for (const iteration of iterations) { for (const jitter of jitterScaleInSeconds) {
for (const deleteFiles of doDeletes) { for (const iteration of iterations) {
await runTest({ for (const deleteFiles of doDeletes) {
agentCount, while (true) {
concurrency, await runTest({
iterations: iteration, agentCount,
doDeletes: deleteFiles concurrency,
}); iterations: iteration,
doDeletes: deleteFiles,
jitterScaleInSeconds: jitter
});
}
}
} }
} }
} }