{"version":3,"sources":["../../src/service/ProcessBalancer.ts"],"sourcesContent":["import os from 'os';\nimport { log } from '../utils/index.js';\n\ninterface balancerConfig {\n    minSlaves?: number;\n    maxSlaves?: number;\n    queueScaleUpThreshold?: number;\n    queueScaleDownThreshold?: number;\n    maxIdleRateThreshold?: number;\n    minIdleRateThreshold?: number;\n    cpuThreshold?: number;\n    memThreshold?: number;\n    checkInterval?: number;\n    checkSlaves: (() => { idleCount: number | undefined, workingCount:number | undefined }) | undefined;\n    checkQueueSize: (() => number) | undefined;\n    addSlave: (() => void) | undefined;\n    removeSlave: (() => void) | undefined;\n}\n\nclass ProcessBalancer {\n    private prevQueueSize: number = 0;\n    private interval: NodeJS.Timeout | undefined;\n    private queueScaleUpThreshold: number;\n    private queueScaleDownThreshold: number;\n    private maxIdleRateThreshold: number;\n    private minIdleRateThreshold: number;\n    private cpuThreshold: number;\n    private memThreshold: number;\n    private checkInterval: number;\n    private checkQueueSize: (() => number) | undefined;\n    private checkSlaves: (() => { idleCount: number | undefined, workingCount:number | undefined }) | undefined;\n    private addSlave: (() => void) | undefined;\n    private removeSlave: (() => void) | undefined;\n\n    constructor(config: balancerConfig) {\n        // if there is at least 3 request in the queue, allow to scale up\n        this.queueScaleUpThreshold = config.queueScaleUpThreshold || 3;\n        // if there is at most one request on the queue, allow to scale down\n        this.queueScaleDownThreshold = config.queueScaleDownThreshold || 0;\n        // if we are using a lot of resources, don't scale up\n        this.cpuThreshold = config.cpuThreshold || 90;\n        this.memThreshold = config.memThreshold || 90;\n        // if 80 percent of all slaves are idle, don't allow to make more\n        this.maxIdleRateThreshold = config.maxIdleRateThreshold || 0.8\n        // if 10 percent of all slaves are idle, don't remove any\n        this.minIdleRateThreshold = config.minIdleRateThreshold || 0.1\n        // how often do we check\n        this.checkInterval = config.checkInterval || 500;\n        // function need to check\n        this.checkQueueSize = config.checkQueueSize;\n        this.checkSlaves = config.checkSlaves;\n        this.addSlave = config.addSlave;\n        this.removeSlave = config.removeSlave;\n        // check if we got all the need callbacks\n        this.checkRequiredFunctions();\n        // Initialize monitoring\n        this.interval = this.startMonitoring();\n    }\n\n    private getCpuUsage(): number {\n        let cpus = os.cpus();\n        let totalLoad = cpus.reduce((acc, cpu) => {\n            let total = Object.values(cpu.times).reduce((t, v) => t + v, 0);\n            return acc + (cpu.times.user / total) * 100;\n        }, 0);\n        return totalLoad / cpus.length;\n    }\n\n    private getMemoryUsage(): number {\n        return ((os.totalmem() - os.freemem()) / os.totalmem()) * 100;\n    }\n\n    private monitorSystem(): void {\n        if(this.checkQueueSize === undefined) throw Error('checkQueueSize is undefined');\n        if(this.checkSlaves === undefined) throw Error('checkSlaves is undefined');\n\n        this.checkRequiredFunctions();\n        const queueSize = this.checkQueueSize();\n        const { idleCount, workingCount } = this.checkSlaves();\n        if(idleCount === undefined || workingCount === undefined)\n            throw new Error('checkSlaves function returned idleCount or workingCount with value of undefined')\n        const idleRate = idleCount / workingCount + idleCount\n        const queueGrowth = queueSize - this.prevQueueSize;\n        this.prevQueueSize = queueSize;\n        const avgCpu = this.getCpuUsage();\n        const avgMem = this.getMemoryUsage();\n\n        /*\n        log(`[ProcessBalancer]\n            Queue Size: ${queueSize},\n            Growth: ${queueGrowth},\n            CPU: ${avgCpu.toFixed(2)}%,\n            MEM: ${avgMem.toFixed(2)}%,\n            Idle Slaves: ${idleCount},\n            Working Slaves: ${workingCount},\n            Total Slaves: ${idleCount + workingCount},\n            Idle Ratio: ${(idleCount / workingCount).toFixed(2)}`\n           );\n           */\n\n           if (\n               // if the queue size is passed a threshold: 3\n               queueSize > this.queueScaleUpThreshold &&\n               // and it is growing\n               queueGrowth > 0 &&\n               // and the average CPU and MEM usage is below 90%\n               avgCpu < this.cpuThreshold &&\n               avgMem < this.memThreshold &&\n               // and the ratio of idle slaves to working slaves is greater than than threshold\n               idleRate < this.maxIdleRateThreshold\n           ){\n               log('Scaling up, adding a node');\n               //@ts-ignore\n               this.addSlave();\n           }\n           if ( // if the queue size is less than or equal to the threshold\n               queueSize <= this.queueScaleDownThreshold &&\n               // if there is at least one\n               idleCount > 1 &&\n               // if the queue size is degreesing or not growing\n               queueGrowth <= 0 &&\n               // if the idle rate is low\n               idleRate > this.minIdleRateThreshold\n              ){\n                  log('Scaling down, removing a node');\n                  //@ts-ignore\n                  this.removeSlave();\n              }\n    }\n\n    private startMonitoring(): NodeJS.Timeout {\n        return setInterval(() => {\n            this.monitorSystem();\n        }, this.checkInterval);\n    }\n\n    private checkRequiredFunctions(): void {\n        if (this.checkQueueSize === undefined)\n            throw new Error('Missing required function checkQueueSize in config');\n        if (this.checkSlaves === undefined)\n            throw new Error('Missing required function checkSlaves in config');\n        if (this.addSlave === undefined)\n            throw new Error('Missing required function addSlave in config');\n        if (this.removeSlave === undefined)\n            throw new Error('Missing required function removeSlave in config');\n    }\n\n    public exit(): void {\n        // end monitoring\n        clearInterval(this.interval);\n    }\n}\n\nexport default ProcessBalancer;\n"],"mappings":"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","names":["os"]}