multi-tasks 2.0.9 → 2.1.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 CHANGED
@@ -14,8 +14,7 @@ How to use:
14
14
  let multiTasks = require('multi-tasks').multiTasks;
15
15
 
16
16
 
17
- //Step1, if you have tasks that need to be executed simultaneously,
18
- // provide them as an array, multi-tasks will automatically split them and execute.
17
+ //Step1, create your tasks that need to be executed simultaneously as an array.
19
18
  let alltasks = [];
20
19
  for(let i=0;i<50;i++){
21
20
  alltasks.push({
@@ -26,9 +25,12 @@ for(let i=0;i<50;i++){
26
25
 
27
26
  //Step2, provide a function to process a certain sub-task and return the result data
28
27
  let processTask = (task, helper)=>{
29
- let {taskCount} = task;
28
+ let {taskCount} = task;//get your task data
29
+
30
+ //Run your processing logic here...
30
31
 
31
- return 'a result which is not a promise';
32
+ //then return the result as a plain javascript object or an async promise
33
+ return 'result data';
32
34
  };
33
35
 
34
36
  //Step3, run!
@@ -109,6 +111,8 @@ let processTask = (task, helper)=>{
109
111
 
110
112
  Changelog:
111
113
 
114
+ - 2.1.1 Fixbug: recreate a new one when a worker collapsed unexpectedly.
115
+ - 2.1.0 Add a new output directory "log" where you can view the process time of each subtask
112
116
  - 2.0.9 Update readme examples
113
117
  - 2.0.8 Update readme examples
114
118
  - 2.0.7 Update changelog
@@ -13,7 +13,7 @@ for(let i=0;i<50;i++){
13
13
  let processTask = (task, helper)=>{
14
14
  let {taskCount} = task;
15
15
 
16
- return 'a result which is not a promise';
16
+ return 'result data';
17
17
  };
18
18
 
19
19
  //Step2.1, or return a promise
@@ -76,10 +76,10 @@ let processTask = (task, helper)=>{
76
76
 
77
77
  //Step3, run!
78
78
  multiTasks.start({
79
- tasks: alltasks,
79
+ initialTasks: alltasks,
80
80
  processTask,
81
81
  taskRootFolder: `../examples-tmp-data/example0`, //a directory to store progress and results files, you can check the progress here
82
- taskId: 'my-task',
82
+ taskId: 'my-readme-task',
83
83
  numberOfWorkers: 3, //Assign how many workers are working in parallel
84
84
  //autoCloseAfterCompletion: true, //if you have dynamically generated new tasks, put this as false
85
85
  onFinish: (report)=>{
@@ -25,6 +25,12 @@ let processTask = (task, helper)=>{
25
25
  if(taskCount===5) aaa = bbb;
26
26
 
27
27
  if(taskCount===6) return 'a result which is not a promise'; //Return a non-promise result is also OK
28
+
29
+ if(taskCount===7) {return new Promise((resolve, reject)=>{//async exception
30
+ setTimeout(()=>{
31
+ a=beforeAll;
32
+ }, 10 * Math.random())
33
+ });}
28
34
 
29
35
  //by default, you should return a promise, all returned data can be found in the results/succ folder
30
36
  return new Promise((resolve, reject)=>{
@@ -44,7 +50,7 @@ multiTasks({
44
50
  initialTasks: alltasks,
45
51
  processTask,
46
52
  taskRootFolder: `../examples-tmp-data/example0`, //a directory to store progress and results files, you can check the progress here
47
- taskId: 'my-task',
53
+ taskId: 'my-task' + Math.random(),
48
54
  numberOfWorkers: 3, //Assign how many workers are working in parallel
49
55
  //autoCloseAfterCompletion: true,
50
56
  onFinish: (report)=>{
@@ -0,0 +1,53 @@
1
+ import {multiTasks} from '../../index.js';
2
+
3
+ //Step1, If you have tasks that need to be executed simultaneously, please provide them as an array, multi-tasks will automatically split them and execute.
4
+ let alltasks = [];
5
+ for(let i=0;i<50;i++){
6
+ let task_props = {
7
+ index: i,
8
+ name: `task-${i}`,
9
+ description: `This prop is for a subtask`
10
+ };
11
+ alltasks.push(task_props);
12
+ }
13
+
14
+ //Step2, Provide a process function to handle a certain sub-task and return the result data
15
+ let processTask = (task, helper)=>{
16
+ //console.log('this is the subtask data you created above:', task);
17
+ let {taskCount} = task;
18
+
19
+ console.log(taskCount)
20
+
21
+
22
+ //This is the demo of exceptions/errors, they will be captured and saved in the results/errors folder
23
+ if(taskCount===3) throw 'exception';
24
+ if(taskCount===4) return Promise.reject({err:'a test error'});
25
+ if(taskCount===5) aaa = bbb;
26
+
27
+ if(taskCount===6) return 'a result which is not a promise'; //Return a non-promise result is also OK
28
+
29
+ //by default, you should return a promise, all returned data can be found in the results/succ folder
30
+ return new Promise((resolve, reject)=>{
31
+ setTimeout(()=>{
32
+ if(taskCount % 2 === 0)helper.createNewTasks({//
33
+ msg:'a new task'
34
+ });
35
+ taskCount === 2 ? reject('a rejected error') : resolve({
36
+ message: 'this is a result from a demo, random data=' + Math.random()
37
+ });
38
+ }, 10 * Math.random())
39
+ })
40
+ }
41
+
42
+ //Step3, run!
43
+ multiTasks({
44
+ initialTasks: alltasks,
45
+ processTask,
46
+ taskRootFolder: `../examples-tmp-data/example0`, //a directory to store progress and results files, you can check the progress here
47
+ taskId: 'my-task',
48
+ numberOfWorkers: 3, //Assign how many workers are working in parallel
49
+ //autoCloseAfterCompletion: true,
50
+ onFinish: (report)=>{
51
+ console.log('finish callback', report);
52
+ }
53
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-tasks",
3
- "version": "2.0.9",
3
+ "version": "2.1.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -35,7 +35,7 @@ let processTask = (task, helper)=>{
35
35
  taskCount === 2 ? reject('a rejected error') : resolve({
36
36
  message: 'this is a result from a demo, random data=' + Math.random()
37
37
  });
38
- }, 10 * Math.random())
38
+ }, 500 * Math.random())
39
39
  })
40
40
  }
41
41
 
@@ -1,4 +1,5 @@
1
1
  #!/bin/bash
2
+ rm -rf ./tmp
2
3
  node step1_start_and_crash
3
4
  sleep 3
4
5
  node step2_resume
@@ -61,58 +61,68 @@ const start = (config)=>{
61
61
  const AsyncQueue = require(`../utils/asyncQueue`);
62
62
  const queue = new AsyncQueue();
63
63
 
64
+ let initWorker = (worker)=>{
65
+ WorkerMgr.addWorker(worker);
66
+ worker.on('message', function(message) {
67
+ if(message === MSG_REQUEST_NEW_TASK){
68
+ queue.enqueue(async ()=>{
69
+ return TaskMgr.updateNewTasks();
70
+ });//use queue to make sure no I/O conflict
71
+ queue.enqueue(async () => {
72
+ return TaskMgr.popNewTask().then((subTask)=>{
73
+ if(!subTask){
74
+ //no tasks left
75
+ console.log('[Master]: no tasks left!');
76
+ //console.log('workers', cluster.workers);
77
+ if(config.autoCloseAfterCompletion){
78
+ killWorker(worker);
79
+ }else{//waiting for new tasks
80
+ worker.send(MSG_NO_TASK_FOUND,()=>{}, {
81
+ keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
82
+ });
83
+ }
84
+ }else{
85
+ let terminateData = {taskCount,subTask,config, id: worker.id};
86
+ if(USER_CONFIG.shouldTerminate && USER_CONFIG.shouldTerminate(terminateData)===true) {
87
+ killAllWorkers();
88
+ return process.exit(0);
89
+ }
90
+ //send new tasks to the worker
91
+ worker.send({
92
+ taskCount,
93
+ startTimestamp: new Date()*1,
94
+ config,
95
+ subTask
96
+ },()=>{}, {
97
+ keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
98
+ });
99
+ taskCount++;
100
+ }
101
+ });
102
+ });
103
+ }else
104
+ if(message.MSG_SET_GLOBAL_PARAMS){
105
+ console.log('xxxxxxxxxxxxxxxxxxxxxxxxxx', message)
106
+ }
107
+ });
108
+ };
109
+
64
110
  let initPromise = config.__resume ? asMaster.resume(config) : asMaster.init(config);
65
111
  initPromise.then((updated_config)=>{
66
112
  console.log(` new tasks=${TaskMgr.getTasks('new').length}`);
67
113
  config = updated_config;
68
114
  for (let i = 0; i < numOfWorkers; i++) {
69
115
  let worker = cluster.fork();
70
- WorkerMgr.addWorker(worker);
71
- worker.on('message', function(message) {
72
- if(message === MSG_REQUEST_NEW_TASK){
73
- queue.enqueue(async ()=>{
74
- return TaskMgr.updateNewTasks();
75
- });//use queue to make sure no I/O conflict
76
- queue.enqueue(async () => {
77
- return TaskMgr.popNewTask().then((subTask)=>{
78
- if(!subTask){
79
- //no tasks left
80
- console.log('[Master]: no tasks left!');
81
- //console.log('workers', cluster.workers);
82
- if(config.autoCloseAfterCompletion){
83
- killWorker(worker);
84
- }else{//waiting for new tasks
85
- worker.send(MSG_NO_TASK_FOUND,()=>{}, {
86
- keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
87
- });
88
- }
89
- }else{
90
- let terminateData = {taskCount,subTask,config, id: worker.id};
91
- if(USER_CONFIG.shouldTerminate && USER_CONFIG.shouldTerminate(terminateData)===true) {
92
- killAllWorkers();
93
- return process.exit(0);
94
- }
95
- //send new tasks to the worker
96
- worker.send({
97
- taskCount,
98
- startTimestamp: new Date()*1,
99
- config,
100
- subTask
101
- },()=>{}, {
102
- keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
103
- });
104
- taskCount++;
105
- }
106
- });
107
- });
108
- }else
109
- if(message.MSG_SET_GLOBAL_PARAMS){
110
- console.log('xxxxxxxxxxxxxxxxxxxxxxxxxx', message)
111
- }
112
- });
116
+ initWorker(worker);
113
117
  }
114
118
  });
115
- cluster.on('exit', (worker, code, signal) => {
119
+ cluster.on('exit', (worker, code, signal) => {
120
+ if (code !== 0 && !worker.exitedAfterDisconnect) {
121
+ console.log('Worker died unexpectedly starting a new one...');
122
+ let worker = cluster.fork();
123
+ initWorker(worker);
124
+ return;
125
+ }
116
126
  let allkilled = true;
117
127
  for (const worker of Object.values(cluster.workers)) {
118
128
  allkilled = allkilled && worker.killed;
@@ -156,9 +166,12 @@ function subWorker(){
156
166
  let sendMsg = (msg)=>{
157
167
  worker.send(msg);
158
168
  };
159
- runTask(config, subTask, sendMsg).then((report)=>{
160
- let endTimestamp = new Date()*1;
161
- let cost = startTimestamp - endTimestamp;
169
+ let subTaskLog = {
170
+ taskCount,
171
+ startTimestamp
172
+ };
173
+ runTask(config, subTask, subTaskLog, sendMsg).then((tasklog)=>{
174
+ let {cost, startTimestamp, endTimestamp} = tasklog;
162
175
 
163
176
  console.log(`[Task Finished] "${config.masterTaskId}"."${subTask.subid}".`, getDateTimeTxt(), `cost=${cost}ms`)
164
177
  }).then(()=>{
@@ -173,7 +186,7 @@ function subWorker(){
173
186
  worker.send(MSG_REQUEST_NEW_TASK);//request new task
174
187
 
175
188
  };
176
- function runTask(config, subTask, sendMsg){
189
+ function runTask(config, subTask, subTaskLog, sendMsg){
177
190
  return new Promise((resolve)=>{
178
191
  let resultPromise = Promise.resolve({});
179
192
  let resultData;
@@ -185,6 +198,7 @@ function runTask(config, subTask, sendMsg){
185
198
  params
186
199
  });
187
200
  };
201
+ TaskMgr.saveTaskLog(subTask.subid, subTaskLog);
188
202
  try{
189
203
  resultPromise = USER_CONFIG.processTask(subTask, helper);//rewrite this method in subclass
190
204
  }catch(e){
@@ -198,11 +212,24 @@ function runTask(config, subTask, sendMsg){
198
212
  if(typeof ex !== 'string') ex = ex.toString();
199
213
  errors.push({ex});
200
214
  }).finally(()=>{
215
+ let {startTimestamp} = subTaskLog;
216
+ let endTimestamp = new Date()*1;
217
+ let cost = endTimestamp - startTimestamp;
218
+ subTaskLog = {
219
+ ...subTaskLog,
220
+ endTimestamp,
221
+ cost
222
+ };
201
223
  let hasError = errors.length > 0;
202
- if(hasError)console.log('hasError=====', hasError, errors)
224
+ if(hasError)console.log('hasError=====', hasError, errors);
225
+ TaskMgr.saveTaskLog(subTask.subid, subTaskLog);
203
226
  TaskMgr.saveTaskResult(subTask.subid, hasError, hasError ? errors : resultData);
204
227
  TaskMgr.mvTask(subTask.subid, 'running',(hasError ? 'finished_with_errors':'finished'), (newPath)=>{
205
- resolve();
228
+ resolve({
229
+ startTimestamp,
230
+ endTimestamp,
231
+ cost
232
+ });
206
233
  });
207
234
  });
208
235
  });
@@ -43,19 +43,21 @@ const TaskMgr = {
43
43
  };
44
44
  //let taskfolder = pathutil.parse(__filename).dir;
45
45
  //let taskFolder = pathutil.resolve(taskfolder, `./tasks/${masterTaskId}`);
46
- progressRootFolder = pathutil.resolve(taskFolder, './progress');
47
- resultsRootFolder = pathutil.resolve(taskFolder, './results');
46
+ const progressRootFolder = pathutil.resolve(taskFolder, './progress');
47
+ const resultsRootFolder = pathutil.resolve(taskFolder, './results');
48
+ const logsRootFolder = pathutil.resolve(taskFolder, './logs');
48
49
 
49
- taskFolderNew = pathutil.resolve(progressRootFolder, './new');
50
- taskFolderRunning = pathutil.resolve(progressRootFolder, './running');
51
- taskFolderFinished = pathutil.resolve(progressRootFolder, './finished');
52
- taskFolderFailed= pathutil.resolve(progressRootFolder, './finished_with_errors');
50
+ const taskFolderNew = pathutil.resolve(progressRootFolder, './new');
51
+ const taskFolderRunning = pathutil.resolve(progressRootFolder, './running');
52
+ const taskFolderFinished = pathutil.resolve(progressRootFolder, './finished');
53
+ const taskFolderFailed= pathutil.resolve(progressRootFolder, './finished_with_errors');
53
54
 
54
- taskFolderResult = pathutil.resolve(resultsRootFolder, './succ');
55
- taskFolderResultFailed = pathutil.resolve(resultsRootFolder, './errors');
55
+ const taskFolderResult = pathutil.resolve(resultsRootFolder, './succ');
56
+ const taskFolderResultFailed = pathutil.resolve(resultsRootFolder, './errors');
56
57
  //let mainRptFolder = pathutil.resolve(taskfolder, './report');
57
58
 
58
59
  //makeDir.sync(mainRptFolder);
60
+ makeDir.sync(logsRootFolder);
59
61
  makeDir.sync(taskFolderNew);
60
62
  makeDir.sync(taskFolderRunning);
61
63
  makeDir.sync(taskFolderFinished);
@@ -68,6 +70,7 @@ const TaskMgr = {
68
70
  taskFolder,
69
71
  progressRootFolder,
70
72
  resultsRootFolder,
73
+ logsRootFolder,
71
74
 
72
75
  taskFolderNew,
73
76
  taskFolderRunning,
@@ -208,6 +211,19 @@ const TaskMgr = {
208
211
  fcontent = JSON.stringify(data);
209
212
  };
210
213
  fs.writeFileSync(fpath, fcontent);
214
+ },
215
+ saveTaskLog:(subid, log)=>{
216
+ let {logsRootFolder} = main_config;
217
+ let folder = logsRootFolder;
218
+ let fpath = pathutil.resolve(folder, `${subid}.json`);
219
+ let fcontent;
220
+ log.subid = subid;
221
+ if(typeof log === 'undefined') {
222
+ fcontent = '';
223
+ }else{
224
+ fcontent = JSON.stringify(log);
225
+ };
226
+ fs.writeFileSync(fpath, fcontent);
211
227
  }
212
228
  };
213
229
  module.exports = TaskMgr;