- * @returns The function to execute.
- */
-export const once = (
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- fn: (...args: any[]) => void,
- context: unknown
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
-): ((...args: any[]) => void) => {
- let called = false
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- return function (...args: any[]): void {
- if (!called) {
- called = true
- fn.apply(context, args)
- called = false
+ * @returns The wrapped function.
+ * @internal
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const once = <T, A extends any[], R>(
+ fn: (...args: A) => R,
+ context: T
+): ((...args: A) => R) => {
+ let result: R
+ return (...args: A) => {
+ if (fn != null) {
+ result = fn.apply<T, A, R>(context, args)
+ ;(fn as unknown as undefined) = (context as unknown as undefined) =
+ undefined
+ }
+ return result
+ }
+}
+
+export const getWorkerChoiceStrategyRetries = <
+ Worker extends IWorker,
+ Data,
+ Response
+>(
+ pool: IPool<Worker, Data, Response>,
+ opts?: WorkerChoiceStrategyOptions
+ ): number => {
+ return (
+ pool.info.maxSize +
+ Object.keys(opts?.weights ?? getDefaultWeights(pool.info.maxSize)).length
+ )
+}
+
+const clone = <T>(object: T): T => {
+ return structuredClone<T>(object)
+}
+
+export const buildWorkerChoiceStrategyOptions = <
+ Worker extends IWorker,
+ Data,
+ Response
+>(
+ pool: IPool<Worker, Data, Response>,
+ opts?: WorkerChoiceStrategyOptions
+ ): WorkerChoiceStrategyOptions => {
+ opts = clone(opts ?? {})
+ opts.weights = opts?.weights ?? getDefaultWeights(pool.info.maxSize)
+ return {
+ ...{
+ runTime: { median: false },
+ waitTime: { median: false },
+ elu: { median: false }
+ },
+ ...opts
+ }
+}
+
+const getDefaultWeights = (
+ poolMaxSize: number,
+ defaultWorkerWeight?: number
+): Record<number, number> => {
+ defaultWorkerWeight = defaultWorkerWeight ?? getDefaultWorkerWeight()
+ const weights: Record<number, number> = {}
+ for (let workerNodeKey = 0; workerNodeKey < poolMaxSize; workerNodeKey++) {
+ weights[workerNodeKey] = defaultWorkerWeight
+ }
+ return weights
+}
+
+const getDefaultWorkerWeight = (): number => {
+ const cpuSpeed = randomInt(500, 2500)
+ let cpusCycleTimeWeight = 0
+ for (const cpu of cpus()) {
+ if (cpu.speed == null || cpu.speed === 0) {
+ cpu.speed =
+ cpus().find(cpu => cpu.speed != null && cpu.speed !== 0)?.speed ??
+ cpuSpeed