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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-tasks",
3
- "version": "3.2.2",
3
+ "version": "3.2.3",
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": [
@@ -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 };
@@ -1,5 +1,6 @@
1
1
  const cluster = require('cluster');
2
- const numCPUs = require('os').cpus().length;
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'){