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.
Files changed (2) hide show
  1. package/README.md +56 -55
  2. 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. It manages progress and tasks based on the file system, which means that even if the host crashes, tasks can be resumed based on file records.
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
- - `multiTasks(config)` run tasks; auto-resumes if the task folder already exists or `initialTasks` points to one.
16
- - `multiTasks.start(config)` — always start a fresh run.
17
- - `multiTasks.resume(config)` — resume an interrupted run.
18
- - `multiTasks.restart(config)` — resume and also retry failed tasks (needs `config.taskFolder`).
19
- - `helper.createNewTasks(tasks)`create new tasks dynamically while processing.
20
- - `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.
21
- - `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.
22
- - `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`.
23
- - Config options:
24
- - `initialTasks` — array of task objects, or a folder path string to resume from.
25
- - `processTask(task, helper)` — required; return a value or a Promise.
26
- - `taskRootFolder`root directory where progress and result files are stored.
27
- - `taskId`task folder name under `taskRootFolder`.
28
- - `taskFolder`full task folder path, required by `restart`.
29
- - `numberOfWorkers`how many worker processes run in parallel; a number, or a percentage string of CPU cores like `"50%"` (default).
30
- - `taskTimeout`optional, milliseconds; an overdue task fails with a timeout error.
31
- - `maxTaskRetries`optional, max times a failed task is auto-retried.
32
- - `autoCloseAfterCompletion`set `false` if you create new tasks dynamically.
33
- - `shouldTerminate(info)`return `true` to terminate the whole process.
34
- - `setSysListener(event, payload, meta)` optional, handle custom system events (sent via `helper.emitSys`) on the master; `meta` has `fromWorkerId` and `fromWorkerPid`.
35
- - `onFinish(report)`called once after all workers are done.
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 your tasks that need to be executed simultaneously as an array.
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 to process a certain sub-task and return the result data
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 results files, you can check the progress here
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, put this as false
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
- //This is the demo of exceptions/errors, they will be captured and saved in the results/errors folder
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 undefined exception will be captured by multi-tasks
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
- //Example5, auto-retry a failed task:
130
- // when a task fails - its worker process crashes, processTask throws
131
- // or rejects, or it exceeds taskTimeout - it is sent back to the
132
- // queue and retried, at most maxTaskRetries times; when the limit
133
- // is reached it lands in finished_with_errors (crash:
134
- // {type:'worker_crash'}, timeout: {type:'timeout'}, processTask
135
- // error: the original error data)
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
- Sometimes the task execution is interrupted due to some reasons (such as power outage), you can resume the execution like this
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/`, //Point 'initialTasks' to the interrupted task directory, multi-tasks will read the tasks in the 'new' folder and initialize them to 'initialTasks' and then continue execution
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) (see [LICENSE](LICENSE))
257
+ [MIT](https://opensource.org/license/MIT)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-tasks",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "Multi-process task scheduling based on Node.js cluster, with crash resume and failed-task restart support",
5
5
  "main": "index.js",
6
6
  "files": [