Files
peardata/server/services/collectors/bcache.js
T
Raven Scott a639b3c953
CI / test (push) Successful in 1m15s
Release rolling / release (push) Successful in 7m6s
Updates
2026-07-18 19:49:57 -04:00

92 lines
2.6 KiB
JavaScript

/**
* BCache collector via /sys/fs/bcache and per-block bcache sysfs.
* Enable: PEARDATA_BCACHE=1 (auto when sysfs present)
*/
import fs from 'fs'
import path from 'path'
import { CollectorPlugin } from './plugin.js'
import { registerChart } from '../../../shared/metrics.js'
export function isBcacheEnabled() {
const v = process.env.PEARDATA_BCACHE
if (v === '0' || v === 'off' || v === 'false') return false
if (v === '1' || v === 'on' || v === 'true') return true
return fs.existsSync('/sys/fs/bcache')
}
function readNum(p) {
try {
return Number(fs.readFileSync(p, 'utf8').trim()) || 0
} catch {
return 0
}
}
export class BcacheCollector extends CollectorPlugin {
constructor() {
super({ name: 'bcache' })
this.prev = new Map()
}
isEnabled() {
return isBcacheEnabled()
}
async collect() {
const ts = Date.now()
/** @type {Array<{ chart: string, context: string, ts: number, values: object }>} */
const batch = []
let dirs = []
try {
dirs = fs.readdirSync('/sys/fs/bcache')
} catch {
return batch
}
for (const id of dirs) {
const base = path.join('/sys/fs/bcache', id)
if (!fs.existsSync(path.join(base, 'stats_total'))) continue
const hits = readNum(path.join(base, 'stats_total', 'cache_hits'))
const misses = readNum(path.join(base, 'stats_total', 'cache_misses'))
const bypass = readNum(path.join(base, 'stats_total', 'cache_bypass_hits'))
const safe = id.slice(0, 12)
const chart = `bcache.${safe}`
registerChart({
id: chart,
name: chart,
context: 'bcache.cache',
title: `BCache ${safe}`,
units: 'events/s',
family: safe,
chartType: 'line',
priority: 4700,
plugin: 'bcache',
dimensions: [
{ id: 'hits', name: 'hits', algorithm: 'incremental' },
{ id: 'misses', name: 'misses', algorithm: 'incremental' },
{ id: 'bypass', name: 'bypass', algorithm: 'incremental' },
],
})
const prev = this.prev.get(id)
const dt = this.intervalMs / 1000
batch.push({
chart,
context: 'bcache.cache',
ts,
values: {
hits: prev ? Math.max(0, hits - prev.hits) / dt : 0,
misses: prev ? Math.max(0, misses - prev.misses) / dt : 0,
bypass: prev ? Math.max(0, bypass - prev.bypass) / dt : 0,
},
})
this.prev.set(id, { hits, misses, bypass })
}
return batch
}
}
let singleton = null
export function getBcacheCollector() {
if (!singleton) singleton = new BcacheCollector()
return singleton
}