multi-tasks 2.0.1 → 2.0.3

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 (36) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +143 -138
  3. package/cmd.js +41 -41
  4. package/examples/example0/run-readme.js +87 -87
  5. package/examples/example0/run.js +52 -52
  6. package/examples/example1/RunnerConsumer.js +25 -25
  7. package/examples/example1/RunnerProducer.js +34 -34
  8. package/examples/example1/run.js +8 -8
  9. package/examples/example2/RunnerConsumer.js +39 -39
  10. package/examples/example2/RunnerProducer.js +34 -34
  11. package/examples/example2/run.js +22 -22
  12. package/index.js +3 -3
  13. package/monitors/cli.js +28 -28
  14. package/package.json +23 -23
  15. package/test/splitter.test.js +16 -16
  16. package/test/test_resume/step1_start_and_crash.js +56 -0
  17. package/test/test_resume/step2_resume.js +21 -0
  18. package/test/test_resume/test.sh +3 -0
  19. package/todo.txt +3 -3
  20. package/utils/asyncQueue.js +35 -31
  21. package/utils/backward-v1.js +10 -10
  22. package/utils/makedir.js +40 -50
  23. package/utils/randoms.js +21 -15
  24. package/utils/test/test_asyncqueue.js +115 -115
  25. package/utils/test_makedir.js +2 -2
  26. package/workers/Multithread.js +196 -176
  27. package/workers/TaskMgr.js +212 -181
  28. package/workers/WorkerMgr.js +30 -30
  29. package/workers/asMaster.js +122 -70
  30. package/workers/asWorker.js +13 -13
  31. package/workers/helper.js +21 -21
  32. package/workers/index.js +45 -7
  33. package/examples/example3-dynamicIncreasing/RunnerConsumer.js +0 -176
  34. package/examples/example3-dynamicIncreasing/RunnerProducer.js +0 -49
  35. package/examples/example3-dynamicIncreasing/run.js +0 -36
  36. package/examples/example3-dynamicIncreasing/util.js +0 -46
