@nxtedition/lib 26.0.36 → 26.1.0
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 +13 -61
- package/numa.js +42 -0
- package/package.json +2 -1
package/app.js
CHANGED
|
@@ -37,8 +37,8 @@ import { isTimeBetween } from './time.js'
|
|
|
37
37
|
import makeUnderPressure from './under-pressure.js'
|
|
38
38
|
import undici from '@nxtedition/undici'
|
|
39
39
|
import { isPrimary } from 'node:cluster'
|
|
40
|
-
import
|
|
41
|
-
import {
|
|
40
|
+
import { nice } from '@nxtedition/sched'
|
|
41
|
+
import { setAffinity } from './numa.js'
|
|
42
42
|
|
|
43
43
|
export function makeApp(appConfig, onTerminate) {
|
|
44
44
|
let ds
|
|
@@ -317,69 +317,21 @@ export function makeApp(appConfig, onTerminate) {
|
|
|
317
317
|
|
|
318
318
|
let affinity = null
|
|
319
319
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const cpulist = fs
|
|
329
|
-
.readFileSync(path.join('/sys/devices/system/node', entry, 'cpulist'), 'utf8')
|
|
330
|
-
.trim()
|
|
331
|
-
const cpus = []
|
|
332
|
-
for (const part of cpulist.split(',')) {
|
|
333
|
-
if (part.includes('-')) {
|
|
334
|
-
const [start, end] = part.split('-').map(Number)
|
|
335
|
-
for (let i = start; i <= end; i++) {
|
|
336
|
-
cpus.push(i)
|
|
337
|
-
}
|
|
338
|
-
} else {
|
|
339
|
-
cpus.push(Number(part))
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
allNodes.push(cpus)
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
let indices = []
|
|
346
|
-
if (Number.isInteger(numa)) {
|
|
347
|
-
indices = [numa % allNodes.length]
|
|
348
|
-
} else if (
|
|
349
|
-
numa == null ||
|
|
350
|
-
numa === true ||
|
|
351
|
-
numa === 'auto' ||
|
|
352
|
-
numa === 'hash' ||
|
|
353
|
-
numa === 'default'
|
|
354
|
-
) {
|
|
355
|
-
indices = [hashString(JSON.stringify({ serviceName, serviceModule })) % allNodes.length]
|
|
356
|
-
} else {
|
|
357
|
-
throw Object.assign(new Error('invalid numa configuration: ' + numa), {
|
|
358
|
-
data: { numa, allNodes },
|
|
359
|
-
})
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const prev = sched_getaffinity(0)
|
|
363
|
-
|
|
364
|
-
affinity = indices.flatMap((i) => allNodes[i] ?? [])
|
|
365
|
-
affinity = fp.uniq(affinity)
|
|
366
|
-
affinity = fp.intersection(affinity, prev)
|
|
367
|
-
|
|
368
|
-
if (
|
|
369
|
-
!Array.isArray(affinity) ||
|
|
370
|
-
affinity.length === 0 ||
|
|
371
|
-
affinity.some((x) => !Number.isInteger(x) || x < 0)
|
|
372
|
-
) {
|
|
373
|
-
throw Object.assign(new Error('invalid numa configuration:' + numa), {
|
|
374
|
-
data: { numa, allNodes, indices, next: affinity, prev },
|
|
375
|
-
})
|
|
320
|
+
if (
|
|
321
|
+
process.platform === 'linux' &&
|
|
322
|
+
(isMainThread || appConfig.numa != null) &&
|
|
323
|
+
appConfig.numa !== false
|
|
324
|
+
) {
|
|
325
|
+
let numa = appConfig.numa
|
|
326
|
+
if (numa == null || numa === true) {
|
|
327
|
+
numa = hashString(JSON.stringify({ serviceName, serviceModule }))
|
|
376
328
|
}
|
|
377
329
|
|
|
378
330
|
try {
|
|
379
|
-
|
|
380
|
-
logger.debug({ data: { numa, affinity
|
|
331
|
+
affinity = setAffinity(numa)
|
|
332
|
+
logger.debug({ data: { numa: appConfig.numa, affinity } }, 'set numa affinity succeeded')
|
|
381
333
|
} catch (err) {
|
|
382
|
-
logger.error({ err }, '
|
|
334
|
+
logger.error({ err, data: { numa: appConfig.numa } }, 'set numa affinity failed')
|
|
383
335
|
}
|
|
384
336
|
}
|
|
385
337
|
|
package/numa.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import { sched_setaffinity } from '@nxtedition/sched'
|
|
5
|
+
|
|
6
|
+
export function setAffinity(numa) {
|
|
7
|
+
const allNodes = []
|
|
8
|
+
for (const entry of fs.readdirSync('/sys/devices/system/node')) {
|
|
9
|
+
if (!entry.startsWith('node')) {
|
|
10
|
+
continue
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const cpulist = fs
|
|
14
|
+
.readFileSync(path.join('/sys/devices/system/node', entry, 'cpulist'), 'utf8')
|
|
15
|
+
.trim()
|
|
16
|
+
const cpus = []
|
|
17
|
+
for (const part of cpulist.split(',')) {
|
|
18
|
+
if (part.includes('-')) {
|
|
19
|
+
const [start, end] = part.split('-').map(Number)
|
|
20
|
+
for (let i = start; i <= end; i++) {
|
|
21
|
+
cpus.push(i)
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
cpus.push(Number(part))
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
allNodes.push(cpus)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (allNodes.length === 0) {
|
|
31
|
+
throw new Error('No NUMA nodes found')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!Number.isInteger(numa) || numa < 0) {
|
|
35
|
+
throw new Error('NUMA node must be a non-negative integer')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const indices = [numa % allNodes.length]
|
|
39
|
+
const affinity = indices.flatMap((i) => allNodes[i] ?? [])
|
|
40
|
+
sched_setaffinity(0, affinity)
|
|
41
|
+
return affinity
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "26.0
|
|
3
|
+
"version": "26.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"type": "module",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"logger.js",
|
|
28
28
|
"logger.d.ts",
|
|
29
29
|
"mime.js",
|
|
30
|
+
"numa.js",
|
|
30
31
|
"proxy.js",
|
|
31
32
|
"timers.js",
|
|
32
33
|
"trace.js",
|