@nxtedition/lib 26.0.28 → 26.0.29

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.
Files changed (2) hide show
  1. package/app.js +60 -1
  2. package/package.json +2 -1
package/app.js CHANGED
@@ -38,6 +38,8 @@ import makeUnderPressure from './under-pressure.js'
38
38
  import undici from '@nxtedition/undici'
39
39
  import { isPrimary } from 'node:cluster'
40
40
  import { nice } from '@napi-rs/nice'
41
+ import path from 'node:path'
42
+ import { sched_getaffinity, sched_setaffinity } from '@nxtedition/sched'
41
43
 
42
44
  export function makeApp(appConfig, onTerminate) {
43
45
  let ds
@@ -124,13 +126,17 @@ export function makeApp(appConfig, onTerminate) {
124
126
  const appDestroyers = []
125
127
 
126
128
  const serviceName = appConfig.name
127
- const serviceModule = appConfig.module ?? 'main'
128
129
  const serviceVersion = appConfig.version
130
+ const serviceModule = appConfig.module ?? 'main'
129
131
  const serviceWorkerId = appConfig.workerId ?? threadId
130
132
  const serviceInstanceId =
131
133
  // process.env.name is the pm2 name of the process
132
134
  appConfig.instanceId ?? appConfig.containerId ?? process.env.name ?? os.hostname()
133
135
 
136
+ if (!serviceName) {
137
+ throw new Error('Service name is required')
138
+ }
139
+
134
140
  const userAgent = (globalThis.userAgent =
135
141
  appConfig.userAgent ??
136
142
  (serviceName &&
@@ -310,6 +316,58 @@ export function makeApp(appConfig, onTerminate) {
310
316
  }
311
317
  }
312
318
 
319
+ let affinity = null
320
+ if (isMainThread && appConfig.affinity !== false && process.platform === 'linux') {
321
+ const allNodes = []
322
+ for (const entry of fs.readdirSync('/sys/devices/system/node')) {
323
+ if (!entry.startsWith('node')) {
324
+ continue
325
+ }
326
+
327
+ const cpulist = fs
328
+ .readFileSync(path.join('/sys/devices/system/node', entry, 'cpulist'), 'utf8')
329
+ .trim()
330
+ const cpus = []
331
+ for (const part of cpulist.split(',')) {
332
+ if (part.includes('-')) {
333
+ const [start, end] = part.split('-').map(Number)
334
+ for (let i = start; i <= end; i++) {
335
+ cpus.push(i)
336
+ }
337
+ } else {
338
+ cpus.push(Number(part))
339
+ }
340
+ }
341
+ allNodes.push(cpus)
342
+ }
343
+
344
+ if (Number.isInteger(appConfig.affinity) && appConfig.affinity >= 0) {
345
+ affinity = allNodes[appConfig.affinity % allNodes.length]
346
+ } else if (appConfig.affinity == null) {
347
+ affinity = allNodes[hashString(serviceName) % allNodes.length]
348
+ } else {
349
+ throw new Error('invalid affinity configuration: ' + appConfig.affinity)
350
+ }
351
+
352
+ affinity = fp.uniq(affinity)
353
+ affinity = fp.intersection(affinity, sched_getaffinity(0))
354
+
355
+ if (
356
+ !Array.isArray(affinity) ||
357
+ affinity.length === 0 ||
358
+ affinity.some((x) => typeof x !== 'number' || x < 0)
359
+ ) {
360
+ throw new Error('invalid affinity configuration: ' + appConfig.affinity)
361
+ }
362
+
363
+ try {
364
+ sched_setaffinity(0, affinity)
365
+ logger.debug({ data: { value: appConfig.affinity, affinity } }, 'sched_setaffinity succeeded')
366
+ } catch (err) {
367
+ logger.error({ err }, 'sched_setaffinity failed')
368
+ }
369
+ }
370
+
313
371
  if (appConfig.toobusy) {
314
372
  const resolution = appConfig.toobusy.resolution ?? 10
315
373
  const interval = appConfig.toobusy.interval ?? 500
@@ -1275,5 +1333,6 @@ export function makeApp(appConfig, onTerminate) {
1275
1333
  serviceInstanceId,
1276
1334
  serviceWorkerId,
1277
1335
  signal: ac.signal,
1336
+ affinity,
1278
1337
  })
1279
1338
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "26.0.28",
3
+ "version": "26.0.29",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",
@@ -68,6 +68,7 @@
68
68
  "@elastic/transport": "^8.9.3",
69
69
  "@napi-rs/nice": "^1.1.1",
70
70
  "@nxtedition/nxt-undici": "^6.4.0",
71
+ "@nxtedition/sched": "^1.0.1",
71
72
  "@smithy/node-http-handler": "^4.1.1",
72
73
  "acorn": "^8.15.0",
73
74
  "astring": "^1.9.0",