@nxtedition/scheduler 4.1.11 → 4.1.13
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 +118 -10
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/limiter.d.ts +21 -0
- package/lib/limiter.d.ts.map +1 -1
- package/lib/limiter.js +266 -48
- package/lib/limiter.js.map +1 -1
- package/lib/queue.d.ts +4 -0
- package/lib/queue.d.ts.map +1 -1
- package/lib/queue.js +55 -42
- package/lib/queue.js.map +1 -1
- package/lib/scheduler.d.ts +28 -0
- package/lib/scheduler.d.ts.map +1 -1
- package/lib/scheduler.js +568 -28
- package/lib/scheduler.js.map +1 -1
- package/package.json +4 -4
package/lib/queue.js
CHANGED
|
@@ -2,6 +2,16 @@ export class FastQueue {
|
|
|
2
2
|
idx = 0;
|
|
3
3
|
cnt = 0;
|
|
4
4
|
arr = [];
|
|
5
|
+
// Consecutive Limiter timer ticks during which this queue had items but dispatched
|
|
6
|
+
// nothing (front blocked on tokens). Unused by Scheduler. See Limiter's
|
|
7
|
+
// anti-starvation force-through.
|
|
8
|
+
stall = 0;
|
|
9
|
+
// This queue's index in Queue.queues (priority + 3), so dequeue sites can update
|
|
10
|
+
// the owner's nonEmptyMask without searching.
|
|
11
|
+
i;
|
|
12
|
+
constructor(i) {
|
|
13
|
+
this.i = i;
|
|
14
|
+
}
|
|
5
15
|
}
|
|
6
16
|
export function parsePriority(p) {
|
|
7
17
|
if (typeof p === 'number') {
|
|
@@ -45,6 +55,20 @@ export function parsePriority(p) {
|
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
const maxInt = 2147483647;
|
|
58
|
+
// Lottery tier for each value of (counter & 127): the lowest set bit among bits 0..6
|
|
59
|
+
// picks the tier (bit0 → highest=6, bit1 → 5, …, bit6 → lowest=0); no bit set → 6.
|
|
60
|
+
// Precomputed so the hot dispatch path does one table load instead of a branch chain.
|
|
61
|
+
const TIER = new Uint8Array(128);
|
|
62
|
+
for (let v = 0; v < 128; v++) {
|
|
63
|
+
let idx = 6;
|
|
64
|
+
for (let b = 0; b < 7; b++) {
|
|
65
|
+
if (v & (1 << b)) {
|
|
66
|
+
idx = 6 - b;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
TIER[v] = idx;
|
|
71
|
+
}
|
|
48
72
|
// Sentinel limits array for callers that don't enforce per-priority caps.
|
|
49
73
|
// `running < NO_LIMITS[n]` is always true for any non-negative running count,
|
|
50
74
|
// so passing this acts as "no filter".
|
|
@@ -66,15 +90,19 @@ export class Queue {
|
|
|
66
90
|
static HIGHER = 2;
|
|
67
91
|
static HIGHEST = 3;
|
|
68
92
|
counter = 0;
|
|
93
|
+
// Bit n is set while queues[n] is non-empty. Maintained by subclasses at the
|
|
94
|
+
// 0 ↔ non-0 cnt transitions (enqueue/dequeue/clear), which are rare relative to
|
|
95
|
+
// dispatches, so getNextQueue can skip empty tiers without touching their objects.
|
|
96
|
+
nonEmptyMask = 0;
|
|
69
97
|
// Queues are stored in order of priority, so index == priority + 3
|
|
70
98
|
queues = [
|
|
71
|
-
new FastQueue(), // 0 lowest
|
|
72
|
-
new FastQueue(), // 1 lower
|
|
73
|
-
new FastQueue(), // 2 low
|
|
74
|
-
new FastQueue(), // 3 normal
|
|
75
|
-
new FastQueue(), // 4 high
|
|
76
|
-
new FastQueue(), // 5 higher
|
|
77
|
-
new FastQueue(), // 6 highest
|
|
99
|
+
new FastQueue(0), // 0 lowest
|
|
100
|
+
new FastQueue(1), // 1 lower
|
|
101
|
+
new FastQueue(2), // 2 low
|
|
102
|
+
new FastQueue(3), // 3 normal
|
|
103
|
+
new FastQueue(4), // 4 high
|
|
104
|
+
new FastQueue(5), // 5 higher
|
|
105
|
+
new FastQueue(6), // 6 highest
|
|
78
106
|
];
|
|
79
107
|
// Pick the next queue to dispatch from.
|
|
80
108
|
//
|
|
@@ -88,45 +116,30 @@ export class Queue {
|
|
|
88
116
|
// turn, defeating starvation prevention. Use `running = -1` to bypass limits on
|
|
89
117
|
// every queue (e.g. when the local worker has nothing running).
|
|
90
118
|
getNextQueue(limits, running) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
idx = 6; // highest: 50%
|
|
95
|
-
}
|
|
96
|
-
else if (this.counter & 0b0000010) {
|
|
97
|
-
idx = 5; // higher: 25%
|
|
98
|
-
}
|
|
99
|
-
else if (this.counter & 0b0000100) {
|
|
100
|
-
idx = 4; // high: 12.5%
|
|
101
|
-
}
|
|
102
|
-
else if (this.counter & 0b0001000) {
|
|
103
|
-
idx = 3; // normal: 6.25%
|
|
104
|
-
}
|
|
105
|
-
else if (this.counter & 0b0010000) {
|
|
106
|
-
idx = 2; // low: 3.125%
|
|
107
|
-
}
|
|
108
|
-
else if (this.counter & 0b0100000) {
|
|
109
|
-
idx = 1; // lower: 1.5625%
|
|
110
|
-
}
|
|
111
|
-
else if (this.counter & 0b1000000) {
|
|
112
|
-
idx = 0; // lowest: 0.78%
|
|
119
|
+
const mask = this.nonEmptyMask;
|
|
120
|
+
if (mask === 0) {
|
|
121
|
+
return null;
|
|
113
122
|
}
|
|
123
|
+
this.counter = (this.counter + 1) & maxInt;
|
|
124
|
+
const idx = TIER[this.counter & 127];
|
|
114
125
|
// Lottery-selected queue: bypass its limit (starvation prevention).
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return preferred;
|
|
118
|
-
}
|
|
119
|
-
for (let n = idx - 1; n >= 0; n--) {
|
|
120
|
-
const q = this.queues[n];
|
|
121
|
-
if (q.cnt > 0 && running < limits[n]) {
|
|
122
|
-
return q;
|
|
123
|
-
}
|
|
126
|
+
if (mask & (1 << idx)) {
|
|
127
|
+
return this.queues[idx];
|
|
124
128
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
// Preferred tier empty: highest-first scan over the NON-EMPTY tiers only (with
|
|
130
|
+
// limit checks), so an empty tier's lottery mass reverts to the most important
|
|
131
|
+
// backlogged work. Scanning downward first instead would hand every empty middle
|
|
132
|
+
// tier's share to LOWER priorities — measured at ~44-47% of dispatch slots going
|
|
133
|
+
// to a backlogged 'low'/'normal' tier while 'highest' was backlogged, versus the
|
|
134
|
+
// ~3-6% the documented exponential bias promises. Iterating set bits via clz32
|
|
135
|
+
// visits only populated tiers (usually one) instead of all seven queue objects.
|
|
136
|
+
let m = mask;
|
|
137
|
+
while (m !== 0) {
|
|
138
|
+
const n = 31 - Math.clz32(m);
|
|
139
|
+
if (running < limits[n]) {
|
|
140
|
+
return this.queues[n];
|
|
129
141
|
}
|
|
142
|
+
m &= ~(1 << n);
|
|
130
143
|
}
|
|
131
144
|
return null;
|
|
132
145
|
}
|
package/lib/queue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,SAAS;IACpB,GAAG,GAAG,CAAC,CAAA;IACP,GAAG,GAAG,CAAC,CAAA;IACP,GAAG,GAAmB,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
package/lib/scheduler.d.ts
CHANGED
|
@@ -37,6 +37,34 @@ export declare class Scheduler extends Queue {
|
|
|
37
37
|
concurrency?: ConcurrencyOptions;
|
|
38
38
|
});
|
|
39
39
|
[kOnExit](): void;
|
|
40
|
+
/**
|
|
41
|
+
* Create a child Scheduler that enforces an ADDITIONAL concurrency limit on top of this
|
|
42
|
+
* (the parent). A task dispatched through the child runs only when it is admitted by BOTH
|
|
43
|
+
* the child's own per-priority cap (counting the child's running tasks) AND the parent's
|
|
44
|
+
* overall `max` (counting all tasks in the parent's pool, across workers when the parent
|
|
45
|
+
* is shared).
|
|
46
|
+
*
|
|
47
|
+
* Canonical use: a shared parent caps global concurrency (e.g. 16 reads across the whole
|
|
48
|
+
* process) while each worker uses a child to cap its local share (e.g. 4):
|
|
49
|
+
*
|
|
50
|
+
* const parent = new Scheduler(Scheduler.makeSharedState(16)) // global pool
|
|
51
|
+
* const reads = parent.child({ concurrency: 4 }) // this worker's cap
|
|
52
|
+
* await reads.run(() => fs.readFile(path), 'high')
|
|
53
|
+
*
|
|
54
|
+
* `opts` mirrors the constructor's options (so per-priority caps work too:
|
|
55
|
+
* `parent.child({ concurrency: { max: 4, low: 1 } })`); omit it for an unlimited local
|
|
56
|
+
* cap (only the parent constrains).
|
|
57
|
+
*
|
|
58
|
+
* Priorities: the child has its own independent per-priority caps and starvation-
|
|
59
|
+
* prevention lottery; the parent's overall `max` is the global gate (a SOFT limit when
|
|
60
|
+
* the parent/root is shared — the same bounded read-then-increment overshoot as a plain
|
|
61
|
+
* shared Scheduler). The parent's own per-priority caps constrain only tasks submitted
|
|
62
|
+
* DIRECTLY to the parent — child tasks see the parent solely as an overall `max`.
|
|
63
|
+
* Children may be nested arbitrarily.
|
|
64
|
+
*/
|
|
65
|
+
child(opts?: {
|
|
66
|
+
concurrency?: ConcurrencyOptions;
|
|
67
|
+
}): Scheduler;
|
|
40
68
|
run<T>(fn: () => PromiseLike<T> | T, priority?: Priority): Promise<T>;
|
|
41
69
|
run<T, U>(fn: (opaque: U) => PromiseLike<T> | T, priority: Priority, opaque: U): Promise<T>;
|
|
42
70
|
acquire(fn: () => unknown, priority?: Priority): boolean;
|
package/lib/scheduler.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,EAAuB,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAA;AAyChG,MAAM,MAAM,kBAAkB,GAC1B,MAAM,GACN;IACE,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAoGL,QAAA,MAAM,OAAO,eAA4B,CAAA;AAQzC,qBAAa,SAAU,SAAQ,KAAK;;IAClC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IAgCjC,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,SAAS,CAAQ;IAQzB,OAAO,CAAC,IAAI,CAAQ;IA2BpB,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,MAAM;IAmB1C,IAAI,WAAW,WAGd;IAED,IAAI,KAAK;;;;;;;;;;MAYR;gBAGC,iBAAiB,EAAE,iBAAiB,GAAG;QAAE,WAAW,CAAC,EAAE,kBAAkB,CAAA;KAAE,EAC3E,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,kBAAkB,CAAA;KAAE;IAyDhD,CAAC,OAAO,CAAC;IAuCT;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,kBAAkB,CAAA;KAAE,GAAG,SAAS;IA2B7D,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC;IACrE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAkD3F,OAAO,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO;IACxD,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO;IAyJ1F,UAAU,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO;IA8ZxC,OAAO,aA4CL;IAEF,CAAC,MAAM,CAAC,OAAO,CAAC;CAuEjB"}
|