slavery-js
Version:
A simple clustering app that allows you to scale an application on multiple thread, containers or machines
1 lines • 15.3 kB
Source Map (JSON)
{"version":3,"sources":["../../src/nodes/NodeManager.ts"],"sourcesContent":["import Cluster from '../cluster/index.js';\nimport Network, { Connection } from '../network/index.js';\nimport Node from './Node.js';\nimport { ServiceAddress, Stash } from '../service/index.js';\nimport { Pool, await_interval, log } from '../utils/index.js';\n\n/* this class is created to manage the nodes socket conenctions on a service,\n * it will handle new conenction fron node, and will remove them when they are disconnected\n * this class provide an interface for other classes to interact with all the nodes\n * if a class wants to run a function on each difrent node,\n * if a class wants to broadcast a message to all the nodes\n * if a class want to get the next idle node,\n * or the number of node connected to the network\n * or the number of node that have been disconnected\n * or which are currently busy, or idle, etc\n */\n\ntype NodeManagerOptions = {\n // the number of processes that will be started on each node\n number_of_nodes?: number,\n max_number_of_nodes?: number,\n min_number_of_node?: number,\n // the number of request that have to be in queue before increasing the number of processes\n increase_node_at_requests?: number,\n // the number of node that have to be idle before decreasing the number of processes\n decrease_node_at_idles?: number,\n // service address for nodes\n service_address?: ServiceAddress[],\n // you can pass the stash object to the node manager\n stash?: Stash,\n // timeout\n timeout?: number,\n}\n\ntype NodeManagerParameters = {\n // name of the service\n name: string,\n // the host and port of the service\n host: string,\n port: number,\n options?: NodeManagerOptions,\n}\n\nclass NodeManager {\n private name: string;\n private network: Network;\n //private heartBeat: number = 1000;\n private nodes: Pool<Node> = new Pool();\n private options: NodeManagerOptions;\n private cluster = new Cluster({});\n private services: ServiceAddress[] = [];\n private stash: Stash | null;\n\n\n constructor({ name, host, port, options }: NodeManagerParameters) {\n this.name = name;\n this.options = options || {};\n this.network = new Network({\n name: this.name + '_node_manager',\n options: {\n timeout: this.options.timeout || 10000,\n }\n });\n // create server\n this.network.createServer( this.name + '_node_manager', host, port);\n // handle when new node is connected\n this.network.onNodeConnection(this.handleNewNode.bind(this));\n // handle when a node is disconnected\n this.network.onNodeDisconnect(this.handleNodeDisconnect.bind(this));\n // set the stash\n this.stash = options?.stash || null;\n }\n\n private async handleNewNode(connection: Connection) {\n /* this function is called when a new node is connected to the master */\n // create a new node\n let node = new Node({\n mode: 'server',\n connection,\n network: this.network,\n services: this.services,\n statusChangeCallback: this.handleStatusChange.bind(this),\n stashSetFunction: async (key: string, value: any) => await this.stash?.set(key, value),\n stashGetFunction: async (key: string) => await this.stash?.get(key),\n });\n // sends the service list to the client node\n let res = await node.start();\n // get the id of the node\n let id = node.getId();\n if(id === undefined) throw new Error('node id is undefined');\n // add to the pool\n this.nodes.add(id, node);\n // add to the enabled pool\n this.setIdle(id);\n }\n\n\n private handleNodeDisconnect(connection: Connection) {\n // get the node id\n let id = connection.getId();\n if(id === undefined) throw new Error('node id is undefined');\n // remove the node from the pool\n this.nodes.remove(id);\n }\n\n private handleStatusChange(status: string, node: Node) {\n // if the node is idle add it to the enabled pool\n // if the node is busy add it to the disabled pool\n if(!status) throw new Error('status is undefined');\n let id = node.getId();\n if(id === undefined) throw new Error('node id is undefined');\n if(node.isIdle() || node.isError())\n this.setIdle(id);\n else if(node.isBusy())\n this.setBusy(id);\n else\n throw new Error('invalid node status');\n }\n\n public async getIdle(node_id: string = '') : Promise<Node> {\n /* this function return a node that is idle */\n if(node_id !== '') { // we are looking for a specific node on the pool\n let node = this.getNode(node_id);\n // await for seelcted node to be idle\n await await_interval({\n condition: () => node.isIdle(),\n timeout: this.options.timeout || 3600000, // one hour\n }).catch(() => {\n throw new Error(`timeout of one hour, node ${node_id} is not idle`);\n });\n return node;\n }\n // check if there are nodes in the pool\n if(this.nodes.isEmpty())\n log('[node manager] (WARNING) no nodes found');\n // await until we get a node which is idle\n // 0 will make it wait for every for a idle node\n await await_interval({\n condition: () => this.nodes.hasEnabled(),\n timeout: 0, // forever\n }).catch(() => { throw new Error('timeout of 10 seconds, no idle node found') });\n //log('[node manager] got idle node');\n // get the next node\n let node = this.nodes.pop();\n if(node === null) throw new Error('node is null');\n // return the node\n return node\n }\n\n public getBusy(){\n // returned a single busy node\n return this.nodes.getDisabled().pop();\n }\n\n public getIdleNodes() : Node[] {\n /* this function return the nodes which are idle */\n return this.nodes.getEnabledObjects();\n }\n\n public getBusyNodes() : Node[] {\n /* this function return the nodes which are busy */\n return this.nodes.getDisabledObjects();\n }\n\n public async forEach(callback: (node: Node) => void) {\n let nodes = this.nodes.toArray();\n // for each node, make a promise\n let promises = nodes.map(async (node: Node) => {\n try {\n if(node.isBusy()) await node.toFinish();\n return callback(node);\n } catch (error) {\n log(`[NodeManager][forEach] Error: ${error}`);\n throw error;\n }\n });\n // wait for all the promises to resolve\n return Promise.all(promises).catch(error => {\n log(`[NodeManager][forEach] Error in Promise.all: ${error}`);\n throw error;\n });\n }\n\n public async setServices(services: ServiceAddress[]) {\n // set the services to all the nodes\n this.services = services;\n if(this.nodes.size() > 0)\n await this.broadcast( async (node: Node) => \n await node.setServices(services)\n );\n }\n\n public async spawnNodes(name: string = '', count: number = 1, metadata: any = {}) {\n /* spawn new nodes */\n if(name === '') name = 'node_' + this.name;\n this.cluster.spawn(name, {\n numberOfSpawns: count,\n metadata: metadata\n });\n }\n\n public async killNode(nodeId: string = '') {\n // this function will get an idle node fom the pool\n if(this.nodes.isEmpty()) return false\n // get an idle node\n let node = (nodeId === '')?\n this.nodes.removeOne() :\n this.nodes.remove(nodeId);\n if(node === null || node === undefined)\n throw new Error('Node sentenced to death could not be found');\n // and exit it\n await node.exit();\n }\n\n public async killNodes(nodesId: string[]=[]) {\n /* kill nodes */\n for(let nodeId of nodesId)\n await this.killNode(nodeId);\n }\n\n public getIdleCount(){\n // return the number of idle nodes\n return this.nodes.getEnabledCount();\n }\n\n public getBusyCount(){\n // return the number of busy nodes\n return this.nodes.getDisabledCount();\n }\n\n public getNodes() {\n // get all nodes\n return this.nodes.toArray();\n }\n\n public nextNode() {\n return this.nodes.next();\n }\n\n public getNodeCount() {\n return this.nodes.size();\n }\n\n public getNode(nodeId: string) {\n // get a node by its id\n let node = this.nodes.get(nodeId);\n if(node === null) throw new Error(`[node manager] (ERROR) selected node ${nodeId} not found`);\n return node;\n }\n\n public getListeners() {\n if(this.network === undefined) throw new Error('network is undefined');\n return this.network.getRegisteredListeners();\n }\n\n public async numberOfNodesConnected(count: number) {\n let timeout = this.options.timeout || 10 * 1000; // 10 seconds\n await await_interval({\n condition: () => this.nodes.size() >= count,\n timeout: timeout,\n }).catch(() => { \n throw new Error(`timeout of ${timeout} seconds, only ${this.nodes.size()} nodes connected, expected ${count}`) \n });\n return true;\n }\n\n\n public async exit() {\n // close all the nodes\n return this.broadcast(\n async (node: Node) => await node.exit()\n );\n }\n\n private async broadcast(callback: (node: Node) => any) {\n // get all the nodes\n let nodes = this.nodes.toArray();\n // for each node, make a promise\n let promises = nodes.map(\n async (node: Node) => {\n try {\n return await callback(node);\n } catch (error) {\n log(`[NodeManager][broadcast] Error for node ${node.getId()}: ${error}`);\n throw error;\n }\n }\n );\n // wait for all the promises to resolve\n return Promise.all(promises).catch(error => {\n log(`[NodeManager][broadcast] Error in Promise.all: ${error}`);\n throw error;\n });\n }\n\n private setIdle = (NodeId: string) => this.nodes.enable(NodeId);\n\n private setBusy = (NodeId: string) => this.nodes.disable(NodeId);\n\n /* synonims */\n public addNode = this.spawnNodes\n public removeNode = this.killNodes\n public getNumberOfNodes = this.getNodeCount;\n\n}\n\nexport default NodeManager;\n"],"mappings":"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","names":["Cluster","Network","Node","node"]}