@@ -1,177 +1,197 @@
1
- var fs = require('fs');
2
- var pathutil = require('path');
3
- const cluster = require('cluster');
4
- const numCPUs = require('os').cpus().length;
5
-
6
- let MSG_REQUEST_NEW_TASK = 'request_new_task';
7
- let MSG_NO_TASK_FOUND = 'no_task_found';
8
-
9
- const TaskMgr = require('./TaskMgr');
10
- const WorkerMgr = require('./WorkerMgr');
11
- const helper = require('./helper');
12
-
13
- let fmtdigit = (n)=>{
14
- return n > 9 ? "" + n: "0" + n;
15
- };
16
- let getDateTimeTxt = ()=>{
17
- let d=new Date();
18
- let YYYY=d.getFullYear()
19
- let MM=fmtdigit(d.getMonth()+1);
20
- let DD=fmtdigit(d.getDate());
21
- let HH=fmtdigit(d.getHours());
22
- let mm=fmtdigit(d.getMinutes());
23
- let ss=fmtdigit(d.getSeconds());
24
- //return moment().format('YYYY-MM-DD HH:mm:ss');
25
- return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`;
26
- };
27
-
28
- let USER_CONFIG;
29
-
30
- let taskCount = 0;
31
-
32
- const start = (config)=>{
33
- USER_CONFIG = config;//this will remain user defined functions
34
- let isMaster = (cluster.isPrimary || cluster.isMaster);
35
-
36
- if (isMaster) {//init
37
- console.log('[Master]: Start!!');
38
- let PID = process.pid;
39
- let numOfWorkers = config.numberOfWorkers;
40
- console.log(` numCPUs=${numCPUs}`);
41
- console.log(` numOfWorkers=${numOfWorkers}`);
42
- console.log(` PID=${PID}`);
43
-
44
- const AsyncQueue = require(`../utils/asyncQueue`);
45
- const queue = new AsyncQueue();
46
-
47
-
48
- require('./asMaster').init(config).then((updated_config)=>{
49
- console.log(` new tasks=${TaskMgr.getTasks('new').length}`);
50
- config = updated_config;
51
- for (let i = 0; i < numOfWorkers; i++) {
52
- let worker = cluster.fork();
53
- WorkerMgr.addWorker(worker);
54
- worker.on('message', function(message) {
55
- if(message === MSG_REQUEST_NEW_TASK){
56
- queue.enqueue(async ()=>{
57
- return TaskMgr.updateNewTasks();
58
- });//use queue to make sure no I/O conflict
59
- queue.enqueue(async () => {
60
- return TaskMgr.popNewTask().then((subTask)=>{
61
- if(!subTask){
62
- //no tasks left
63
- console.log('[Master]: no tasks left!');
64
- //console.log('workers', cluster.workers);
65
- if(config.autoCloseAfterCompletion){
66
- worker.kill();
67
- }else{//waiting for new tasks
68
- worker.send(MSG_NO_TASK_FOUND,()=>{}, {
69
- keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
70
- });
71
- }
72
- //
73
- }else{
74
- //send new tasks to the worker
75
- worker.send({
76
- taskCount,
77
- startTimestamp: new Date()*1,
78
- config,
79
- subTask
80
- },()=>{}, {
81
- keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
82
- });
83
- taskCount++;
84
- }
85
- });
86
- });
87
- }
88
- });
89
- }
90
- });
91
- cluster.on('exit', (worker, code, signal) => {
92
- let allkilled = true;
93
- for (const worker of Object.values(cluster.workers)) {
94
- allkilled = allkilled && worker.killed;
95
- }
96
- if(allkilled){
97
- if(USER_CONFIG.onFinish){
98
- USER_CONFIG.onFinish({});
99
- delete USER_CONFIG.onFinish;
100
- }
101
- setTimeout(()=>{
102
- console.log('[Master]: all workers are done, process exit.')
103
- process.exit(0);
104
- }, 10);
105
- }
106
- });
107
- }else{
108
- subWorker();
109
- }
110
- };
111
- function subWorker(){
112
- let worker = cluster.worker;
113
- //console.log(`>[w]=${process.pid} started`);
114
- process.on('message', function(message) {
115
- //
116
- });
117
- worker.on('message', function(message) {
118
- if(message === MSG_NO_TASK_FOUND){//wait for new task
119
- console.log(` (Worker #${worker.id}): waiting`);
120
- setTimeout(()=>{
121
- worker.send(MSG_REQUEST_NEW_TASK);//request new task
122
- }, 5000 + Math.random()*5000);
123
- }else
124
- if(message.subTask){//new task
125
- let {config, subTask, startTimestamp, taskCount} = message;
126
- TaskMgr.load(config);
127
- //console.log('ssss', subTask)
128
- if(subTask){
129
- subTask.index = taskCount;//for v1 backward
130
- subTask.taskCount = taskCount;
131
- console.log(`[Task Start]: "${subTask.subid}"`, `pid=${process.pid}`, getDateTimeTxt());
132
- runTask(config, subTask, message).then((report)=>{
133
- let endTimestamp = new Date()*1;
134
- let cost = startTimestamp - endTimestamp;
135
-
136
- console.log(`[Task Finished] "${config.masterTaskId}"."${subTask.subid}".`, getDateTimeTxt(), `cost=${cost}ms`)
137
- }).then(()=>{
138
- worker.send(MSG_REQUEST_NEW_TASK);//request new task
139
- });
140
- }else{
141
- console.log('Can not find: message.subTask');
142
- throw 'can not find subTask!'
143
- }
144
- }
145
- });
146
- worker.send(MSG_REQUEST_NEW_TASK);//request new task
147
-
148
- };
149
- function runTask(config, subTask, message){
150
- return new Promise((resolve)=>{
151
- let resultPromise = Promise.resolve({});
152
- let resultData;
153
- let errors = [];
154
- helper.load(config);
155
- try{
156
- resultPromise = USER_CONFIG.processTask(subTask, helper);//rewrite this method in subclass
157
- }catch(e){
158
- errors.push({subTask, exception: 'Exception:' + e.toString()});
159
- }
160
- let ispromise = resultPromise instanceof Promise;
161
- if(!ispromise){resultPromise = Promise.resolve(resultPromise);}//convert data to promise
162
- resultPromise.then((data)=>{
163
- resultData = data;
164
- }).catch((ex)=>{
165
- if(typeof ex !== 'string') ex = ex.toString();
166
- errors.push({ex});
167
- }).finally(()=>{
168
- let hasError = errors.length > 0;
169
- if(hasError)console.log('hasError=====', hasError, errors)
170
- TaskMgr.saveTaskResult(subTask.subid, hasError, hasError ? errors : resultData);
171
- TaskMgr.mvTask(subTask.subid, 'running',(hasError ? 'finished_with_errors':'finished'), (newPath)=>{
172
- resolve();
173
- });
174
- });
175
- });
176
- };
1
+ var fs = require('fs');
2
+ var pathutil = require('path');
3
+ const cluster = require('cluster');
4
+ const numCPUs = require('os').cpus().length;
5
+
6
+ let MSG_REQUEST_NEW_TASK = 'request_new_task';
7
+ let MSG_NO_TASK_FOUND = 'no_task_found';
8
+
9
+ const asMaster = require('./asMaster');
10
+ const TaskMgr = require('./TaskMgr');
11
+ const WorkerMgr = require('./WorkerMgr');
12
+ const helper = require('./helper');
13
+
14
+ let fmtdigit = (n)=>{
15
+ return n > 9 ? "" + n: "0" + n;
16
+ };
17
+ let getDateTimeTxt = ()=>{
18
+ let d=new Date();
19
+ let YYYY=d.getFullYear()
20
+ let MM=fmtdigit(d.getMonth()+1);
21
+ let DD=fmtdigit(d.getDate());
22
+ let HH=fmtdigit(d.getHours());
23
+ let mm=fmtdigit(d.getMinutes());
24
+ let ss=fmtdigit(d.getSeconds());
25
+ //return moment().format('YYYY-MM-DD HH:mm:ss');
26
+ return `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`;
27
+ };
28
+
29
+ let USER_CONFIG;
30
+
31
+ let taskCount = 0;
32
+
33
+ let killAllWorkers = ()=>{
34
+ for (const id in cluster.workers) {
35
+ if (cluster.workers.hasOwnProperty(id)) {
36
+ const worker = cluster.workers[id];
37
+ console.log(`Killing worker ${worker.process.pid}`);
38
+ killWorker(worker);
39
+ }
40
+ }
41
+ };
42
+ let killWorker = (worker)=>{
43
+ worker.disconnect();
44
+ //worker.process.kill();
45
+ worker.kill();
46
+ };
47
+
48
+ const start = (config)=>{
49
+ USER_CONFIG = config;//this will remain user defined functions
50
+ let isMaster = (cluster.isPrimary || cluster.isMaster);
51
+
52
+ if (isMaster) {//init
53
+ console.log('[Master]: Start!!');
54
+ let PID = process.pid;
55
+ let numOfWorkers = config.numberOfWorkers;
56
+ console.log(` numCPUs=${numCPUs}`);
57
+ console.log(` numOfWorkers=${numOfWorkers}`);
58
+ console.log(` PID=${PID}`);
59
+
60
+ const AsyncQueue = require(`../utils/asyncQueue`);
61
+ const queue = new AsyncQueue();
62
+
63
+ let initPromise = config.__resume ? asMaster.resume(config) : asMaster.init(config);
64
+ initPromise.then((updated_config)=>{
65
+ console.log(` new tasks=${TaskMgr.getTasks('new').length}`);
66
+ config = updated_config;
67
+ for (let i = 0; i < numOfWorkers; i++) {
68
+ let worker = cluster.fork();
69
+ WorkerMgr.addWorker(worker);
70
+ worker.on('message', function(message) {
71
+ if(message === MSG_REQUEST_NEW_TASK){
72
+ queue.enqueue(async ()=>{
73
+ return TaskMgr.updateNewTasks();
74
+ });//use queue to make sure no I/O conflict
75
+ queue.enqueue(async () => {
76
+ return TaskMgr.popNewTask().then((subTask)=>{
77
+ if(!subTask){
78
+ //no tasks left
79
+ console.log('[Master]: no tasks left!');
80
+ //console.log('workers', cluster.workers);
81
+ if(config.autoCloseAfterCompletion){
82
+ killWorker(worker);
83
+ }else{//waiting for new tasks
84
+ worker.send(MSG_NO_TASK_FOUND,()=>{}, {
85
+ keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
86
+ });
87
+ }
88
+ }else{
89
+ let terminateData = {taskCount,subTask,config, id: worker.id};
90
+ if(USER_CONFIG.shouldTerminate && USER_CONFIG.shouldTerminate(terminateData)===true) {
91
+ killAllWorkers();
92
+ return process.exit(0);
93
+ }
94
+ //send new tasks to the worker
95
+ worker.send({
96
+ taskCount,
97
+ startTimestamp: new Date()*1,
98
+ config,
99
+ subTask
100
+ },()=>{}, {
101
+ keepOpen: (typeof config.keepOpen === 'undefined') ? false : config.keepOpen
102
+ });
103
+ taskCount++;
104
+ }
105
+ });
106
+ });
107
+ }
108
+ });
109
+ }
110
+ });
111
+ cluster.on('exit', (worker, code, signal) => {
112
+ let allkilled = true;
113
+ for (const worker of Object.values(cluster.workers)) {
114
+ allkilled = allkilled && worker.killed;
115
+ }
116
+ if(allkilled){
117
+ if(USER_CONFIG.onFinish){
118
+ USER_CONFIG.onFinish({});
119
+ delete USER_CONFIG.onFinish;
120
+ }
121
+ setTimeout(()=>{
122
+ console.log('[Master]: all workers are done, process exit.')
123
+ process.exit(0);
124
+ }, 10);
125
+ }
126
+ });
127
+ }else{
128
+ subWorker();
129
+ }
130
+ };
131
+ function subWorker(){
132
+ let worker = cluster.worker;
133
+ //console.log(`>[w]=${process.pid} started`);
134
+ process.on('message', function(message) {
135
+ //
136
+ });
137
+ worker.on('message', function(message) {
138
+ if(message === MSG_NO_TASK_FOUND){//wait for new task
139
+ console.log(` (Worker #${worker.id}): waiting`);
140
+ setTimeout(()=>{
141
+ worker.send(MSG_REQUEST_NEW_TASK);//request new task
142
+ }, 5000 + Math.random()*5000);
143
+ }else
144
+ if(message.subTask){//new task
145
+ let {config, subTask, startTimestamp, taskCount} = message;
146
+ TaskMgr.load(config);
147
+ //console.log('ssss', subTask)
148
+ if(subTask){
149
+ subTask.index = taskCount;//for v1 backward
150
+ subTask.taskCount = taskCount;
151
+ console.log(`[Task Start]: "${subTask.subid}"`, `pid=${process.pid}`, getDateTimeTxt());
152
+ runTask(config, subTask, message).then((report)=>{
153
+ let endTimestamp = new Date()*1;
154
+ let cost = startTimestamp - endTimestamp;
155
+
156
+ console.log(`[Task Finished] "${config.masterTaskId}"."${subTask.subid}".`, getDateTimeTxt(), `cost=${cost}ms`)
157
+ }).then(()=>{
158
+ worker.send(MSG_REQUEST_NEW_TASK);//request new task
159
+ });
160
+ }else{
161
+ console.log('Can not find: message.subTask');
162
+ throw 'can not find subTask!'
163
+ }
164
+ }
165
+ });
166
+ worker.send(MSG_REQUEST_NEW_TASK);//request new task
167
+
168
+ };
169
+ function runTask(config, subTask, message){
170
+ return new Promise((resolve)=>{
171
+ let resultPromise = Promise.resolve({});
172
+ let resultData;
173
+ let errors = [];
174
+ helper.load(config);
175
+ try{
176
+ resultPromise = USER_CONFIG.processTask(subTask, helper);//rewrite this method in subclass
177
+ }catch(e){
178
+ errors.push({subTask, exception: 'Exception:' + e.toString()});
179
+ }
180
+ let ispromise = resultPromise instanceof Promise;
181
+ if(!ispromise){resultPromise = Promise.resolve(resultPromise);}//convert data to promise
182
+ resultPromise.then((data)=>{
183
+ resultData = data;
184
+ }).catch((ex)=>{
185
+ if(typeof ex !== 'string') ex = ex.toString();
186
+ errors.push({ex});
187
+ }).finally(()=>{
188
+ let hasError = errors.length > 0;
189
+ if(hasError)console.log('hasError=====', hasError, errors)
190
+ TaskMgr.saveTaskResult(subTask.subid, hasError, hasError ? errors : resultData);
191
+ TaskMgr.mvTask(subTask.subid, 'running',(hasError ? 'finished_with_errors':'finished'), (newPath)=>{
192
+ resolve();
193
+ });
194
+ });
195
+ });
196
+ };
177
197
  module.exports = {start};