readFileCached capped at 64KB. On 56-core hosts /proc/interrupts (and similar) often exceeds that — reads were silently truncated. Buffer now grows (up to 1MB). That didn’t break system.cpu idle (aggregate line is first), but it was a real high-core regression.
70 lines
1.9 KiB
JavaScript
70 lines
1.9 KiB
JavaScript
import fs from 'fs'
|
|
|
|
/** Shared buffer — grows if a read fills it (e.g. /proc/interrupts on 56-core hosts). */
|
|
let readBuf = Buffer.alloc(65536)
|
|
const MAX_READ_BUF = 1024 * 1024
|
|
const SMALL_BUF = Buffer.alloc(8192)
|
|
const fdCache = new Map()
|
|
|
|
export function readFileCached(path) {
|
|
let entry = fdCache.get(path)
|
|
if (entry === undefined) {
|
|
try {
|
|
const fd = fs.openSync(path, 'r')
|
|
entry = { fd }
|
|
fdCache.set(path, entry)
|
|
} catch {
|
|
fdCache.set(path, null)
|
|
return null
|
|
}
|
|
}
|
|
if (!entry) return null
|
|
try {
|
|
let bytesRead = fs.readSync(entry.fd, readBuf, 0, readBuf.length, 0)
|
|
// Filled the buffer → likely truncated; grow and retry (common on high-core /proc files)
|
|
while (bytesRead === readBuf.length && readBuf.length < MAX_READ_BUF) {
|
|
readBuf = Buffer.alloc(Math.min(readBuf.length * 4, MAX_READ_BUF))
|
|
bytesRead = fs.readSync(entry.fd, readBuf, 0, readBuf.length, 0)
|
|
}
|
|
if (bytesRead === readBuf.length) {
|
|
// Still truncated at cap — full read for correctness
|
|
try {
|
|
return fs.readFileSync(path, 'utf8')
|
|
} catch {
|
|
/* fall through with truncated buffer */
|
|
}
|
|
}
|
|
return readBuf.toString('utf8', 0, bytesRead)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read a file using the shared small buffer, no FD caching.
|
|
* Good for dynamic paths (per-PID, per-cgroup) where open+close each tick is acceptable
|
|
* but Buffer allocation is not.
|
|
*/
|
|
export function readFileBuf(path) {
|
|
try {
|
|
const fd = fs.openSync(path, 'r')
|
|
try {
|
|
const bytes = fs.readSync(fd, SMALL_BUF, 0, SMALL_BUF.length, 0)
|
|
return SMALL_BUF.toString('utf8', 0, bytes)
|
|
} finally {
|
|
fs.closeSync(fd)
|
|
}
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function closeFdCache() {
|
|
for (const [path, entry] of fdCache) {
|
|
if (entry) {
|
|
try { fs.closeSync(entry.fd) } catch {}
|
|
}
|
|
}
|
|
fdCache.clear()
|
|
}
|