@travetto/worker 5.0.0-rc.9 → 5.0.1
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.
- package/README.md +2 -2
- package/__index__.ts +4 -8
- package/package.json +2 -2
- package/src/pool.ts +5 -7
- package/src/queue.ts +0 -85
- package/src/support/barrier.ts +0 -89
- package/src/support/error.ts +0 -6
- package/src/support/timeout.ts +0 -42
- /package/src/{comm → ipc}/channel.ts +0 -0
- /package/src/{comm → ipc}/child.ts +0 -0
- /package/src/{comm → ipc}/parent.ts +0 -0
- /package/src/{comm → ipc}/types.ts +0 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ yarn add @travetto/worker
|
|
|
16
16
|
This module provides the necessary primitives for handling dependent workers. A worker can be an individual actor or could be a pool of workers. Node provides ipc (inter-process communication) functionality out of the box. This module builds upon that by providing enhanced event management, richer process management, as well as constructs for orchestrating a conversation between two processes.
|
|
17
17
|
|
|
18
18
|
## Execution Pools
|
|
19
|
-
With respect to managing multiple executions, [WorkPool](https://github.com/travetto/travetto/tree/main/module/worker/src/pool.ts#
|
|
19
|
+
With respect to managing multiple executions, [WorkPool](https://github.com/travetto/travetto/tree/main/module/worker/src/pool.ts#L32) is provided to allow for concurrent operation, and processing of jobs concurrently. To manage the flow of jobs, [AsyncQueue](https://github.com/travetto/travetto/tree/main/module/runtime/src/queue.ts#L6) is used to support a wide range of use cases. [AsyncQueue](https://github.com/travetto/travetto/tree/main/module/runtime/src/queue.ts#L6) allows for manual control of iteration, which is useful for event driven work loads.
|
|
20
20
|
|
|
21
21
|
## IPC Support
|
|
22
|
-
Within the `comm` package, there is support for two primary communication elements: [ChildCommChannel](https://github.com/travetto/travetto/tree/main/module/worker/src/
|
|
22
|
+
Within the `comm` package, there is support for two primary communication elements: [ChildCommChannel](https://github.com/travetto/travetto/tree/main/module/worker/src/ipc/child.ts#L6) and [ParentCommChannel](https://github.com/travetto/travetto/tree/main/module/worker/src/ipc/parent.ts#L10). Usually [ParentCommChannel](https://github.com/travetto/travetto/tree/main/module/worker/src/ipc/parent.ts#L10) indicates it is the owner of the sub process. [ChildCommChannel](https://github.com/travetto/travetto/tree/main/module/worker/src/ipc/child.ts#L6) indicates that it has been created/spawned/forked by the parent and will communicate back to it's parent. This generally means that a [ParentCommChannel](https://github.com/travetto/travetto/tree/main/module/worker/src/ipc/parent.ts#L10) can be destroyed (i.e. killing the subprocess) where a [ChildCommChannel](https://github.com/travetto/travetto/tree/main/module/worker/src/ipc/child.ts#L6) can only exit the process, but the channel cannot be destroyed.
|
package/__index__.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
export * from './src/
|
|
2
|
-
export * from './src/
|
|
3
|
-
export * from './src/
|
|
4
|
-
export * from './src/
|
|
5
|
-
export * from './src/support/barrier';
|
|
6
|
-
export * from './src/support/timeout';
|
|
7
|
-
export * from './src/support/error';
|
|
8
|
-
export * from './src/queue';
|
|
1
|
+
export * from './src/ipc/channel';
|
|
2
|
+
export * from './src/ipc/child';
|
|
3
|
+
export * from './src/ipc/parent';
|
|
4
|
+
export * from './src/ipc/types';
|
|
9
5
|
export * from './src/pool';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/worker",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Process management utilities, with a focus on inter-process communication",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"exec",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"directory": "module/worker"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@travetto/runtime": "^5.0.
|
|
28
|
+
"@travetto/runtime": "^5.0.1",
|
|
29
29
|
"generic-pool": "^3.9.0"
|
|
30
30
|
},
|
|
31
31
|
"travetto": {
|
package/src/pool.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import { Options, Pool, createPool } from 'generic-pool';
|
|
3
3
|
|
|
4
|
-
import { Env, Util } from '@travetto/runtime';
|
|
5
|
-
|
|
6
|
-
import { WorkQueue } from './queue';
|
|
4
|
+
import { Env, Util, AsyncQueue } from '@travetto/runtime';
|
|
7
5
|
|
|
8
6
|
type ItrSource<I> = Iterable<I> | AsyncIterable<I>;
|
|
9
7
|
|
|
@@ -67,10 +65,10 @@ export class WorkPool {
|
|
|
67
65
|
},
|
|
68
66
|
validate: async (x: Worker<I, O>) => x.active ?? true
|
|
69
67
|
}, {
|
|
70
|
-
max: WorkPool.DEFAULT_SIZE,
|
|
71
|
-
min: 1,
|
|
72
68
|
evictionRunIntervalMillis: 5000,
|
|
73
69
|
...(opts ?? {}),
|
|
70
|
+
max: opts?.max ?? WorkPool.DEFAULT_SIZE,
|
|
71
|
+
min: opts?.min ?? 1,
|
|
74
72
|
});
|
|
75
73
|
|
|
76
74
|
|
|
@@ -143,7 +141,7 @@ export class WorkPool {
|
|
|
143
141
|
* Process a given input source as an async iterable
|
|
144
142
|
*/
|
|
145
143
|
static runStream<I, O>(worker: WorkerInput<I, O>, input: ItrSource<I>, opts?: WorkPoolConfig<I, O>): AsyncIterable<O> {
|
|
146
|
-
const itr = new
|
|
144
|
+
const itr = new AsyncQueue<O>();
|
|
147
145
|
const res = this.run(worker, input, {
|
|
148
146
|
...opts,
|
|
149
147
|
onComplete: (ev, inp, finishIdx) => {
|
|
@@ -163,7 +161,7 @@ export class WorkPool {
|
|
|
163
161
|
value: O;
|
|
164
162
|
total: number;
|
|
165
163
|
}> {
|
|
166
|
-
const itr = new
|
|
164
|
+
const itr = new AsyncQueue<{ idx: number, value: O, total: number }>();
|
|
167
165
|
const res = this.run(worker, input, {
|
|
168
166
|
...opts,
|
|
169
167
|
onComplete: (ev, inp, finishIdx) => {
|
package/src/queue.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Util } from '@travetto/runtime';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* WorkQueue, a manual async iterator. Items are added manually, and consumed asynchronously
|
|
5
|
-
*/
|
|
6
|
-
export class WorkQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
7
|
-
|
|
8
|
-
#queue: X[] = [];
|
|
9
|
-
#done = false;
|
|
10
|
-
#ready = Util.resolvablePromise();
|
|
11
|
-
#size: number;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Initial set of items
|
|
15
|
-
*/
|
|
16
|
-
constructor(initial: Iterable<X> = [], signal?: AbortSignal) {
|
|
17
|
-
this.#queue.push(...initial);
|
|
18
|
-
this.#size = this.#queue.length;
|
|
19
|
-
signal?.addEventListener('abort', () => this.close());
|
|
20
|
-
if (signal?.aborted) {
|
|
21
|
-
this.close();
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Allow for iteration
|
|
26
|
-
[Symbol.asyncIterator](): AsyncIterator<X> {
|
|
27
|
-
return this;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Wait for next event to fire
|
|
32
|
-
*/
|
|
33
|
-
async next(): Promise<IteratorResult<X>> {
|
|
34
|
-
while (!this.#done && !this.#queue.length) {
|
|
35
|
-
await this.#ready.promise;
|
|
36
|
-
this.#ready = Util.resolvablePromise();
|
|
37
|
-
}
|
|
38
|
-
return { value: (this.#queue.length ? this.#queue.shift() : undefined)!, done: this.#done };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Queue next event to fire
|
|
43
|
-
* @param {boolean} immediate Determines if item(s) should be append or prepended to the queue
|
|
44
|
-
*/
|
|
45
|
-
add(item: X, immediate = false): void {
|
|
46
|
-
this.#queue[immediate ? 'unshift' : 'push'](item);
|
|
47
|
-
this.#size += 1;
|
|
48
|
-
this.#ready.resolve();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Queue a list of data to stream
|
|
53
|
-
* @param {boolean} immediate Determines if item(s) should be append or prepended to the queue
|
|
54
|
-
*/
|
|
55
|
-
addAll(items: Iterable<X>): void {
|
|
56
|
-
const copy = [...items];
|
|
57
|
-
this.#queue.push(...copy);
|
|
58
|
-
this.#size += copy.length;
|
|
59
|
-
this.#ready.resolve();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Close the iterator
|
|
64
|
-
*/
|
|
65
|
-
close(): void {
|
|
66
|
-
this.#done = true;
|
|
67
|
-
this.#ready.resolve();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Throw an error from the queue, rejecting and terminating immediately
|
|
72
|
-
*/
|
|
73
|
-
async throw(e?: Error): Promise<IteratorResult<X>> {
|
|
74
|
-
this.#done = true;
|
|
75
|
-
this.#ready.reject(e);
|
|
76
|
-
return { value: undefined, done: this.#done };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Get size, will change as items are added
|
|
81
|
-
*/
|
|
82
|
-
get size(): number {
|
|
83
|
-
return this.#size;
|
|
84
|
-
}
|
|
85
|
-
}
|
package/src/support/barrier.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { TimeSpan, Util } from '@travetto/runtime';
|
|
2
|
-
|
|
3
|
-
import { Timeout } from './timeout';
|
|
4
|
-
|
|
5
|
-
function canCancel(o: unknown): o is { cancel(): unknown } {
|
|
6
|
-
return !!o && (typeof o === 'object') && 'cancel' in o && typeof o.cancel === 'function';
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Build an execution barrier to handle various limitations
|
|
11
|
-
*/
|
|
12
|
-
export class Barrier {
|
|
13
|
-
/**
|
|
14
|
-
* Listen for an unhandled event, as a promise
|
|
15
|
-
*/
|
|
16
|
-
static listenForUnhandled(): Promise<unknown> & { cancel?: () => void } {
|
|
17
|
-
const uncaught = Util.resolvablePromise<Promise<unknown> & { cancel?: () => void }>();
|
|
18
|
-
const onError = (err: Error): void => { Util.queueMacroTask().then(() => uncaught.reject(err)); };
|
|
19
|
-
process.on('unhandledRejection', onError).on('uncaughtException', onError);
|
|
20
|
-
const cancel = (): void => {
|
|
21
|
-
process.off('unhandledRejection', onError).off('unhandledException', onError);
|
|
22
|
-
uncaught.resolve(undefined!); // Close the promise
|
|
23
|
-
};
|
|
24
|
-
Object.defineProperty(uncaught.promise, 'cancel', { value: cancel });
|
|
25
|
-
return uncaught.promise;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
#support: string[] = [];
|
|
29
|
-
#barriers = new Map<string, Promise<unknown>>([]);
|
|
30
|
-
|
|
31
|
-
constructor(
|
|
32
|
-
timeout?: number | TimeSpan,
|
|
33
|
-
unhandled?: boolean
|
|
34
|
-
) {
|
|
35
|
-
if (timeout !== undefined) {
|
|
36
|
-
this.add(new Timeout(timeout).wait(), true);
|
|
37
|
-
}
|
|
38
|
-
if (unhandled) {
|
|
39
|
-
this.add(Barrier.listenForUnhandled());
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Add a new barrier
|
|
45
|
-
*/
|
|
46
|
-
add(p: (() => Promise<unknown>) | Promise<unknown>, support = false): this {
|
|
47
|
-
if (!('then' in p)) {
|
|
48
|
-
p = p();
|
|
49
|
-
}
|
|
50
|
-
const k = Util.uuid();
|
|
51
|
-
p = p
|
|
52
|
-
.finally(() => this.#barriers.delete(k))
|
|
53
|
-
.catch(err => { this.cleanup(); throw err; });
|
|
54
|
-
|
|
55
|
-
if (!support) {
|
|
56
|
-
p = p.then(() => this.cleanup());
|
|
57
|
-
} else {
|
|
58
|
-
this.#support.push(k);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this.#barriers.set(k, p);
|
|
62
|
-
return this;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Clean up, and cancel all cancellable barriers
|
|
67
|
-
*/
|
|
68
|
-
cleanup(): void {
|
|
69
|
-
for (const k of this.#support) {
|
|
70
|
-
const el = this.#barriers.get(k);
|
|
71
|
-
if (canCancel(el)) {
|
|
72
|
-
el.cancel();
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
this.#barriers.clear();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Wait for all barriers to clear out
|
|
80
|
-
*/
|
|
81
|
-
async wait(): Promise<Error | undefined> {
|
|
82
|
-
let capturedError: Error | undefined;
|
|
83
|
-
// Wait for all barriers to be satisfied
|
|
84
|
-
while (this.#barriers.size) {
|
|
85
|
-
await Promise.race(this.#barriers.values()).catch(err => capturedError ??= err);
|
|
86
|
-
}
|
|
87
|
-
return capturedError;
|
|
88
|
-
}
|
|
89
|
-
}
|
package/src/support/error.ts
DELETED
package/src/support/timeout.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import timers from 'node:timers/promises';
|
|
2
|
-
import { TimeSpan, TimeUtil, Util } from '@travetto/runtime';
|
|
3
|
-
import { ExecutionError } from './error';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Timeout support, throws self on timeout
|
|
7
|
-
*/
|
|
8
|
-
export class Timeout extends ExecutionError {
|
|
9
|
-
|
|
10
|
-
#timeout?: AbortController;
|
|
11
|
-
#resolver = Util.resolvablePromise();
|
|
12
|
-
#duration: number;
|
|
13
|
-
|
|
14
|
-
constructor(duration: number | TimeSpan, op: string = 'Operation') {
|
|
15
|
-
super(`${op} timed out after ${duration}${typeof duration === 'number' ? 'ms' : ''}`);
|
|
16
|
-
this.#duration = TimeUtil.asMillis(duration);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Stop timeout from firing
|
|
21
|
-
*/
|
|
22
|
-
cancel(): void {
|
|
23
|
-
if (this.#timeout) {
|
|
24
|
-
this.#resolver.resolve();
|
|
25
|
-
this.#timeout.abort();
|
|
26
|
-
this.#timeout = undefined;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Wait for timeout as a promise
|
|
32
|
-
*/
|
|
33
|
-
wait(): Promise<void> {
|
|
34
|
-
if (!this.#timeout) {
|
|
35
|
-
this.#timeout = new AbortController();
|
|
36
|
-
timers.setTimeout(this.#duration, undefined, { ref: false, signal: this.#timeout.signal })
|
|
37
|
-
.then(() => this.#resolver.reject(this))
|
|
38
|
-
.catch(() => false);
|
|
39
|
-
}
|
|
40
|
-
return this.#resolver.promise;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|