@nxtedition/lib 27.0.3 → 27.0.4
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/app.js +7 -13
- package/numa.js +64 -30
- package/package.json +4 -4
- package/time.js +3 -13
package/app.js
CHANGED
|
@@ -562,7 +562,7 @@ export function makeApp(appConfig, onTerminateOrMeta, metaOrNull) {
|
|
|
562
562
|
|
|
563
563
|
if (process.platform === 'linux' && appConfig.numa != null && appConfig.numa !== '') {
|
|
564
564
|
let numa = appConfig.numa
|
|
565
|
-
if (numa == 'auto' || numa === true) {
|
|
565
|
+
if (numa == 'auto' || numa === true || numa === 'hash') {
|
|
566
566
|
numa = hashString(
|
|
567
567
|
JSON.stringify({ serviceName, serviceModule, serviceInstanceId, serviceWorkerId }),
|
|
568
568
|
)
|
|
@@ -577,33 +577,27 @@ export function makeApp(appConfig, onTerminateOrMeta, metaOrNull) {
|
|
|
577
577
|
.observe(`${config.hostname}:monitor.stats?`, ds.record.PROVIDER)
|
|
578
578
|
.pipe(
|
|
579
579
|
rxjs.map((stats) => stats?.net?.bonding?.[0]?.numaNode ?? null),
|
|
580
|
-
rxjs.filter((n) => Number.isInteger(n) && n >= 0),
|
|
581
|
-
rxjs.timeout({
|
|
582
|
-
first: 10e3,
|
|
583
|
-
with: () => rxjs.of(null),
|
|
584
|
-
}),
|
|
585
580
|
rxjs.distinctUntilChanged(),
|
|
586
581
|
rxjs.retry({
|
|
587
|
-
resetOnSuccess: true,
|
|
588
582
|
delay(err, retryCount) {
|
|
589
583
|
logger.error({ err, retryCount }, 'net numa failed')
|
|
590
584
|
return rxjs.timer(10e3)
|
|
591
585
|
},
|
|
592
586
|
}),
|
|
593
587
|
)
|
|
594
|
-
.subscribe((
|
|
595
|
-
if (
|
|
588
|
+
.subscribe((numa) => {
|
|
589
|
+
if (numa != null) {
|
|
596
590
|
try {
|
|
597
|
-
const affinity = numa.setAffinity(
|
|
591
|
+
const affinity = numa.setAffinity(numa)
|
|
598
592
|
logger.debug(
|
|
599
|
-
{ hostname: config.hostname,
|
|
593
|
+
{ hostname: config.hostname, numa, affinity },
|
|
600
594
|
'net numa succeeded',
|
|
601
595
|
)
|
|
602
596
|
} catch (err) {
|
|
603
|
-
logger.error({ err, hostname: config.hostname,
|
|
597
|
+
logger.error({ err, hostname: config.hostname, numa }, 'net numa failed')
|
|
604
598
|
}
|
|
605
599
|
} else {
|
|
606
|
-
logger.warn({ hostname: config.hostname,
|
|
600
|
+
logger.warn({ hostname: config.hostname, numa }, 'net numa missing')
|
|
607
601
|
}
|
|
608
602
|
})
|
|
609
603
|
}
|
package/numa.js
CHANGED
|
@@ -5,66 +5,100 @@ import { sched_setaffinity } from '@nxtedition/sched'
|
|
|
5
5
|
|
|
6
6
|
function parseRange(value) {
|
|
7
7
|
if (typeof value !== 'string') {
|
|
8
|
-
throw new Error('
|
|
8
|
+
throw new Error('range must be a string')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const range = []
|
|
12
12
|
for (const part of value.split(',')) {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const trimmed = part.trim()
|
|
14
|
+
if (!trimmed) {
|
|
15
|
+
continue
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (trimmed.includes('-')) {
|
|
19
|
+
const [startStr, endStr, ...rest] = trimmed.split('-')
|
|
20
|
+
|
|
15
21
|
if (rest.length > 0) {
|
|
16
|
-
throw new Error(
|
|
22
|
+
throw new Error(`Invalid range: "${trimmed}"`)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const start = Number(startStr)
|
|
26
|
+
const end = Number(endStr)
|
|
27
|
+
|
|
28
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < 0) {
|
|
29
|
+
throw new Error(`Invalid number in range: "${trimmed}"`)
|
|
17
30
|
}
|
|
31
|
+
|
|
32
|
+
if (end < start) {
|
|
33
|
+
throw new Error(`Invalid range order: "${trimmed}" (end < start)`)
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
for (let i = start; i <= end; i++) {
|
|
19
37
|
range.push(i)
|
|
20
38
|
}
|
|
21
|
-
} else
|
|
22
|
-
|
|
39
|
+
} else {
|
|
40
|
+
const num = Number(trimmed)
|
|
41
|
+
if (!Number.isInteger(num) || num < 0) {
|
|
42
|
+
throw new Error(`Invalid number: "${trimmed}"`)
|
|
43
|
+
}
|
|
44
|
+
range.push(num)
|
|
23
45
|
}
|
|
24
46
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
return range
|
|
47
|
+
|
|
48
|
+
return [...new Set(range)].sort((a, b) => a - b)
|
|
29
49
|
}
|
|
30
50
|
|
|
31
51
|
/**
|
|
32
52
|
*
|
|
33
|
-
* @param {number|number[]} numa
|
|
53
|
+
* @param {null|number|number[]} numa
|
|
34
54
|
* @returns {number[]}
|
|
35
55
|
*/
|
|
36
56
|
export function setAffinity(numa) {
|
|
37
|
-
|
|
38
|
-
if (indices.some((x) => !Number.isInteger(x) || x < 0)) {
|
|
39
|
-
throw new Error('NUMA node must be a non-negative integer')
|
|
40
|
-
}
|
|
57
|
+
let affinity
|
|
41
58
|
|
|
42
|
-
|
|
59
|
+
if (numa === null) {
|
|
60
|
+
affinity = parseRange(fs.readFileSync('/sys/devices/system/cpu/present', 'utf8').trim())
|
|
61
|
+
} else {
|
|
62
|
+
if (numa.some((x) => !Number.isInteger(x) || x < 0)) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
'NUMA node must be null, a non-negative integer or array of non-negative integers',
|
|
65
|
+
)
|
|
66
|
+
}
|
|
43
67
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
const allNodes = []
|
|
69
|
+
for (const entry of fs.readdirSync('/sys/devices/system/node')) {
|
|
70
|
+
if (!/^node\d+$/.test(entry)) {
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const cpulist = fs
|
|
75
|
+
.readFileSync(path.join('/sys/devices/system/node', entry, 'cpulist'), 'utf8')
|
|
76
|
+
.trim()
|
|
77
|
+
|
|
78
|
+
allNodes.push(parseRange(cpulist))
|
|
48
79
|
}
|
|
49
80
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
81
|
+
if (allNodes.length === 0) {
|
|
82
|
+
throw new Error('No NUMA nodes found')
|
|
83
|
+
}
|
|
53
84
|
|
|
54
|
-
|
|
85
|
+
affinity = numa.flatMap((i) => allNodes[i % allNodes.length] ?? [])
|
|
55
86
|
}
|
|
56
87
|
|
|
57
|
-
|
|
58
|
-
|
|
88
|
+
const isolated = parseRange(fs.readFileSync('/sys/devices/system/cpu/isolated', 'utf8').trim())
|
|
89
|
+
|
|
90
|
+
affinity = affinity.filter((cpu) => !isolated.includes(cpu))
|
|
91
|
+
affinity = [...new Set(affinity)].sort((a, b) => a - b)
|
|
92
|
+
|
|
93
|
+
if (affinity.length === 0) {
|
|
94
|
+
throw new Error('Resulting CPU affinity is empty')
|
|
59
95
|
}
|
|
60
96
|
|
|
61
|
-
const affinity = indices
|
|
62
|
-
.flatMap((i) => allNodes[i % allNodes.length] ?? [])
|
|
63
|
-
.filter((cpu) => !isolated.includes(cpu))
|
|
64
97
|
sched_setaffinity(0, affinity)
|
|
98
|
+
|
|
65
99
|
globalThis.__nxt_sched_affinity = {
|
|
66
100
|
cpulist: affinity,
|
|
67
|
-
nodelist: indices,
|
|
68
101
|
}
|
|
102
|
+
|
|
69
103
|
return affinity
|
|
70
104
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "27.0.
|
|
3
|
+
"version": "27.0.4",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"type": "module",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"@elastic/transport": "^8.9.3",
|
|
53
53
|
"@nxtedition/nxt-undici": "^6.4.17",
|
|
54
54
|
"@nxtedition/sched": "^1.0.2",
|
|
55
|
-
"@nxtedition/template": "^1.0.0
|
|
56
|
-
"@nxtedition/weak-cache": "^1.0.0
|
|
55
|
+
"@nxtedition/template": "^1.0.0",
|
|
56
|
+
"@nxtedition/weak-cache": "^1.0.0",
|
|
57
57
|
"diff": "5.2.0",
|
|
58
58
|
"fast-querystring": "^1.1.2",
|
|
59
59
|
"http-errors": "^2.0.0",
|
|
@@ -87,5 +87,5 @@
|
|
|
87
87
|
"pino": ">=7.0.0",
|
|
88
88
|
"rxjs": "^7.0.0"
|
|
89
89
|
},
|
|
90
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "be0475e7b0d724ba6f2f9fdbaa0c3f069e49e6a7"
|
|
91
91
|
}
|
package/time.js
CHANGED
|
@@ -10,19 +10,9 @@ export function fastNow() {
|
|
|
10
10
|
return fastNowTime
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function isTimeBetween(date, startTime, endTime
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (isUTC) {
|
|
18
|
-
currentHours = date.getUTCHours()
|
|
19
|
-
currentMinutes = date.getUTCMinutes()
|
|
20
|
-
} else {
|
|
21
|
-
// Convert local time to UTC equivalent
|
|
22
|
-
const utcDate = new Date(date.getTime() + date.getTimezoneOffset() * 60000)
|
|
23
|
-
currentHours = utcDate.getUTCHours()
|
|
24
|
-
currentMinutes = utcDate.getUTCMinutes()
|
|
25
|
-
}
|
|
13
|
+
export function isTimeBetween(date, startTime, endTime) {
|
|
14
|
+
const currentHours = date.getHours()
|
|
15
|
+
const currentMinutes = date.getMinutes()
|
|
26
16
|
|
|
27
17
|
// Validate and parse start and end times
|
|
28
18
|
if (!startTime) startTime = '00:00' // Default start at midnight
|