@awesome-ecs/workers
Version:
The Workers package for Awesome ECS. Provides basic implementation for the Worker communication using ECS.
1 lines • 13 kB
Source Map (JSON)
{"version":3,"sources":["../../src/systems/index.ts","../../src/systems/middlewares/initializers/worker-thread-health-schedule.ts","../../src/systems/middlewares/initializers/worker-thread-message-subscription.ts","../../src/systems/middlewares/initializers/worker-thread-health-subscription.ts","../../src/systems/middlewares/updaters/worker-thread-health-request.ts","../../src/systems/middlewares/updaters/worker-thread-health-response.ts"],"sourcesContent":["export * from './middlewares/initializers/worker-thread-health-schedule';\nexport * from './middlewares/initializers/worker-thread-message-subscription';\nexport * from './middlewares/initializers/worker-thread-health-subscription';\nexport * from './middlewares/updaters/worker-thread-health-request';\nexport * from './middlewares/updaters/worker-thread-health-response';\n","import { ISystemMiddleware, ISystemContext } from '@awesome-ecs/abstract/systems';\nimport { WorkerThreadEntity } from '../../../entities/worker-thread';\n\n// @injectable\nexport class WorkerThreadHealthScheduleSystem implements ISystemMiddleware<WorkerThreadEntity> {\n action(context: ISystemContext<WorkerThreadEntity>): void {\n const { identity } = context.entity;\n\n context.scheduler.scheduleUpdate(\n {\n entityType: identity.entityType,\n entityUid: identity.model.uid\n },\n identity.model.healthCheckInterval || 5000\n );\n }\n\n cleanup(context: ISystemContext<WorkerThreadEntity>): void {\n context.scheduler.removeSchedule();\n context.entity.instance.worker.terminate();\n }\n}\n","import { ISystemMiddleware, ISystemContext } from '@awesome-ecs/abstract/systems';\nimport { WorkerThreadEventType } from '../../../abstract/events/event-type';\nimport { WorkerThreadEventMessageResponseData } from '../../../abstract/events/message';\nimport { WorkerMessageType, WorkerResponseMessage } from '../../../abstract/worker-message';\nimport { WorkerStatus } from '../../../components/worker-instance';\nimport { WorkerThreadEntity } from '../../../entities/worker-thread';\n\n// @injectable\nexport class WorkerThreadMessageSubscriptionSystem\n implements ISystemMiddleware<WorkerThreadEntity>\n{\n shouldRun(context: ISystemContext<WorkerThreadEntity>): boolean {\n return !!context.entity.instance.worker;\n }\n\n action(context: ISystemContext<WorkerThreadEntity>): void {\n const { worker } = context.entity.instance;\n\n worker.onmessage = (event: MessageEvent<WorkerResponseMessage<any>>) => {\n const data: WorkerThreadEventMessageResponseData<any> = {\n uid:\n event.data.messageType === WorkerMessageType.health\n ? WorkerThreadEventType.messageReceivedHealth\n : WorkerThreadEventType.messageReceivedData,\n message: event.data\n };\n\n // dispatch event to SELF so it can be processed by other middleware on next tick\n context.events.dispatchEvent(data, context.entity.myProxy);\n\n if (event.data.messageType === WorkerMessageType.data) {\n context.entity.instance.status = WorkerStatus.available;\n }\n\n context.logger.debug(\n '[WORKER-RECEIVED] Message from worker',\n context.entity.myProxy,\n data.message.messageType,\n data.message.messageUid,\n data.message.workerUid\n );\n };\n\n worker.onerror = (error: ErrorEvent) => {\n const data: WorkerThreadEventMessageResponseData<any> = {\n uid: WorkerThreadEventType.messageReceivedHealth,\n message: null,\n error\n };\n\n context.events.dispatchEvent(data);\n context.entity.instance.status = WorkerStatus.error;\n };\n\n worker.onmessageerror = (error: MessageEvent) => {\n const data: WorkerThreadEventMessageResponseData<any> = {\n uid: WorkerThreadEventType.messageReceivedHealth,\n message: null,\n messageError: error\n };\n\n context.events.dispatchEvent(data);\n context.entity.instance.status = WorkerStatus.error;\n };\n }\n\n cleanup(context: ISystemContext<WorkerThreadEntity>): void {\n context.events.clearSubscriptions();\n }\n}\n","import { ISystemMiddleware, ISystemContext } from '@awesome-ecs/abstract/systems';\nimport { WorkerThreadEventType } from '../../../abstract/events/event-type';\nimport { WorkerThreadEntity } from '../../../entities/worker-thread';\n\n// @injectable\nexport class WorkerThreadHealthSubscriptionSystem implements ISystemMiddleware<WorkerThreadEntity> {\n action(context: ISystemContext<WorkerThreadEntity>): void {\n context.events.subscribeTo(WorkerThreadEventType.messageReceivedHealth);\n }\n\n cleanup(context: ISystemContext<WorkerThreadEntity>): void {\n context.events.unsubscribeFrom(WorkerThreadEventType.messageReceivedHealth);\n }\n}\n","import { ISystemMiddleware, ISystemContext } from '@awesome-ecs/abstract/systems';\nimport { assertComponent } from '@awesome-ecs/core/utils';\nimport { WorkerThreadEventType } from '../../../abstract/events/event-type';\nimport { WorkerComponentType } from '../../../abstract/types/component-type';\nimport { WorkerEntityType } from '../../../abstract/types/entity-type';\nimport { WorkerThreadHealthRequest } from '../../../abstract/worker-health';\nimport { WorkerMessageType, WorkerRequestMessage } from '../../../abstract/worker-message';\nimport { WorkerStatus } from '../../../components/worker-instance';\nimport { WorkerThreadEntity, WorkerThreadModel } from '../../../entities/worker-thread';\n\n// @injectable\nexport class WorkerThreadHealthRequestSystem implements ISystemMiddleware<WorkerThreadEntity> {\n private maxHeartbeatsPending = 0;\n\n shouldRun(context: ISystemContext<WorkerThreadEntity>): boolean {\n const { lastHealthQuery } = context.entity.instance;\n\n if (!lastHealthQuery) {\n return true;\n }\n\n const passedMs = new Date().getTime() - lastHealthQuery.getTime();\n\n if (passedMs > context.entity.identity.model.healthCheckInterval) {\n return true;\n }\n\n return false;\n }\n\n action(context: ISystemContext<WorkerThreadEntity>): void {\n assertComponent(WorkerComponentType.workerInstance, context.entity);\n\n const { entity } = context;\n const { instance } = entity;\n\n if (instance.status === WorkerStatus.busy) {\n context.logger.debug(\n \"Worker is still busy, won't send heartbeat.\",\n entity.identity.model.uid\n );\n return;\n }\n\n const randomUid = Math.random().toFixed(3);\n\n if (instance.heartbeats.size > this.maxHeartbeatsPending) {\n context.logger.warn(\n `Worker ${entity.identity.model.uid} has ${instance.heartbeats.size} pending heartbeat. Will deactivate!`\n );\n\n // remove current unresponsive worker\n context.repository.removeEntity(context.entity.myProxy);\n\n const existingModel = context.entity.identity.model;\n\n // register new worker\n const model: WorkerThreadModel = {\n type: existingModel.type,\n uid: `worker-${existingModel.type}-${randomUid}`,\n healthCheckInterval: existingModel.healthCheckInterval\n };\n\n context.repository.addEntity(WorkerEntityType.workerThread, model);\n } else {\n this.sendHeartbeatMessage(randomUid, entity);\n context.entity.instance.lastHealthQuery = new Date();\n }\n }\n\n cleanup(context: ISystemContext<WorkerThreadEntity>): void {\n context.events.unsubscribeFrom(WorkerThreadEventType.messageReceivedHealth);\n }\n\n private sendHeartbeatMessage(uid: string, entity: Readonly<WorkerThreadEntity>) {\n const message: WorkerRequestMessage<WorkerThreadHealthRequest> = {\n messageUid: `worker-heartbeat-${uid}`,\n request: {\n heartbeatValue: uid\n },\n workerType: entity.identity.model.type,\n messageType: WorkerMessageType.health\n };\n\n entity.instance.worker.postMessage(message);\n entity.instance.heartbeats.set(uid, true);\n }\n}\n","import { ISystemMiddleware, ISystemContext } from '@awesome-ecs/abstract/systems';\nimport { assertComponent } from '@awesome-ecs/core/utils';\nimport { WorkerThreadEventType } from '../../../abstract/events/event-type';\nimport { WorkerThreadEventMessageResponseData } from '../../../abstract/events/message';\nimport { WorkerComponentType } from '../../../abstract/types/component-type';\nimport { WorkerThreadHealthResponse } from '../../../abstract/worker-health';\nimport { WorkerStatus } from '../../../components/worker-instance';\nimport { WorkerThreadEntity } from '../../../entities/worker-thread';\n\n// @injectable\nexport class WorkerThreadHealthResponseSystem implements ISystemMiddleware<WorkerThreadEntity> {\n shouldRun(context: ISystemContext<WorkerThreadEntity>): boolean {\n return context.events.hasEvent(WorkerThreadEventType.messageReceivedHealth);\n }\n\n action(context: ISystemContext<WorkerThreadEntity>): void {\n assertComponent(WorkerComponentType.workerInstance, context.entity);\n\n const { instance } = context.entity;\n const event = context.events.getEvent<\n WorkerThreadEventMessageResponseData<WorkerThreadHealthResponse>\n >(WorkerThreadEventType.messageReceivedHealth);\n\n if (event.data.error) {\n throw new Error(`[WORKER-REPLY] ${event.data.error.message}`);\n }\n\n const { message } = event.data;\n\n context.logger.debug('[WORKER-HEALTH-REPLY]', context.entity.identity.model.uid, message);\n\n if (instance.status === WorkerStatus.starting) {\n instance.status = WorkerStatus.available;\n }\n\n instance.heartbeats.delete(message.data.heartbeatValue);\n }\n\n cleanup(context: ISystemContext<WorkerThreadEntity>): void {\n context.events.unsubscribeFrom(WorkerThreadEventType.messageReceivedHealth);\n }\n}\n"],"mappings":"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","names":["import_utils"]}