multi-tasks 3.2.2 → 3.2.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/README.md +3 -0
- package/package.json +1 -1
- package/utils/resolvePriority.js +37 -0
- package/workers/Multithread.js +12 -1
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ multiTasks({
|
|
|
48
48
|
numberOfWorkers: 3, //how many workers are working in parallel
|
|
49
49
|
//taskTimeout: 30000, //optional, in milliseconds, an overdue task is treated as failed with a timeout error
|
|
50
50
|
//maxTaskRetries: 2, //optional, auto-retry a failed task (worker crash, processTask error, or timeout); retried tasks go back to the queue
|
|
51
|
+
//workerPriority: 'low', //optional, one of 'low' | 'below_normal' | 'normal' | 'above_normal' | 'high' (or a nice number -20~19); lower the OS scheduling priority of workers to keep the machine responsive during long batches
|
|
51
52
|
//autoCloseAfterCompletion: true, //set true to exit the process when all tasks are done; keep it false (default) if you create new tasks dynamically
|
|
52
53
|
shouldTerminate:(info)=>{
|
|
53
54
|
//return true if you need to terminate the whole process
|
|
@@ -291,6 +292,7 @@ The API has three parts: the entry functions, the config options, and the `helpe
|
|
|
291
292
|
- **`numberOfWorkers`** — how many worker processes run in parallel; a number, or a percentage string of CPU cores like `"50%"` (default).
|
|
292
293
|
- **`taskTimeout`** — optional, milliseconds; an overdue task fails with a timeout error.
|
|
293
294
|
- **`maxTaskRetries`** — optional, max times a failed task is auto-retried.
|
|
295
|
+
- **`workerPriority`** — optional, OS scheduling priority for worker processes: `'low' | 'below_normal' | 'normal' | 'above_normal' | 'high'`, or a nice number (integer -20~19). Lower priority keeps the machine responsive while a long batch occupies all cores; invalid values throw before any task folder is created.
|
|
294
296
|
- **`progressBar`** — `true` by default: shows a live single-line progress bar in the terminal (set `false` to disable); logs (including your own `console.*` calls) always go to `<taskFolder>/.sys/run.log`, and print to the console as well only when the bar is off or stdout is not a TTY.
|
|
295
297
|
- **`autoCloseAfterCompletion`** — set `true` to kill workers and exit the process when no tasks are left; default is `false`, i.e. workers stay alive waiting for dynamically created tasks.
|
|
296
298
|
- **`shouldTerminate(info)`** — return `true` to terminate the whole process.
|
|
@@ -313,6 +315,7 @@ The API has three parts: the entry functions, the config options, and the `helpe
|
|
|
313
315
|
|
|
314
316
|
### Changelog:
|
|
315
317
|
|
|
318
|
+
- 3.2.3 Support workerPriority: set OS scheduling priority for worker processes
|
|
316
319
|
- 3.2.2 initialTasks accepts a function (sync or async) that is called only once, in the master process
|
|
317
320
|
- 3.2.1 Fix retry bug: user-defined taskCount/index no longer overwritten on task retries
|
|
318
321
|
- 3.2.0 Add progressBar (on by default); console output diverted to run.log; new startup banner
|
package/package.json
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const PRIORITY_NAMES = {
|
|
6
|
+
low: os.constants.priority.PRIORITY_LOW,
|
|
7
|
+
below_normal: os.constants.priority.PRIORITY_BELOW_NORMAL,
|
|
8
|
+
normal: os.constants.priority.PRIORITY_NORMAL,
|
|
9
|
+
above_normal: os.constants.priority.PRIORITY_ABOVE_NORMAL,
|
|
10
|
+
high: os.constants.priority.PRIORITY_HIGH,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// 解析 workerPriority 配置:
|
|
14
|
+
// - undefined -> 返回 undefined(不设置优先级, 行为不变);
|
|
15
|
+
// - string -> 枚举 'low'|'below_normal'|'normal'|'above_normal'|'high'(允许首尾空白),
|
|
16
|
+
// 映射 os.constants.priority 常量;
|
|
17
|
+
// - number -> 须为 -20~19 的整数(os.setPriority 原生 nice 范围), 原样返回;
|
|
18
|
+
// - 其余 -> 抛 Error。
|
|
19
|
+
const resolveWorkerPriority = (value) => {
|
|
20
|
+
if (typeof value === 'undefined') {
|
|
21
|
+
return undefined;
|
|
22
|
+
};
|
|
23
|
+
if (typeof value === 'string') {
|
|
24
|
+
const name = value.trim();
|
|
25
|
+
if (Object.prototype.hasOwnProperty.call(PRIORITY_NAMES, name)) {
|
|
26
|
+
return PRIORITY_NAMES[name];
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
if (typeof value === 'number') {
|
|
30
|
+
if (Number.isInteger(value) && value >= -20 && value <= 19) {
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
throw new Error(`Invalid workerPriority: ${JSON.stringify(value)}. Expected 'low' | 'below_normal' | 'normal' | 'above_normal' | 'high', or an integer between -20 and 19.`);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = { resolveWorkerPriority };
|
package/workers/Multithread.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const cluster = require('cluster');
|
|
2
|
-
const
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const numCPUs = os.cpus().length;
|
|
3
4
|
|
|
4
5
|
let MSG_REQUEST_NEW_TASK = 'request_new_task';
|
|
5
6
|
let MSG_NO_TASK_FOUND = 'no_task_found';
|
|
@@ -9,6 +10,7 @@ const TaskMgr = require('./TaskMgr');
|
|
|
9
10
|
const WorkerMgr = require('./WorkerMgr');
|
|
10
11
|
const helper = require('./helper');
|
|
11
12
|
const { resolveNumberOfWorkers } = require('../utils/resolveWorkers');
|
|
13
|
+
const { resolveWorkerPriority } = require('../utils/resolvePriority');
|
|
12
14
|
const runLog = require('../utils/runLog');
|
|
13
15
|
const progressCtl = require('../utils/progressCtl');
|
|
14
16
|
const startupBanner = require('../utils/startupBanner');
|
|
@@ -57,6 +59,7 @@ const start = (config)=>{
|
|
|
57
59
|
progressCtl.reset();
|
|
58
60
|
runLog.reset();//runLog 已脱离 progressCtl: 不复位则新一轮日志继续写旧 run.log、console 仍被旧 hook 拦截
|
|
59
61
|
config.numberOfWorkers = resolveNumberOfWorkers(config.numberOfWorkers);//支持百分比字符串; 须在 fork 循环读值前折算, 非法值在此抛错
|
|
62
|
+
config.workerPriority = resolveWorkerPriority(config.workerPriority);//枚举映射/数字校验; 非法值 fail fast(同 numberOfWorkers)
|
|
60
63
|
let numOfWorkers = config.numberOfWorkers;
|
|
61
64
|
|
|
62
65
|
const AsyncQueue = require(`../utils/asyncQueue`);
|
|
@@ -126,6 +129,14 @@ const start = (config)=>{
|
|
|
126
129
|
|
|
127
130
|
let initWorker = (worker)=>{
|
|
128
131
|
WorkerMgr.addWorker(worker);
|
|
132
|
+
if (typeof config.workerPriority === 'number') {
|
|
133
|
+
try {
|
|
134
|
+
os.setPriority(worker.process.pid, config.workerPriority);
|
|
135
|
+
} catch (e) {
|
|
136
|
+
//优先级是建议性设置: 失败(如 POSIX 上调高需 root)仅告警, 不中断批任务
|
|
137
|
+
runLog.warn(`[Master]: failed to set priority of worker pid=${worker.process.pid}`, e);
|
|
138
|
+
};
|
|
139
|
+
};
|
|
129
140
|
worker.on('message', function(message) {
|
|
130
141
|
if(message && message.__sysEvent){//worker 系统事件: master 自己消费, 不转发
|
|
131
142
|
if(message.event === 'TERMINATE_ALL_WORKERS'){
|