Files
peardata/server/utils/fd-cache.js
T
Raven Scott da9541528e
CI / test (push) Successful in 1m0s
Release rolling / release (push) Failing after 1m50s
In /proc/stat, guest is already inside user (and guest_nice inside nice). Peardata was adding guest into the total again, which shrinks every other slice — including idle.
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.
2026-07-22 12:36:30 -04:00

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()
}