{"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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAoB;AACpB,qBAAoC;AACpC,kBAAiB;AAEjB,mBAA0C;AAuC1C,MAAM,YAAY;AAAA,EAWd,YAAY,EAAE,MAAM,MAAM,MAAM,QAAQ,GAA0B;AAVlE,wBAAQ;AACR,wBAAQ;AAER;AAAA,wBAAQ,SAAoB,IAAI,kBAAK;AACrC,wBAAQ;AACR,wBAAQ,WAAU,IAAI,eAAAA,QAAQ,CAAC,CAAC;AAChC,wBAAQ,YAA6B,CAAC;AACtC,wBAAQ;AAoPV,wBAAQ,WAAU,CAAC,WAAmB,KAAK,MAAM,OAAO,MAAM;AAE9D,wBAAQ,WAAU,CAAC,WAAmB,KAAK,MAAM,QAAQ,MAAM;AAG/D;AAAA,wBAAO,WAAU,KAAK;AACtB,wBAAO,cAAa,KAAK;AACzB,wBAAO,oBAAmB,KAAK;AAvPzB,SAAK,OAAO;AACZ,SAAK,UAAU,WAAW,CAAC;AAC3B,SAAK,UAAU,IAAI,eAAAC,QAAQ;AAAA,MACvB,MAAM,KAAK,OAAO;AAAA,MAClB,SAAS;AAAA,QACL,SAAS,KAAK,QAAQ,WAAW;AAAA,MACrC;AAAA,IACJ,CAAC;AAED,SAAK,QAAQ,aAAc,KAAK,OAAO,iBAAiB,MAAM,IAAI;AAElE,SAAK,QAAQ,iBAAiB,KAAK,cAAc,KAAK,IAAI,CAAC;AAE3D,SAAK,QAAQ,iBAAiB,KAAK,qBAAqB,KAAK,IAAI,CAAC;AAElE,SAAK,QAAQ,SAAS,SAAS;AAAA,EACrC;AAAA,EAEA,MAAc,cAAc,YAAwB;AAGhD,QAAI,OAAO,IAAI,YAAAC,QAAK;AAAA,MAChB,MAAM;AAAA,MACN;AAAA,MACA,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,MACf,sBAAsB,KAAK,mBAAmB,KAAK,IAAI;AAAA,MACvD,kBAAkB,OAAO,KAAa,UAAe,MAAM,KAAK,OAAO,IAAI,KAAK,KAAK;AAAA,MACrF,kBAAkB,OAAO,QAAgB,MAAM,KAAK,OAAO,IAAI,GAAG;AAAA,IACtE,CAAC;AAED,QAAI,MAAM,MAAM,KAAK,MAAM;AAE3B,QAAI,KAAK,KAAK,MAAM;AACpB,QAAG,OAAO,OAAW,OAAM,IAAI,MAAM,sBAAsB;AAE3D,SAAK,MAAM,IAAI,IAAI,IAAI;AAEvB,SAAK,QAAQ,EAAE;AAAA,EACnB;AAAA,EAGQ,qBAAqB,YAAwB;AAEjD,QAAI,KAAK,WAAW,MAAM;AAC1B,QAAG,OAAO,OAAW,OAAM,IAAI,MAAM,sBAAsB;AAE3D,SAAK,MAAM,OAAO,EAAE;AAAA,EACxB;AAAA,EAEQ,mBAAmB,QAAgB,MAAY;AAGnD,QAAG,CAAC,OAAQ,OAAM,IAAI,MAAM,qBAAqB;AACjD,QAAI,KAAK,KAAK,MAAM;AACpB,QAAG,OAAO,OAAW,OAAM,IAAI,MAAM,sBAAsB;AAC3D,QAAG,KAAK,OAAO,KAAK,KAAK,QAAQ;AAC7B,WAAK,QAAQ,EAAE;AAAA,aACX,KAAK,OAAO;AAChB,WAAK,QAAQ,EAAE;AAAA;AAEf,YAAM,IAAI,MAAM,qBAAqB;AAAA,EAC7C;AAAA,EAEA,MAAa,QAAQ,UAAkB,IAAoB;AAEvD,QAAG,YAAY,IAAI;AACf,UAAIC,QAAO,KAAK,QAAQ,OAAO;AAE/B,gBAAM,6BAAe;AAAA,QACjB,WAAW,MAAMA,MAAK,OAAO;AAAA,QAC3B,SAAS,KAAK,QAAQ,WAAW;AAAA;AAAA,MACvC,CAAC,EAAE,MAAM,MAAM;AACX,cAAM,IAAI,MAAM,6BAA6B,OAAO,cAAc;AAAA,MACtE,CAAC;AACD,aAAOA;AAAA,IACX;AAEA,QAAG,KAAK,MAAM,QAAQ;AAClB,4BAAI,yCAAyC;AAGjD,cAAM,6BAAe;AAAA,MACjB,WAAW,MAAM,KAAK,MAAM,WAAW;AAAA,MACrC,SAAS;AAAA;AAAA,IACf,CAAC,EAAE,MAAM,MAAM;AAAE,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAAE,CAAC;AAG/E,QAAI,OAAO,KAAK,MAAM,IAAI;AAC1B,QAAG,SAAS,KAAM,OAAM,IAAI,MAAM,cAAc;AAEhD,WAAO;AAAA,EACX;AAAA,EAEO,UAAS;AAEZ,WAAO,KAAK,MAAM,YAAY,EAAE,IAAI;AAAA,EACxC;AAAA,EAEO,eAAwB;AAE3B,WAAO,KAAK,MAAM,kBAAkB;AAAA,EACxC;AAAA,EAEO,eAAwB;AAE3B,WAAO,KAAK,MAAM,mBAAmB;AAAA,EACzC;AAAA,EAEA,MAAa,QAAQ,UAAgC;AACjD,QAAI,QAAQ,KAAK,MAAM,QAAQ;AAE/B,QAAI,WAAW,MAAM,IAAI,OAAO,SAAe;AAC3C,UAAI;AACA,YAAG,KAAK,OAAO,EAAG,OAAM,KAAK,SAAS;AACtC,eAAO,SAAS,IAAI;AAAA,MACxB,SAAS,OAAO;AACZ,8BAAI,iCAAiC,KAAK,EAAE;AAC5C,cAAM;AAAA,MACV;AAAA,IACJ,CAAC;AAED,WAAO,QAAQ,IAAI,QAAQ,EAAE,MAAM,WAAS;AACxC,4BAAI,gDAAgD,KAAK,EAAE;AAC3D,YAAM;AAAA,IACV,CAAC;AAAA,EACL;AAAA,EAEA,MAAa,YAAY,UAA4B;AAEjD,SAAK,WAAW;AAChB,QAAG,KAAK,MAAM,KAAK,IAAI;AACnB,YAAM,KAAK;AAAA,QAAW,OAAO,SACzB,MAAM,KAAK,YAAY,QAAQ;AAAA,MACnC;AAAA,EACR;AAAA,EAEA,MAAa,WAAW,OAAe,IAAI,QAAgB,GAAG,WAAgB,CAAC,GAAG;AAE9E,QAAG,SAAS,GAAI,QAAO,UAAU,KAAK;AACtC,SAAK,QAAQ,MAAM,MAAM;AAAA,MACrB,gBAAgB;AAAA,MAChB;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEA,MAAa,SAAS,SAAiB,IAAI;AAEvC,QAAG,KAAK,MAAM,QAAQ,EAAG,QAAO;AAEhC,QAAI,OAAQ,WAAW,KACnB,KAAK,MAAM,UAAU,IACrB,KAAK,MAAM,OAAO,MAAM;AAC5B,QAAG,SAAS,QAAQ,SAAS;AACzB,YAAM,IAAI,MAAM,4CAA4C;AAEhE,UAAM,KAAK,KAAK;AAAA,EACpB;AAAA,EAEA,MAAa,UAAU,UAAkB,CAAC,GAAG;AAEzC,aAAQ,UAAU;AACd,YAAM,KAAK,SAAS,MAAM;AAAA,EAClC;AAAA,EAEO,eAAc;AAEnB,WAAO,KAAK,MAAM,gBAAgB;AAAA,EACpC;AAAA,EAEO,eAAc;AAEnB,WAAO,KAAK,MAAM,iBAAiB;AAAA,EACrC;AAAA,EAEO,WAAW;AAEd,WAAO,KAAK,MAAM,QAAQ;AAAA,EAC9B;AAAA,EAEO,WAAW;AACd,WAAO,KAAK,MAAM,KAAK;AAAA,EAC3B;AAAA,EAEO,eAAe;AACpB,WAAO,KAAK,MAAM,KAAK;AAAA,EACzB;AAAA,EAEO,QAAQ,QAAgB;AAE3B,QAAI,OAAO,KAAK,MAAM,IAAI,MAAM;AAChC,QAAG,SAAS,KAAM,OAAM,IAAI,MAAM,wCAAwC,MAAM,YAAY;AAC5F,WAAO;AAAA,EACX;AAAA,EAEO,eAAe;AAClB,QAAG,KAAK,YAAY,OAAW,OAAM,IAAI,MAAM,sBAAsB;AACrE,WAAO,KAAK,QAAQ,uBAAuB;AAAA,EAC/C;AAAA,EAEA,MAAa,uBAAuB,OAAe;AAC/C,QAAI,UAAU,KAAK,QAAQ,WAAW,KAAK;AAC3C,cAAM,6BAAe;AAAA,MACnB,WAAW,MAAM,KAAK,MAAM,KAAK,KAAK;AAAA,MACtC;AAAA,IACF,CAAC,EAAE,MAAM,MAAM;AACX,YAAM,IAAI,MAAM,cAAc,OAAO,kBAAkB,KAAK,MAAM,KAAK,CAAC,8BAA8B,KAAK,EAAE;AAAA,IACjH,CAAC;AACD,WAAO;AAAA,EACX;AAAA,EAGA,MAAa,OAAO;AAEhB,WAAO,KAAK;AAAA,MACR,OAAO,SAAe,MAAM,KAAK,KAAK;AAAA,IAC1C;AAAA,EACJ;AAAA,EAEA,MAAc,UAAU,UAA+B;AAEnD,QAAI,QAAQ,KAAK,MAAM,QAAQ;AAE/B,QAAI,WAAW,MAAM;AAAA,MACjB,OAAO,SAAe;AAClB,YAAI;AACA,iBAAO,MAAM,SAAS,IAAI;AAAA,QAC9B,SAAS,OAAO;AACZ,gCAAI,2CAA2C,KAAK,MAAM,CAAC,KAAK,KAAK,EAAE;AACvE,gBAAM;AAAA,QACV;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO,QAAQ,IAAI,QAAQ,EAAE,MAAM,WAAS;AACxC,4BAAI,kDAAkD,KAAK,EAAE;AAC7D,YAAM;AAAA,IACV,CAAC;AAAA,EACL;AAWF;AAEA,IAAO,sBAAQ;","names":["Cluster","Network","Node","node"]}