@awesome-ecs/workers
Version:
The Workers package for Awesome ECS. Provides basic implementation for the Worker communication using ECS.
136 lines (135 loc) • 5.17 kB
JavaScript
import "../worker-message-CpfPNmUD.mjs";
import "../worker-instance-D6m0tNnA.mjs";
import { assertComponent } from "@awesome-ecs/core/utils";
//#region src/systems/middlewares/initializers/worker-thread-health-schedule.ts
var WorkerThreadHealthScheduleSystem = class {
action(context) {
const { identity } = context.entity;
context.scheduler.scheduleUpdate({
entityType: identity.entityType,
entityUid: identity.model.uid
}, identity.model.healthCheckInterval || 5e3);
}
cleanup(context) {
context.scheduler.removeSchedule();
context.entity.instance.worker?.terminate();
}
};
//#endregion
//#region src/systems/middlewares/initializers/worker-thread-message-subscription.ts
var WorkerThreadMessageSubscriptionSystem = class {
shouldRun(context) {
return !!context.entity.instance.worker;
}
action(context) {
const { identity, instance } = context.entity;
const proxy = identity.proxy;
instance.worker.onmessage = (event) => {
const data = {
uid: event.data.messageType === "health" ? "message-received-health" : "message-received-data",
message: event.data
};
context.events.dispatchEvent(data, proxy);
context.logger.debug("[WORKER-RECEIVED] Message from worker", proxy, data.message.messageType, data.message.messageUid, data.message.workerUid);
};
instance.worker.onerror = (error) => {
const data = {
uid: "message-received-health",
message: null,
error
};
context.events.broadcastEvent(data);
instance.status = 3;
};
instance.worker.onmessageerror = (error) => {
const data = {
uid: "message-received-health",
message: null,
messageError: error
};
context.events.broadcastEvent(data);
instance.status = 3;
};
}
};
//#endregion
//#region src/systems/middlewares/updaters/worker-thread-data-complete.ts
var WorkerThreadDataCompleteSystem = class {
shouldRun(context, event) {
const workerUid = context.entity.identity.model.uid.toString();
return event.origin.entityUid.toString() === workerUid || event.data.message?.workerUid === workerUid;
}
action(context, event) {
if (event.data.message.messageType !== "data") return;
context.entity.instance.status = 1;
}
};
//#endregion
//#region src/systems/middlewares/updaters/worker-thread-health-request.ts
var WorkerThreadHealthRequestSystem = class {
maxHeartbeatsPending = 3;
shouldRun(context) {
if (!context.entity.instance.worker) return false;
const { lastHealthQuery } = context.entity.instance;
if (!lastHealthQuery) return true;
if ((/* @__PURE__ */ new Date()).getTime() - lastHealthQuery.getTime() > context.entity.identity.model.healthCheckInterval) return true;
return false;
}
action(context) {
assertComponent(context.entity.instance, context.entity, "instance");
const { entity } = context;
const { instance } = entity;
if (instance.status === 2) {
context.logger.debug("Worker is still busy, won't send heartbeat.", entity.identity.model.uid);
return;
}
const randomUid = Math.random().toFixed(3);
if (instance.heartbeats.size > this.maxHeartbeatsPending) {
context.logger.warn(`Worker ${entity.identity.model.uid} has ${instance.heartbeats.size} pending heartbeat. Will deactivate!`);
context.repository.removeEntity(context.entity.identity.proxy);
const existingModel = context.entity.identity.model;
const model = {
type: existingModel.type,
uid: `worker-${existingModel.type}-${randomUid}`,
healthCheckInterval: existingModel.healthCheckInterval
};
context.repository.addEntity("worker-thread", model);
} else {
this.sendHeartbeatMessage(randomUid, entity);
context.entity.instance.lastHealthQuery = /* @__PURE__ */ new Date();
}
}
sendHeartbeatMessage(uid, entity) {
const message = {
messageUid: `worker-heartbeat-${uid}`,
request: { heartbeatValue: uid },
workerType: entity.identity.model.type,
messageType: "health"
};
entity.instance.worker.postMessage(message);
entity.instance.heartbeats.set(uid, true);
}
};
//#endregion
//#region src/systems/middlewares/updaters/worker-thread-health-response.ts
var WorkerThreadHealthResponseSystem = class {
shouldRun(context, event) {
return !!context.entity.instance.worker && this.isEventForWorker(context, event);
}
action(context, event) {
assertComponent(context.entity.instance, context.entity, "instance");
const { instance } = context.entity;
if (event.data.error) throw new Error(`[WORKER-REPLY] ${event.data.error.message}`);
const { message } = event.data;
context.logger.debug("[WORKER-HEALTH-REPLY]", context.entity.identity.model.uid, message);
if (instance.status === 0) instance.status = 1;
instance.heartbeats.delete(message.data.heartbeatValue);
}
isEventForWorker(context, event) {
const workerUid = context.entity.identity.model.uid.toString();
return event.origin.entityUid.toString() === workerUid || event.data.message?.workerUid === workerUid;
}
};
//#endregion
export { WorkerThreadDataCompleteSystem, WorkerThreadHealthRequestSystem, WorkerThreadHealthResponseSystem, WorkerThreadHealthScheduleSystem, WorkerThreadMessageSubscriptionSystem };
//# sourceMappingURL=index.mjs.map