multi-tasks 3.1.0 → 3.1.2
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 +56 -55
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# multi-tasks
|
|
2
2
|
|
|
3
|
-
Multi-tasks is a toolkit to manage long-term and large-scale parallel computing tasks.
|
|
3
|
+
Multi-tasks is a toolkit to manage long-term and large-scale parallel computing tasks. Progress and tasks are stored on the file system, so tasks can be resumed even if the host crashes.
|
|
4
4
|
|
|
5
5
|
**Zero dependencies:** multi-tasks has no runtime dependencies at all — it is built entirely on Node.js built-in modules (`cluster`, `fs`, `path`, `os`), so installing it adds nothing extra to your `node_modules`.
|
|
6
6
|
|
|
@@ -12,27 +12,36 @@ npm install multi-tasks
|
|
|
12
12
|
|
|
13
13
|
### API:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
15
|
+
The API has three parts: the entry functions, the config options, and the `helper` object injected into `processTask`.
|
|
16
|
+
|
|
17
|
+
**Entry functions:**
|
|
18
|
+
|
|
19
|
+
- **`multiTasks(config)`** — run tasks; auto-resumes if the task folder already exists or `initialTasks` points to one.
|
|
20
|
+
- **`multiTasks.start(config)`** — always start a fresh run.
|
|
21
|
+
- **`multiTasks.resume(config)`** — resume an interrupted run.
|
|
22
|
+
- **`multiTasks.restart(config)`** — resume and also retry failed tasks (needs `config.taskFolder`).
|
|
23
|
+
|
|
24
|
+
**Config options:**
|
|
25
|
+
|
|
26
|
+
- **`initialTasks`** — array of task objects, or a folder path string to resume from.
|
|
27
|
+
- **`processTask(task, helper)`** — required; return a value or a Promise.
|
|
28
|
+
- **`taskRootFolder`** — root directory where progress and result files are stored.
|
|
29
|
+
- **`taskId`** — task folder name under `taskRootFolder`.
|
|
30
|
+
- **`taskFolder`** — full task folder path, required by `restart`.
|
|
31
|
+
- **`numberOfWorkers`** — how many worker processes run in parallel; a number, or a percentage string of CPU cores like `"50%"` (default).
|
|
32
|
+
- **`taskTimeout`** — optional, milliseconds; an overdue task fails with a timeout error.
|
|
33
|
+
- **`maxTaskRetries`** — optional, max times a failed task is auto-retried.
|
|
34
|
+
- **`autoCloseAfterCompletion`** — set `false` if you create new tasks dynamically.
|
|
35
|
+
- **`shouldTerminate(info)`** — return `true` to terminate the whole process.
|
|
36
|
+
- **`setSysListener(event, payload, meta)`** — optional, handle custom system events (sent via `helper.emitSys`) on the master; `meta` has `fromWorkerId` and `fromWorkerPid`.
|
|
37
|
+
- **`onFinish(report)`** — called once after all workers are done.
|
|
38
|
+
|
|
39
|
+
**Task helper** (the `helper` object passed as the second argument of `processTask`):
|
|
40
|
+
|
|
41
|
+
- **`helper.createNewTasks(tasks)`** — create new tasks dynamically while processing.
|
|
42
|
+
- **`helper.emit(event, payload, option?)`** — broadcast an event to all workers (relayed by the master, best-effort); `option.includingMe` defaults to `true` — pass `{includingMe: false}` to exclude the sender. Payload must be JSON-serializable.
|
|
43
|
+
- **`helper.setListener(event, handler)`** — listen for broadcast events; one handler per event per worker process (re-registering replaces it), so calling it inside `processTask` is always safe; `handler(payload, meta)` gets `meta.fromWorkerId` and `meta.fromWorkerPid`. Broadcasts are runtime-only messages — not persisted, not replayed on resume.
|
|
44
|
+
- **`helper.emitSys(event, payload?)`** — send a system event to the master (consumed by the master itself, not relayed). Built-in event: `'TERMINATE_ALL_WORKERS'` force-kills all workers and exits the master; any other event goes to `config.setSysListener`.
|
|
36
45
|
|
|
37
46
|
### How to use:
|
|
38
47
|
|
|
@@ -40,7 +49,7 @@ npm install multi-tasks
|
|
|
40
49
|
//see examples/example0
|
|
41
50
|
let multiTasks = require('multi-tasks').multiTasks;
|
|
42
51
|
|
|
43
|
-
//Step1, create
|
|
52
|
+
//Step1, create the tasks to run in parallel as an array.
|
|
44
53
|
let alltasks = [];
|
|
45
54
|
for(let i=0;i<50;i++){
|
|
46
55
|
alltasks.push({
|
|
@@ -49,7 +58,7 @@ for(let i=0;i<50;i++){
|
|
|
49
58
|
});
|
|
50
59
|
};
|
|
51
60
|
|
|
52
|
-
//Step2, provide a function
|
|
61
|
+
//Step2, provide a function that processes each sub-task and returns the result
|
|
53
62
|
let processTask = (task, helper)=>{
|
|
54
63
|
let {taskCount} = task;//get your task data
|
|
55
64
|
|
|
@@ -63,12 +72,12 @@ let processTask = (task, helper)=>{
|
|
|
63
72
|
multiTasks({
|
|
64
73
|
initialTasks: alltasks,
|
|
65
74
|
processTask,
|
|
66
|
-
taskRootFolder: `../examples-tmp-data/example0`, //a directory to store progress and
|
|
75
|
+
taskRootFolder: `../examples-tmp-data/example0`, //a directory to store progress and result files; you can check the progress here
|
|
67
76
|
taskId: 'my-task',
|
|
68
77
|
numberOfWorkers: 3, //how many workers are working in parallel
|
|
69
78
|
//taskTimeout: 30000, //optional, in milliseconds, an overdue task is treated as failed with a timeout error
|
|
70
79
|
//maxTaskRetries: 2, //optional, auto-retry a failed task (worker crash, processTask error, or timeout); retried tasks go back to the queue
|
|
71
|
-
//autoCloseAfterCompletion: true, //if you have dynamically generated new tasks,
|
|
80
|
+
//autoCloseAfterCompletion: true, //if you have dynamically generated new tasks, set this to false
|
|
72
81
|
shouldTerminate:(info)=>{
|
|
73
82
|
//return true if you need to terminate the whole process
|
|
74
83
|
},
|
|
@@ -118,21 +127,26 @@ let processTask = (task, helper)=>{
|
|
|
118
127
|
let processTask = (task, helper)=>{
|
|
119
128
|
let {taskCount} = task;
|
|
120
129
|
|
|
121
|
-
//
|
|
130
|
+
//Demo of exceptions/errors: they are captured and saved in the results/errors folder
|
|
122
131
|
if(taskCount===3) throw 'exception';
|
|
123
132
|
if(taskCount===4) return Promise.reject({err:'a test error'});//use Promise.reject method
|
|
124
|
-
if(taskCount===5) aaa = bbb;//this
|
|
133
|
+
if(taskCount===5) aaa = bbb;//this ReferenceError will be captured by multi-tasks
|
|
125
134
|
|
|
126
135
|
return {data:'succ'};
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
//Example4, timeout: a task that does not finish within taskTimeout is
|
|
139
|
+
// treated as failed with a timeout error
|
|
140
|
+
multiTasks({
|
|
141
|
+
initialTasks: alltasks,
|
|
142
|
+
processTask,
|
|
143
|
+
taskRootFolder: `../examples-tmp-data/example-timeout`,
|
|
144
|
+
taskId: 'my-task',
|
|
145
|
+
numberOfWorkers: 3,
|
|
146
|
+
taskTimeout: 30000,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
//Example5, auto-retry a failed task with maxTaskRetries
|
|
136
150
|
multiTasks({
|
|
137
151
|
initialTasks: alltasks,
|
|
138
152
|
processTask,
|
|
@@ -142,13 +156,7 @@ multiTasks({
|
|
|
142
156
|
maxTaskRetries: 2,
|
|
143
157
|
});
|
|
144
158
|
|
|
145
|
-
//Example6, broadcast events between workers
|
|
146
|
-
// a worker emits an event and the master relays it to every worker
|
|
147
|
-
// (best-effort, runtime only - not persisted, not replayed on
|
|
148
|
-
// resume); by default the sender also receives its own event, pass
|
|
149
|
-
// {includingMe: false} to exclude it; each event keeps only one
|
|
150
|
-
// listener per worker process, so calling setListener on every
|
|
151
|
-
// task is safe
|
|
159
|
+
//Example6, broadcast events between workers with helper.emit/helper.setListener
|
|
152
160
|
multiTasks({
|
|
153
161
|
initialTasks: alltasks,
|
|
154
162
|
taskRootFolder: `../examples-tmp-data/example-broadcast`,
|
|
@@ -163,12 +171,7 @@ multiTasks({
|
|
|
163
171
|
},
|
|
164
172
|
});
|
|
165
173
|
|
|
166
|
-
//Example7, system events from a worker to the master
|
|
167
|
-
// helper.emitSys sends a system event that the master consumes
|
|
168
|
-
// itself (not relayed to workers); the built-in event
|
|
169
|
-
// TERMINATE_ALL_WORKERS force-kills all workers and exits the
|
|
170
|
-
// master (unfinished tasks stay for resume); any other event is
|
|
171
|
-
// passed to config.setSysListener on the master
|
|
174
|
+
//Example7, system events from a worker to the master with helper.emitSys
|
|
172
175
|
multiTasks({
|
|
173
176
|
initialTasks: alltasks,
|
|
174
177
|
taskRootFolder: `../examples-tmp-data/example-sysevent`,
|
|
@@ -191,12 +194,12 @@ multiTasks({
|
|
|
191
194
|
|
|
192
195
|
### Resuming
|
|
193
196
|
|
|
194
|
-
|
|
197
|
+
If the execution is interrupted (e.g. a power outage), resume it like this:
|
|
195
198
|
|
|
196
199
|
```javascript
|
|
197
200
|
|
|
198
201
|
multiTasks({
|
|
199
|
-
initialTasks: `/myworks/my_scan_tasks/`, //
|
|
202
|
+
initialTasks: `/myworks/my_scan_tasks/`, //point 'initialTasks' to the interrupted task directory; multi-tasks reads the tasks in its 'new' folder and continues execution
|
|
200
203
|
...
|
|
201
204
|
...//Other configurations remain unchanged
|
|
202
205
|
...
|
|
@@ -204,12 +207,10 @@ multiTasks({
|
|
|
204
207
|
|
|
205
208
|
```
|
|
206
209
|
|
|
207
|
-
### Testing:
|
|
208
|
-
|
|
209
|
-
The usage patterns documented above are covered by automated tests (unit tests in `test/`, end-to-end tests in `teste2e/`). The e2e suite runs the API in real child processes with a mixed workload — successful tasks (both promise and non-promise results), planned failures (rejected promises and thrown exceptions), dynamically created tasks via `helper.createNewTasks`, and workers that crash randomly — then resumes repeatedly and verifies that every task lands exactly one result in `results/succ` or `results/errors`.
|
|
210
|
-
|
|
211
210
|
### Changelog:
|
|
212
211
|
|
|
212
|
+
- 3.1.2 Update README
|
|
213
|
+
- 3.1.1 Fix readme documentation
|
|
213
214
|
- 3.1.0 Support worker broadcast ('helper.emit' and 'helper.setListener') and system events ('helper.emitSys' and 'setSysListener', with built-in 'TERMINATE_ALL_WORKERS'); default numberOfWorkers is now "50%" of CPU cores (was core count minus 1)
|
|
214
215
|
- 3.0.4 numberOfWorkers accepts a percentage string of CPU cores, e.g. "50%"
|
|
215
216
|
- 3.0.3 Support 'maxTaskRetries'
|
|
@@ -253,4 +254,4 @@ The usage patterns documented above are covered by automated tests (unit tests i
|
|
|
253
254
|
|
|
254
255
|
### License:
|
|
255
256
|
|
|
256
|
-
[MIT](https://opensource.org/license/MIT)
|
|
257
|
+
[MIT](https://opensource.org/license/MIT)
|