At ~200 articles/s what the prober stores (inspections, par2 file lists, the WAL) took ~0.8-1.1 GB an hour of homelab's root, and nothing stopped it at the 10 GB floor. Below 12 GB free it claims nothing (resumed 1 GB above): the lanes go dry and its share shrinks to its minimum. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
41 lines
1.5 KiB
TypeScript
41 lines
1.5 KiB
TypeScript
import { statfs } from 'node:fs/promises'
|
||
import logger from '@adonisjs/core/services/logger'
|
||
|
||
/** How often free space is measured. */
|
||
const CHECK_MS = 60_000
|
||
/** Resumed only this much above the floor: not paused and resumed at every flush. */
|
||
const MARGIN_GB = 1
|
||
|
||
/**
|
||
* Free space on the filesystem holding `path`, against a floor: low below it, again fine MARGIN_GB above it. In a
|
||
* container `/` is an overlay on the host's Docker root, which on homelab is the NVMe the catalog and the WAL are on.
|
||
* The prober filled it ~0.8–1.1 GB an hour at ~200 articles/s, and nothing stopped it at the 10 GB floor (LOG.md
|
||
* 2026-10-18 (77)). Off when the floor is 0.
|
||
*/
|
||
export class DiskSpace {
|
||
private checkedAt = 0
|
||
private low = false
|
||
|
||
constructor(
|
||
private what: string,
|
||
private path: string,
|
||
private floorGb: number,
|
||
private free: (path: string) => Promise<number> = async (p) => {
|
||
const s = await statfs(p)
|
||
return s.bavail * s.bsize
|
||
},
|
||
private checkMs = CHECK_MS
|
||
) {}
|
||
|
||
async short(): Promise<boolean> {
|
||
if (!this.floorGb) return false
|
||
if (Date.now() - this.checkedAt < this.checkMs) return this.low
|
||
this.checkedAt = Date.now()
|
||
const gb = (await this.free(this.path)) / 1e9
|
||
const was = this.low
|
||
this.low = gb < this.floorGb + (was ? MARGIN_GB : 0)
|
||
if (this.low !== was) logger.info(`${gb.toFixed(1)} GB free (floor ${this.floorGb} GB): ${this.what} ${this.low ? 'paused' : 'resumed'}.`)
|
||
return this.low
|
||
}
|
||
}
|