@atolljs/node 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -24,6 +24,20 @@ function scoped(scope) {
|
|
|
24
24
|
}
|
|
25
25
|
var fmtBytes = (bytes) => bytes >= 1 << 20 ? `${(bytes / (1 << 20)).toFixed(1)}MB` : bytes >= 1024 ? `${(bytes / 1024).toFixed(1)}KB` : `${bytes}B`;
|
|
26
26
|
|
|
27
|
+
// ../../src/devtools.ts
|
|
28
|
+
var THREAD2 = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope ? "worker" : "main";
|
|
29
|
+
var sink2 = null;
|
|
30
|
+
var emitDevtools = (event) => {
|
|
31
|
+
if (sink2) sink2({ ...event, at: performance.now(), thread: THREAD2 });
|
|
32
|
+
};
|
|
33
|
+
var forwardDevtools = (event) => {
|
|
34
|
+
if (sink2) sink2(event);
|
|
35
|
+
};
|
|
36
|
+
var devtoolsEnabled = () => sink2 !== null;
|
|
37
|
+
var BODY_PREVIEW = 8 * 1024;
|
|
38
|
+
var seq = 0;
|
|
39
|
+
var nextDevtoolsId = (prefix) => `${prefix}-${++seq}`;
|
|
40
|
+
|
|
27
41
|
// ../../src/contract/sharedMemory.ts
|
|
28
42
|
var memLog = scoped("memory");
|
|
29
43
|
var codecTextEncoder = new TextEncoder();
|
|
@@ -246,8 +260,11 @@ function listFactory(d, ctx, byteOffset) {
|
|
|
246
260
|
writeAt,
|
|
247
261
|
commit: () => {
|
|
248
262
|
if (ctx.version) {
|
|
249
|
-
Atomics.add(ctx.version.view, ctx.version.index, 1);
|
|
263
|
+
const prev = Atomics.add(ctx.version.view, ctx.version.index, 1);
|
|
250
264
|
Atomics.notify(ctx.version.view, ctx.version.index);
|
|
265
|
+
if (ctx.path && devtoolsEnabled()) {
|
|
266
|
+
emitDevtools({ type: "memory:write", path: ctx.path, version: prev + 1 });
|
|
267
|
+
}
|
|
251
268
|
}
|
|
252
269
|
},
|
|
253
270
|
read: () => Array.from({ length: count }, (_, i) => readAt(i)),
|
|
@@ -384,6 +401,10 @@ var WorkerPoolImpl = class {
|
|
|
384
401
|
waitMs: agg(),
|
|
385
402
|
runMs: agg()
|
|
386
403
|
};
|
|
404
|
+
/** Devtools correlation id ('pool-N') — stamped on every event this pool emits. */
|
|
405
|
+
poolId = nextDevtoolsId("pool");
|
|
406
|
+
label;
|
|
407
|
+
nextCallId = 0;
|
|
387
408
|
sharedMemory;
|
|
388
409
|
/** The attached memory-persistence adapter, if the config supplied one. */
|
|
389
410
|
persistence;
|
|
@@ -404,6 +425,7 @@ var WorkerPoolImpl = class {
|
|
|
404
425
|
this.maxQueue = config.maxQueue ?? Infinity;
|
|
405
426
|
this.respawn = config.respawn ?? true;
|
|
406
427
|
this.taskTimeout = config.taskTimeout;
|
|
428
|
+
this.label = config.name;
|
|
407
429
|
if (config.sharedMemory) {
|
|
408
430
|
this.sharedMemory = config.sharedMemory;
|
|
409
431
|
this.memoryManager = new MemoryManager(config.memory);
|
|
@@ -418,6 +440,14 @@ var WorkerPoolImpl = class {
|
|
|
418
440
|
{ workerUrl: config.workerUrl ? String(config.workerUrl) : "createWorker()" }
|
|
419
441
|
);
|
|
420
442
|
poolLog.debug(`tip: setLogLevel('trace') for per-task detail`);
|
|
443
|
+
emitDevtools({
|
|
444
|
+
type: "pool:init",
|
|
445
|
+
poolId: this.poolId,
|
|
446
|
+
label: this.label,
|
|
447
|
+
poolSize,
|
|
448
|
+
concurrency: this.concurrency,
|
|
449
|
+
memoryBytes: this.sharedMemory?.totalBytes
|
|
450
|
+
});
|
|
421
451
|
for (let i = 0; i < poolSize; i++) {
|
|
422
452
|
this.addWorker(this.spawn());
|
|
423
453
|
}
|
|
@@ -437,18 +467,27 @@ var WorkerPoolImpl = class {
|
|
|
437
467
|
addWorker(worker) {
|
|
438
468
|
const slot = { worker, inFlight: 0 };
|
|
439
469
|
worker.addEventListener("message", (event) => {
|
|
440
|
-
|
|
470
|
+
const data = event.data;
|
|
471
|
+
if (data.type === "ATOLL_DEVTOOLS" && data.event) {
|
|
472
|
+
forwardDevtools({
|
|
473
|
+
...data.event,
|
|
474
|
+
worker: { poolId: this.poolId, slot: this.slots.indexOf(slot) }
|
|
475
|
+
});
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
this.handleReply(slot, data);
|
|
441
479
|
});
|
|
442
480
|
worker.addEventListener("error", (event) => {
|
|
443
481
|
this.handleWorkerError(slot, event);
|
|
444
482
|
});
|
|
445
483
|
if (this.memoryManager) {
|
|
446
|
-
worker.postMessage({ type: "INIT_MEMORY", memory: this.memoryManager.memory });
|
|
484
|
+
worker.postMessage({ type: "INIT_MEMORY", memory: this.memoryManager.memory, devtools: devtoolsEnabled() });
|
|
447
485
|
} else {
|
|
448
|
-
worker.postMessage({ type: "INIT" });
|
|
486
|
+
worker.postMessage({ type: "INIT", devtools: devtoolsEnabled() });
|
|
449
487
|
}
|
|
450
488
|
this.slots.push(slot);
|
|
451
489
|
poolLog.debug(`worker spawned, ${this.memoryManager ? "INIT_MEMORY" : "INIT"} sent`);
|
|
490
|
+
emitDevtools({ type: "worker:spawn", poolId: this.poolId, slot: this.slots.indexOf(slot) });
|
|
452
491
|
return slot;
|
|
453
492
|
}
|
|
454
493
|
handleReply(slot, data) {
|
|
@@ -459,14 +498,17 @@ var WorkerPoolImpl = class {
|
|
|
459
498
|
this.detach(entry);
|
|
460
499
|
if (!entry.done) {
|
|
461
500
|
entry.done = true;
|
|
462
|
-
|
|
501
|
+
const runMs = performance.now() - (entry.sentAt ?? entry.enqueuedAt);
|
|
502
|
+
this.statsAgg.runMs.add(runMs);
|
|
463
503
|
if (data.success) {
|
|
464
504
|
this.statsAgg.completed++;
|
|
465
505
|
taskLog.debug(`\u2190 ${entry.contract.taskId} ok`);
|
|
506
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: "ok", runMs });
|
|
466
507
|
entry.resolve(data.result);
|
|
467
508
|
} else {
|
|
468
509
|
this.statsAgg.failed++;
|
|
469
510
|
taskLog.warn(`\u2190 ${entry.contract.taskId} failed: ${data.error}`);
|
|
511
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: "error", runMs, error: data.error });
|
|
470
512
|
entry.reject(new Error(data.error));
|
|
471
513
|
}
|
|
472
514
|
}
|
|
@@ -478,6 +520,8 @@ var WorkerPoolImpl = class {
|
|
|
478
520
|
const e = event;
|
|
479
521
|
const message = e?.message ?? (e?.error ? String(e.error) : "worker error");
|
|
480
522
|
poolLog.warn(`worker error: ${message}`);
|
|
523
|
+
const idx = this.slots.indexOf(slot);
|
|
524
|
+
emitDevtools({ type: "worker:error", poolId: this.poolId, slot: idx, message });
|
|
481
525
|
for (const [id, entry] of [...this.pending]) {
|
|
482
526
|
if (entry.slot === slot) {
|
|
483
527
|
this.pending.delete(id);
|
|
@@ -485,20 +529,22 @@ var WorkerPoolImpl = class {
|
|
|
485
529
|
if (entry.done) continue;
|
|
486
530
|
entry.done = true;
|
|
487
531
|
this.statsAgg.failed++;
|
|
532
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: "crashed", error: message });
|
|
488
533
|
entry.reject(new WorkerCrashedError(message));
|
|
489
534
|
}
|
|
490
535
|
}
|
|
491
|
-
const idx = this.slots.indexOf(slot);
|
|
492
536
|
if (idx >= 0) this.slots.splice(idx, 1);
|
|
493
537
|
slot.worker.terminate();
|
|
494
538
|
if (this.respawn) {
|
|
495
539
|
poolLog.info("respawning crashed worker");
|
|
496
540
|
this.addWorker(this.spawn());
|
|
541
|
+
emitDevtools({ type: "worker:respawn", poolId: this.poolId, slot: this.slots.length - 1 });
|
|
497
542
|
} else if (this.slots.length === 0) {
|
|
498
543
|
for (const entry of this.queue.splice(0)) {
|
|
499
544
|
entry.done = true;
|
|
500
545
|
this.detach(entry);
|
|
501
546
|
this.statsAgg.failed++;
|
|
547
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: "crashed", error: message });
|
|
502
548
|
entry.reject(new WorkerCrashedError(message));
|
|
503
549
|
}
|
|
504
550
|
}
|
|
@@ -527,10 +573,13 @@ var WorkerPoolImpl = class {
|
|
|
527
573
|
entry.messageId = messageId;
|
|
528
574
|
entry.slot = slot;
|
|
529
575
|
entry.sentAt = performance.now();
|
|
530
|
-
|
|
576
|
+
const waitMs = entry.sentAt - entry.enqueuedAt;
|
|
577
|
+
this.statsAgg.waitMs.add(waitMs);
|
|
531
578
|
slot.inFlight++;
|
|
532
579
|
this.pending.set(messageId, entry);
|
|
533
|
-
|
|
580
|
+
const slotIndex = this.slots.indexOf(slot);
|
|
581
|
+
taskLog.debug(`\u2192 ${entry.contract.taskId}`, { worker: slotIndex, args: entry.args });
|
|
582
|
+
emitDevtools({ type: "task:dispatch", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, slot: slotIndex, waitMs });
|
|
534
583
|
slot.worker.postMessage({
|
|
535
584
|
type: "EXECUTE_TASK",
|
|
536
585
|
messageId,
|
|
@@ -557,6 +606,7 @@ var WorkerPoolImpl = class {
|
|
|
557
606
|
entry.done = true;
|
|
558
607
|
this.detach(entry);
|
|
559
608
|
this.statsAgg.aborted++;
|
|
609
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: err instanceof TaskTimeoutError ? "timeout" : "aborted" });
|
|
560
610
|
entry.reject(err);
|
|
561
611
|
this.drainQueue();
|
|
562
612
|
this.checkDrained();
|
|
@@ -582,6 +632,7 @@ var WorkerPoolImpl = class {
|
|
|
582
632
|
const entry = {
|
|
583
633
|
contract,
|
|
584
634
|
args,
|
|
635
|
+
callId: ++this.nextCallId,
|
|
585
636
|
enqueuedAt: performance.now(),
|
|
586
637
|
resolve,
|
|
587
638
|
reject,
|
|
@@ -596,12 +647,14 @@ var WorkerPoolImpl = class {
|
|
|
596
647
|
if (timeout !== void 0) {
|
|
597
648
|
entry.timer = setTimeout(() => this.cancel(entry, new TaskTimeoutError()), timeout);
|
|
598
649
|
}
|
|
650
|
+
emitDevtools({ type: "task:enqueue", poolId: this.poolId, callId: entry.callId, taskId: contract.taskId });
|
|
599
651
|
const slot = this.pickWorker();
|
|
600
652
|
if (slot) {
|
|
601
653
|
this.send(slot, entry);
|
|
602
654
|
} else if (this.queue.length >= this.maxQueue) {
|
|
603
655
|
entry.done = true;
|
|
604
656
|
this.detach(entry);
|
|
657
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: contract.taskId, outcome: "queue-full" });
|
|
605
658
|
reject(new PoolQueueFullError());
|
|
606
659
|
return;
|
|
607
660
|
} else {
|
|
@@ -666,12 +719,14 @@ var WorkerPoolImpl = class {
|
|
|
666
719
|
(e) => poolLog.warn("memory persistence stop failed", e)
|
|
667
720
|
);
|
|
668
721
|
poolLog.info(`terminating ${this.slots.length} worker(s)`);
|
|
722
|
+
emitDevtools({ type: "pool:terminate", poolId: this.poolId });
|
|
669
723
|
const crashed = new WorkerCrashedError("pool terminated");
|
|
670
724
|
for (const [, entry] of this.pending) {
|
|
671
725
|
this.detach(entry);
|
|
672
726
|
if (entry.done) continue;
|
|
673
727
|
entry.done = true;
|
|
674
728
|
this.statsAgg.failed++;
|
|
729
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: "crashed", error: "pool terminated" });
|
|
675
730
|
entry.reject(crashed);
|
|
676
731
|
}
|
|
677
732
|
this.pending.clear();
|
|
@@ -679,6 +734,7 @@ var WorkerPoolImpl = class {
|
|
|
679
734
|
entry.done = true;
|
|
680
735
|
this.detach(entry);
|
|
681
736
|
this.statsAgg.failed++;
|
|
737
|
+
emitDevtools({ type: "task:settle", poolId: this.poolId, callId: entry.callId, taskId: entry.contract.taskId, outcome: "crashed", error: "pool terminated" });
|
|
682
738
|
entry.reject(crashed);
|
|
683
739
|
}
|
|
684
740
|
for (const slot of this.slots) {
|
package/dist/index.js
CHANGED
package/dist/redis.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atolljs/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Node runtime adapter — node:worker_threads pools, no framework",
|
|
5
5
|
"homepage": "https://jwhenry3.github.io/atolljs/consumer/quickstart/",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"registry": "https://registry.npmjs.org"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@atolljs/core": "0.1.
|
|
37
|
+
"@atolljs/core": "0.1.7"
|
|
38
38
|
},
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|