multi-tasks 2.0.2 → 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.
- package/LICENSE +21 -21
- package/README.md +143 -140
- package/cmd.js +41 -41
- package/examples/example0/run-readme.js +87 -87
- package/examples/example0/run.js +52 -52
- package/examples/example1/RunnerConsumer.js +25 -25
- package/examples/example1/RunnerProducer.js +34 -34
- package/examples/example1/run.js +8 -8
- package/examples/example2/RunnerConsumer.js +39 -39
- package/examples/example2/RunnerProducer.js +34 -34
- package/examples/example2/run.js +22 -22
- package/index.js +3 -3
- package/monitors/cli.js +28 -28
- package/package.json +23 -23
- package/test/splitter.test.js +16 -16
- package/test/test_resume/step1_start_and_crash.js +56 -0
- package/test/test_resume/step2_resume.js +21 -0
- package/test/test_resume/test.sh +3 -0
- package/todo.txt +3 -3
- package/utils/asyncQueue.js +35 -35
- package/utils/backward-v1.js +10 -10
- package/utils/makedir.js +40 -54
- package/utils/randoms.js +21 -15
- package/utils/test/test_asyncqueue.js +115 -115
- package/utils/test_makedir.js +2 -2
- package/workers/Multithread.js +196 -197
- package/workers/TaskMgr.js +212 -185
- package/workers/WorkerMgr.js +30 -30
- package/workers/asMaster.js +122 -70
- package/workers/asWorker.js +13 -13
- package/workers/helper.js +21 -21
- package/workers/index.js +45 -42
package/workers/Multithread.js
CHANGED
|
@@ -1,198 +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
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let
|
|
19
|
-
let
|
|
20
|
-
let
|
|
21
|
-
let
|
|
22
|
-
let
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
worker.kill();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
let
|
|
55
|
-
|
|
56
|
-
console.log(`
|
|
57
|
-
console.log(`
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
queue
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}else{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
USER_CONFIG.onFinish
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
subTask.
|
|
151
|
-
subTask.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
let
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
let
|
|
173
|
-
let
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
TaskMgr.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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 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
|
+
};
|
|
198
197
|
module.exports = {start};
|