import path from "node:path"; import { readFileSync } from "node:fs"; import { lstat, readFile, realpath, stat } from "node:fs/promises"; import { execa } from "execa"; import type { Paths } from "./paths"; import { getFreeBytes } from "./diskSpace"; import type { StorageLocation, StorageVolume } from "./storageLocations"; import { countersVerdict, healthTimings, isDriveNotAnswering, onDrive, parseBlockStat, setCounterReader, stalledLocation, type BlockStatSample, type HealthDetector, type LocationHealthState, } from "./storageHealth"; import { counterSampleMinimumMs, secondsText } from "./storageHealthTimings"; // STORAGE VOLUME PROBES — is this location's disk here, and if not, where? // // SERVER-ONLY. It imports execa, so nothing reachable from a `"use client"` // file may import it (next build fails on `node:child_process`). The pure half // — types, `locationOfDataDir`, the migration — is `storageLocations.ts`, and // that is the one a client component may reach for. // // EVERY SUBPROCESS HERE FAILS OPEN. `reject: false`, a timeout, and a catch, // and on any failure the answer is "identity unknown" — never "unreachable". // The reason is Docker: block devices are invisible inside a container and the // media root is an identity bind-mount, so `findmnt` can legitimately know // nothing about a perfectly healthy root (RUNNING_IN_DOCKER.md §another drive). // A probe that turned "I could not ask" into "your disk is gone" would declare // every containerised corpus broken. Availability comes from `stat`, which is // the one thing that is always true; the subprocesses only ever ADD identity. export type StorageLocationStatus = // The root is a directory right now. The only status with freeBytes. | "available" // The root is not there, but the recorded UUID is mounted somewhere else — // `candidateRoot` is where the root would be after a re-point. | "mounted-elsewhere" // The recorded UUID has a /dev/disk/by-uuid node but is not mounted. | "unmounted" // The recorded UUID is not present on this machine at all. | "absent" // The root is not there and we have no identity to look for — nothing to say // beyond "that path does not exist". | "missing" // The drive did not answer the last health probe (`lib/storageHealth.ts`), so // this probe did not ask: an in-process `stat` there would hold one of the // process's I/O threads until the drive answered. No identity, no free space. | "stalled"; // `known: false` is the fail-open answer and is NOT a problem report: it means // the probe could not ask (no findmnt, a container, a timeout), not that the // root is in a bad state. export type StorageIdentity = | ({ known: true } & StorageVolume) | { known: false }; export type StorageLocationProbe = { status: StorageLocationStatus; identity: StorageIdentity; // Only for "mounted-elsewhere": join(currentMountpoint, volume.relPath). candidateRoot?: string; // Only when available and the mount looks like it will not survive a reboot. warning?: string; // Only when available — `getFreeBytes` walks up on ENOENT, so on a missing // root it would cheerfully report the PARENT volume's free space, which for // an unmounted platter is the free space of the disk holding /run/media. freeBytes?: number; }; export type VolumeBins = Pick; // The plan's budget: a findmnt that has not answered in 3 s has hit a wedged // automounter or a hung NFS mount, and the right answer is "unknown", now. export const FINDMNT_TIMEOUT_MS = 3_000; // udisksctl talks to a daemon over D-Bus and then waits for a real mount. export const MOUNT_TIMEOUT_MS = 15_000; // The kernel's stable-name directory for volumes. A location's UUID has an // entry here whenever the disk is attached, mounted or not — which is the whole // difference between "unmounted" and "absent". export const BY_UUID_DIR = "/dev/disk/by-uuid"; // Test seams ONLY, shared by probeLocation and mountByUuid so the by-uuid // directory is named in one place. Production callers pass nothing and get the // constants above; the unit tests shorten the findmnt timeout (so the "fake // binary that sleeps" case does not cost the suite three seconds) and point // `byUuidDir` at a tmp directory, which is the only way to exercise // unmounted-vs-absent without plugging a disk in. export type ProbeOptions = { findmntTimeoutMs?: number; byUuidDir?: string; }; type Run = { ok: boolean; // undefined = the binary never ran to completion (not installed, or killed // by the timeout). A NUMBER is an answer from findmnt itself, and the // difference matters: `findmnt --fstab` exits 1 to MEAN "no such entry", // which is a fact, while a missing binary means "I could not ask", which is // not. Conflating them would warn about fstab on every location in a // container. exitCode: number | undefined; stdout: string; stderr: string; }; async function run( bin: string, args: string[], timeout: number, ): Promise { try { const res = await execa(bin, args, { buffer: true, reject: false, timeout, }); const exitCode = res.timedOut ? undefined : (res.exitCode ?? undefined); return { ok: exitCode === 0, exitCode, stdout: typeof res.stdout === "string" ? res.stdout : "", stderr: typeof res.stderr === "string" ? res.stderr : "", }; } catch { // ENOENT on the binary itself lands here in some execa paths. Same answer. return { ok: false, exitCode: undefined, stdout: "", stderr: "" }; } } // `findmnt -J -T ` — the mount the path is ON (-T resolves a path, not // just a mountpoint), as JSON so a label with a space cannot be misparsed. async function identityOfPath( root: string, bins: VolumeBins, timeoutMs: number, ): Promise { const res = await run( bins.findmntBin, ["-J", "-T", root, "-o", "TARGET,SOURCE,FSTYPE,LABEL,UUID"], timeoutMs, ); if (!res.ok) return { known: false }; let parsed: unknown; try { parsed = JSON.parse(res.stdout); } catch { return { known: false }; } const fs0 = (parsed as { filesystems?: unknown[] })?.filesystems?.[0] as | Record | undefined; if (!fs0) return { known: false }; const uuid = typeof fs0.uuid === "string" ? fs0.uuid : ""; const mountpoint = typeof fs0.target === "string" ? fs0.target : ""; // No mountpoint means we learned nothing usable; no UUID means the volume has // no stable name to find it by later (tmpfs, overlay, a bind mount in a // container) — in both cases identity stays unknown rather than half-filled. if (!uuid || !mountpoint) return { known: false }; return { known: true, uuid, fstype: typeof fs0.fstype === "string" ? fs0.fstype : undefined, label: typeof fs0.label === "string" ? fs0.label : undefined, mountpoint, relPath: relativeUnder(mountpoint, root), }; } // The root's path relative to its mountpoint, "" when they are the same dir. // Never "..": if `root` is somehow not under `mountpoint` we keep "" rather // than inventing a traversal that a later join would follow off the volume. function relativeUnder(mountpoint: string, root: string): string { const rel = path.relative(mountpoint, root); if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) return ""; return rel; } // `findmnt -rn -S UUID= -o TARGET` — where that volume is mounted now, if // anywhere. Raw + no headings, one mountpoint per line; we take the first. async function mountpointOfUuid( uuid: string, bins: VolumeBins, timeoutMs: number, ): Promise { const res = await run( bins.findmntBin, ["-rn", "-S", `UUID=${uuid}`, "-o", "TARGET"], timeoutMs, ); if (!res.ok) return ""; const first = res.stdout .split("\n") .map((l) => l.trim()) .find((l) => l !== ""); return first ?? ""; } // `findmnt --fstab -S UUID=` exits 1 when the volume has no fstab entry — // that non-zero exit is the ANSWER, not a failure, which is why this reads // `exitCode` and not `ok`. FAILS OPEN TO "yes, it is in fstab" only when the // binary never ran: a missing findmnt must not warn about fstab on every // location on the page. async function isInFstab( uuid: string, bins: VolumeBins, timeoutMs: number, ): Promise { const res = await run( bins.findmntBin, ["--fstab", "-S", `UUID=${uuid}`], timeoutMs, ); if (res.exitCode === undefined) return true; return res.exitCode === 0 && res.stdout.trim() !== ""; } function automountWarning( loc: StorageLocation, identity: StorageIdentity, ): string { const uuid = identity.known ? identity.uuid : ""; const fstype = (identity.known && identity.fstype) || "auto"; return ( `automount — may not be present at boot; add ` + `\`UUID=${uuid} /mnt/${loc.id} ${fstype} nofail 0 2\` to /etc/fstab, ` + `or rely on re-point` ); } // Probe one location. Read-only: it never mounts, never writes settings, and // never touches the corpus. export async function probeLocation( loc: StorageLocation, bins: VolumeBins, opts: ProbeOptions = {}, ): Promise { const timeoutMs = opts.findmntTimeoutMs ?? FINDMNT_TIMEOUT_MS; const root = loc.root.trim(); // A DRIVE THAT IS NOT ANSWERING IS NOT ASKED. The `stat` and `statfs` below // run in-process, and on a stalled disk each holds an I/O thread until the // drive comes back; the health pass (below) already asked without touching // it. Both go through `onDrive`'s watchdog, too: one that has not answered // within the budget (`storage.health.budgetMs`, 3 s by default) marks the // location stalled and this probe answers so. const stalledProbe: StorageLocationProbe = { status: "stalled", identity: { known: false }, }; if (stalledLocation(loc)) return stalledProbe; // AVAILABILITY IS `stat`, AND ONLY `stat`. A root that is a directory is // available even when every identity probe below fails — see the header. let isDir = false; if (root !== "") { try { isDir = (await onDrive(loc, () => stat(root))).isDirectory(); } catch (err) { if (isDriveNotAnswering(err)) return stalledProbe; isDir = false; } } if (isDir) { const identity = await identityOfPath(root, bins, timeoutMs); // getFreeBytes FAILS OPEN TO Infinity (statfs unsupported, a permissions // error, ENOTDIR) — right for a download gate, wrong to carry as a number: // Infinity does not survive JSON, so it reaches a client component as // `null` and every byte formatter downstream gets a surprise. An // unmeasurable root reports no free space at all, which is the honest // answer and the one the field is already optional for. let free: number; try { free = await onDrive(loc, () => getFreeBytes(root)); } catch (err) { if (isDriveNotAnswering(err)) return stalledProbe; throw err; } // Two ways a mount will not be there after a reboot: udisks put it under // /run/media (or /media) because a human plugged it in, or there is no // fstab entry naming its UUID. The fstab call is skipped when the // mountpoint already says "automount" — same warning, one less subprocess. let warning: string | undefined; if (identity.known) { const automounted = identity.mountpoint.startsWith("/run/media/") || identity.mountpoint.startsWith("/media/"); if (automounted || !(await isInFstab(identity.uuid, bins, timeoutMs))) { warning = automountWarning(loc, identity); } } return { status: "available", identity, ...(warning ? { warning } : {}), ...(Number.isFinite(free) ? { freeBytes: free } : {}), }; } // The root is not a directory (absent, or — treated identically — a file). // Without a recorded UUID there is nothing to look for. const uuid = loc.volume?.uuid?.trim() ?? ""; if (!uuid) return { status: "missing", identity: { known: false } }; const target = await mountpointOfUuid(uuid, bins, timeoutMs); if (target) { const relPath = loc.volume?.relPath ?? ""; return { status: "mounted-elsewhere", // A live sighting of the recorded volume: uuid and mountpoint come from // findmnt, fstype/label are carried over from the record (this query asks // for TARGET only). This is what the re-point job writes back as the // location's `volume`. identity: { known: true, uuid, fstype: loc.volume?.fstype, label: loc.volume?.label, mountpoint: target, relPath, }, candidateRoot: relPath ? path.join(target, relPath) : target, }; } // Not mounted. Is the disk even attached? `/dev/disk/by-uuid/` is a // symlink the kernel maintains; lstat it so a dangling link still counts as // "the udev entry is there". try { await lstat(path.join(opts.byUuidDir ?? BY_UUID_DIR, uuid)); return { status: "unmounted", identity: { known: false } }; } catch { return { status: "absent", identity: { known: false } }; } } // --------------------------------------------------------------------------- // The health probe: is the drive ANSWERING, asked from a child process // --------------------------------------------------------------------------- // // `probeLocation` answers "is the disk here" with an in-process `stat`, which is // right until the disk is here and not answering: then that `stat` does not // fail, it waits — for as long as the drive takes, on one of the four threads // libuv runs every filesystem call on. A child process waiting in the kernel // holds none of them. So this runs `stat` on the root as a SUBPROCESS and races // it against a timer, and the timer's answer is `stalled`. // // THE TIMER WINS, AND NOTHING WAITS FOR THE CHILD. A process in uninterruptible // I/O cannot be killed until the I/O returns, so awaiting its exit (which is // what execa's own `timeout` does) would hand the wait straight back to us. // The child is sent SIGKILL, which it takes when the drive lets it, and its // promise settles into a handler nobody awaits. // // FAILS OPEN, like everything else here: a `stat` that could not be started // (no binary) is "could not ask", which is `ok`, never `stalled`. Only a child // that started and did not answer in time is a stall. export type HealthProbeOptions = { timeoutMs?: number; // Test seam: the binary to run (it is called as ` -L -c %F -- `). statBin?: string; }; // One pass's answer about one location. `answer: null` is no verdict (the // counters' first sample, or a second one taken too soon after the last). export type HealthVerdict = { answer: LocationHealthState | null; detector?: HealthDetector; // For a `stalled` answer: what did not answer, in words with no path. cause?: string; // The block device the counters were read from. device?: string; }; // What the health pass asks about a location. A bare state is a verdict with // no detector named (the tests' scripted probes). export type LocationHealthProbe = ( loc: Pick, ) => Promise; export async function probeLocationHealth( loc: Pick, opts: HealthProbeOptions = {}, ): Promise { const root = loc.root.trim(); if (root === "") return "absent"; const timeoutMs = opts.timeoutMs ?? healthTimings().probeTimeoutMs; let child: ReturnType; try { child = execa(opts.statBin ?? "stat", ["-L", "-c", "%F", "--", root], { buffer: true, reject: false, stdin: "ignore", }); } catch { return "ok"; } const answered: Promise = child.then( (res) => { // No exit code: the binary never ran (or was killed after the race was // already decided). Could not ask. if (typeof res.exitCode !== "number") return "ok"; if (res.exitCode !== 0) return "absent"; const out = typeof res.stdout === "string" ? res.stdout.trim() : ""; return out === "directory" ? "ok" : "absent"; }, () => "ok", ); let timer: ReturnType | undefined; const timedOut = new Promise((resolve) => { timer = setTimeout(() => resolve("stalled"), timeoutMs); timer.unref?.(); }); const answer = await Promise.race([answered, timedOut]); if (timer) clearTimeout(timer); if (answer === "stalled") { try { child.kill("SIGKILL"); } catch { /* already gone */ } } return answer; } // --------------------------------------------------------------------------- // The counters detector: the block device's own request counters // --------------------------------------------------------------------------- // // THE STAT PROBE ABOVE CAN BE ANSWERED FROM THE KERNEL'S CACHE. A root's inode // is cached whenever anything has used the drive lately, so a child `stat` of // it answers in microseconds while the reads that actually reach the device // wait out a reset loop. The block device's counters (lib/storageHealth.ts, // `countersVerdict`) are the device's own account of what it has done, and // reading them touches only /sys. So the health pass asks this first: // // 1. the root's device: `findmnt -J -T -o SOURCE,UUID` as a child // raced against `probeTimeoutMs` (3 s by default; a findmnt stuck // resolving the root holds nothing of ours), the `[subvolume]` suffix a // bind or btrfs mount adds taken off, // `/dev/mapper/` resolved to its `dm-N`, then the basename. A partition // and a mapper device both have `/sys/class/block//stat`. Asked only // when the root has no device yet or its device's /sys entry cannot be read // (a drive replugged under another name): a findmnt per pass would leave // one child stuck per pass during a long stall. One that times out names // none this pass; one whose UUID is not the location's recorded one names // none (the root is then a directory on some other filesystem). // 2. that device's `stat` line, compared with the previous pass's sample for // the location (same device, at least `minCounterIntervalMs()` earlier). // // NO DEVICE (a container, no findmnt, a network or tmpfs mount, no /sys entry) // FALLS BACK TO THE CHILD `stat`, and the verdict says which detector answered. export type DetectorOptions = HealthProbeOptions & { // Test seams: where /sys/class/block is, and the clock. sysBlockDir?: string; now?: number; }; export const SYS_BLOCK_DIR = "/sys/class/block"; // Two samples closer than this are not compared: a healthy drive can have a // request in flight at two instants a moment apart without completing one. // Derived from the pass interval (`counterSampleMinimumMs`): 10 s at the // default 15 s, so a /storage Refresh just after a pass gives no verdict. export function minCounterIntervalMs(): number { return counterSampleMinimumMs(healthTimings().passIntervalMs); } type CounterSample = BlockStatSample & { device: string; at: number }; type DetectorState = { samples: Map; deviceByRoot: Map; }; declare global { // eslint-disable-next-line no-var var __yttHealthDetector__: DetectorState | undefined; } // ON globalThis, the house pattern: the health pass runs in instrumentation's // module copy and /storage's Refresh in a page's, and they must compare // against the same previous sample. function detectorState(): DetectorState { globalThis.__yttHealthDetector__ ??= { samples: new Map(), deviceByRoot: new Map(), }; return globalThis.__yttHealthDetector__; } export function resetHealthDetector(): void { detectorState().samples.clear(); detectorState().deviceByRoot.clear(); } // THE WATCHDOG'S READING of a device's counters (lib/storageHealth.ts, // `onDrive`): synchronous, so it does not wait behind the thread pool it is // judging, and cheap — a /sys read is answered by the kernel from memory and // never reaches the drive. function readCountersNow(device: string): BlockStatSample | null { try { return parseBlockStat(readFileSync(path.join(SYS_BLOCK_DIR, device, "stat"), "utf8")); } catch { return null; } } setCounterReader(readCountersNow); type Raced = { answered: false } | ({ answered: true } & Run); // `run`, but raced against a timer that nobody waits past: a child stuck in // the kernel is sent SIGKILL and left to exit when it can. async function runRaced(bin: string, args: string[], timeoutMs: number): Promise { let child: ReturnType; try { child = execa(bin, args, { buffer: true, reject: false, stdin: "ignore" }); } catch { return { answered: true, ok: false, exitCode: undefined, stdout: "", stderr: "" }; } const answered: Promise = child.then( (res) => { const exitCode = typeof res.exitCode === "number" ? res.exitCode : undefined; return { answered: true as const, ok: exitCode === 0, exitCode, stdout: typeof res.stdout === "string" ? res.stdout : "", stderr: typeof res.stderr === "string" ? res.stderr : "", }; }, () => ({ answered: true as const, ok: false, exitCode: undefined, stdout: "", stderr: "" }), ); let timer: ReturnType | undefined; const timedOut = new Promise((resolve) => { timer = setTimeout(() => resolve({ answered: false }), timeoutMs); }); const out = await Promise.race([answered, timedOut]); if (timer) clearTimeout(timer); if (!out.answered) { try { child.kill("SIGKILL"); } catch { /* already gone */ } } return out; } // The block device name under /sys/class/block for a mount SOURCE, or null. export async function blockDeviceName(source: string): Promise { const bare = source.replace(/\[.*\]$/, "").trim(); if (!bare.startsWith("/dev/")) return null; // /dev/mapper/ and /dev/disk/by-*/ are links to the kernel's name. // Resolving them reads /dev, never the drive. const resolved = await realpath(bare).catch(() => bare); const name = path.basename(resolved); return name && name !== "dev" ? name : null; } async function blockDeviceOfRoot( loc: Pick, bins: Pick, timeoutMs: number, ): Promise<{ device: string | null; timedOut: boolean }> { const res = await runRaced( bins.findmntBin, ["-J", "-T", loc.root, "-o", "SOURCE,UUID"], timeoutMs, ); if (!res.answered) return { device: null, timedOut: true }; if (!res.ok) return { device: null, timedOut: false }; let fs0: Record | undefined; try { fs0 = (JSON.parse(res.stdout) as { filesystems?: Record[] }) ?.filesystems?.[0]; } catch { return { device: null, timedOut: false }; } const source = typeof fs0?.source === "string" ? fs0.source : ""; const uuid = typeof fs0?.uuid === "string" ? fs0.uuid : ""; const recorded = loc.volume?.uuid?.trim() ?? ""; if (recorded && uuid && uuid !== recorded) return { device: null, timedOut: false }; return { device: await blockDeviceName(source), timedOut: false }; } async function readBlockStat( device: string, sysBlockDir: string, ): Promise { try { return parseBlockStat(await readFile(path.join(sysBlockDir, device, "stat"), "utf8")); } catch { return null; } } // One location's verdict for the health pass: the counters when its device can // be named and read, the child `stat` otherwise. export async function detectLocationHealth( loc: Pick, bins: Pick, opts: DetectorOptions = {}, ): Promise { const now = opts.now ?? Date.now(); const timeoutMs = opts.timeoutMs ?? healthTimings().probeTimeoutMs; const sysBlockDir = opts.sysBlockDir ?? SYS_BLOCK_DIR; const detector = detectorState(); // The device this root was last known on, if its /sys entry still reads; // findmnt only when there is none (see step 1 above). let device: string | null = detector.deviceByRoot.get(loc.root) ?? null; let sample = device ? await readBlockStat(device, sysBlockDir) : null; if (!sample) { detector.deviceByRoot.delete(loc.root); const mapped = loc.root.trim() ? await blockDeviceOfRoot(loc, bins, timeoutMs) : { device: null, timedOut: false }; device = mapped.device; sample = device ? await readBlockStat(device, sysBlockDir) : null; } if (device) { if (sample) { detector.deviceByRoot.set(loc.root, device); const prev = detector.samples.get(loc.id); if (prev && prev.device === device && now - prev.at < minCounterIntervalMs()) { return { answer: null, detector: "counters", device }; } detector.samples.set(loc.id, { ...sample, device, at: now }); if (!prev || prev.device !== device) { return { answer: null, detector: "counters", device }; } const answer = countersVerdict(prev, sample); return { answer, detector: "counters", device, ...(answer === "stalled" ? { cause: `its disk (${device}) had ${sample.inFlight} request(s) in flight and ` + `completed none in ${Math.round((now - prev.at) / 1000)} s`, } : {}), }; } } detector.samples.delete(loc.id); const answer = await probeLocationHealth(loc, opts); return { answer, detector: "stat", ...(answer === "stalled" ? { cause: `a stat of its root did not answer within ${secondsText(timeoutMs)}` } : {}), }; } // udisksctl availability, memoised per binary path. Same shape as a digest // app's probe (`digestApps.ts` claudeCode.probe): `--version`, reject:false, // short timeout. Memoised because /storage asks once per render and the answer // does not change while the process lives. const udisksctlProbes = new Map>(); export function udisksctlAvailable(bins: VolumeBins): Promise { const bin = bins.udisksctlBin; const hit = udisksctlProbes.get(bin); if (hit) return hit; const probe = run(bin, ["--version"], FINDMNT_TIMEOUT_MS).then((r) => r.ok); udisksctlProbes.set(bin, probe); return probe; } // Test seam: the memo above outlives a test's fake bin otherwise. export function resetUdisksctlProbeCache(): void { udisksctlProbes.clear(); } export type MountResult = { ok: boolean; // The mountpoint udisksctl reported, when it said one. mountpoint?: string; // udisksctl's own words. Surfaced verbatim — a polkit denial under a service // session is the expected failure and the operator needs to read it. error?: string; }; // Mount a volume by UUID. Offered only for an `unmounted` location, and only // when the binary resolves. NEVER RETRIED: a failure here is a policy or // hardware answer, and a second attempt just produces a second denial. export async function mountByUuid( uuid: string, bins: VolumeBins, opts: ProbeOptions = {}, ): Promise { if (!(await udisksctlAvailable(bins))) { return { ok: false, error: `udisksctl not found (set UDISKSCTL_BIN) — mount ${uuid} by hand`, }; } const res = await run( bins.udisksctlBin, ["mount", "-b", path.join(opts.byUuidDir ?? BY_UUID_DIR, uuid)], MOUNT_TIMEOUT_MS, ); if (!res.ok) { const said = res.stderr.trim() || res.stdout.trim(); return { ok: false, error: said || `udisksctl mount failed for ${uuid}` }; } // "Mounted /dev/sdb1 at /run/media//." — the trailing period is // part of the message and not part of the path. const m = /\bat\s+(.+?)\.?\s*$/m.exec(res.stdout.trim()); return { ok: true, mountpoint: m ? m[1] : undefined }; } // --------------------------------------------------------------------------- // The probe memo // --------------------------------------------------------------------------- export type MemoizedProbe = StorageLocationProbe & { // When this answer was taken, ms since epoch. The page renders it as an age. probedAt: number; }; export const PROBE_MEMO_MS = 10_000; type MemoEntry = { root: string; probedAt: number; probe: StorageLocationProbe }; const probeMemo = new Map(); // Test seam, and the escape hatch for a process that has just written a root. export function resetStorageProbeMemo(): void { probeMemo.clear(); } // Probe a location, at most once per PROBE_MEMO_MS. // // KEYED BY ID, INVALIDATED BY ROOT. /storage renders on every navigation and a // probe is up to three subprocesses; ten seconds is short enough that a disk // the operator just plugged in shows up on the next reload and long enough that // a page with six locations does not fork eighteen processes per click. The // root is carried in the entry because a re-point changes it under the same id, // and answering for the old root would show the operator the state they just // left. // // `refresh` BYPASSES the memo — that is what the Refresh button is for. It is // not the same as a short TTL: the operator pressing Refresh has just done // something physical (plugged the disk in, mounted it) and is asking for an // answer taken after it. export async function probeLocationMemo( loc: StorageLocation, bins: VolumeBins, opts: ProbeOptions & { refresh?: boolean; now?: number } = {}, ): Promise { const now = opts.now ?? Date.now(); // The stall is asked before the memo, so a remembered "available" from a few // seconds ago does not outlive the drive's answer. Not remembered either: // the health probe's state is already the memory. if (stalledLocation(loc)) { return { status: "stalled", identity: { known: false }, probedAt: now }; } const hit = probeMemo.get(loc.id); if ( !opts.refresh && hit && hit.root === loc.root && now - hit.probedAt < PROBE_MEMO_MS ) { return { ...hit.probe, probedAt: hit.probedAt }; } const probe = await probeLocation(loc, bins, opts); probeMemo.set(loc.id, { root: loc.root, probedAt: now, probe }); return { ...probe, probedAt: now }; }