-import { randomBytes, randomInt, randomUUID } from 'node:crypto';
+import { randomBytes, randomInt, randomUUID, webcrypto } from 'node:crypto';
import { inspect } from 'node:util';
import {
/**
* Generates a cryptographically secure random number in the [0,1[ range
*
- * @returns
+ * @returns A number in the [0,1[ range
*/
export const secureRandom = (): number => {
- return randomBytes(4).readUInt32LE() / 0x100000000;
+ return webcrypto.getRandomValues(new Uint32Array(1))[0] / 0x100000000;
};
export const JSONStringifyWithMapSupport = (
import { WorkerAbstract } from './WorkerAbstract';
import type { WorkerData, WorkerOptions } from './WorkerTypes';
-import { sleep } from './WorkerUtils';
+import { randomizeDelay, sleep } from './WorkerUtils';
export class WorkerDynamicPool extends WorkerAbstract<WorkerData> {
private readonly pool: DynamicThreadPool<WorkerData>;
await this.pool.execute(elementData);
// Start element sequentially to optimize memory at startup
this.workerOptions.elementStartDelay! > 0 &&
- (await sleep(this.workerOptions.elementStartDelay!));
+ (await sleep(randomizeDelay(this.workerOptions.elementStartDelay!)));
}
}
type WorkerSetElement,
WorkerSetEvents,
} from './WorkerTypes';
-import { sleep } from './WorkerUtils';
+import { randomizeDelay, sleep } from './WorkerUtils';
export class WorkerSet extends WorkerAbstract<WorkerData> {
public readonly emitter!: EventEmitter;
public async start(): Promise<void> {
this.addWorkerSetElement();
// Add worker set element sequentially to optimize memory at startup
- this.workerOptions.workerStartDelay! > 0 && (await sleep(this.workerOptions.workerStartDelay!));
+ this.workerOptions.workerStartDelay! > 0 &&
+ (await sleep(randomizeDelay(this.workerOptions.workerStartDelay!)));
this.started = true;
}
++workerSetElement.numberOfWorkerElements;
// Add element sequentially to optimize memory at startup
if (this.workerOptions.elementStartDelay! > 0) {
- await sleep(this.workerOptions.elementStartDelay!);
+ await sleep(randomizeDelay(this.workerOptions.elementStartDelay!));
}
}
chosenWorkerSetElement = this.addWorkerSetElement();
// Add worker set element sequentially to optimize memory at startup
this.workerOptions.workerStartDelay! > 0 &&
- (await sleep(this.workerOptions.workerStartDelay!));
+ (await sleep(randomizeDelay(this.workerOptions.workerStartDelay!)));
}
return chosenWorkerSetElement;
}
import { WorkerAbstract } from './WorkerAbstract';
import type { WorkerData, WorkerOptions } from './WorkerTypes';
-import { sleep } from './WorkerUtils';
+import { randomizeDelay, sleep } from './WorkerUtils';
export class WorkerStaticPool extends WorkerAbstract<WorkerData> {
private readonly pool: FixedThreadPool<WorkerData>;
await this.pool.execute(elementData);
// Start element sequentially to optimize memory at startup
this.workerOptions.elementStartDelay! > 0 &&
- (await sleep(this.workerOptions.elementStartDelay!));
+ (await sleep(randomizeDelay(this.workerOptions.elementStartDelay!)));
}
}
+import { webcrypto } from 'node:crypto';
+
import chalk from 'chalk';
export const sleep = async (milliSeconds: number): Promise<NodeJS.Timeout> => {
export const defaultErrorHandler = (error: Error): void => {
console.error(chalk.red('Worker errored: '), error);
};
+
+export const randomizeDelay = (delay: number): number => {
+ const random = secureRandom();
+ const sign = random < 0.5 ? -1 : 1;
+ const randomSum = delay * 0.2 * random; // 0-20% of the delay
+ return delay + sign * randomSum;
+};
+
+/**
+ * Generates a cryptographically secure random number in the [0,1[ range
+ *
+ * @returns A number in the [0,1[ range
+ * @internal
+ */
+const secureRandom = (): number => {
+ return webcrypto.getRandomValues(new Uint32Array(1))[0] / 0x100000000;
+};