7.5 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
The Problem
The System page (PR#29, PR#32) measures one disk: the filesystem of the served repository's checkout.
A Hostsharing Managed Webspace has two: the SSD home directory and the HDD tree under /home/storage/<pac>/users/<user>, each with its own group quota.
The plan is to keep the artifacts, which grow, on the HDD and everything else on the SSD.
Whether the HDD needs booking is then a question the page cannot answer, and the mount path is no title for a gauge.
A second problem surfaces with that plan: the measurement series live in system-metrics-state.json in the artifact root.
Moving the artifacts to the HDD would move the history with them, onto the slow disk.
Non-Goals
- Detecting SSD versus HDD; the labels come from the configuration.
- Moving the artifacts, the werkdock image store, or anything else; this PR only measures.
- Keeping the flat
diskTotalGib/diskUsedGib/diskFreeGib/diskSourcefields ofGET /api/system;werkator.jswas their only consumer.
The Scenarios
Feature: The System page shows every configured filesystem
Background
- The instance configuration
~/.werkator.ymlowns what is the host's, not a repository's (ADR 0009). - A monitored disk has a label (the gauge title), a path on the filesystem, and a key naming its series.
- The reading of a disk is the tightest of the user quota, the group quota, and the volume of that filesystem (PR#16).
Scenario#33.01: metrics.disks is an instance section
So that a host's mounts are configured once for the instance, and a branch cannot reach them by construction.
- Given
~/.werkator.ymldeclaresmetrics.diskswith a label and a path per entry - When a repository's effective configuration is loaded
- Then the section comes from the home file alone
- and a copy in a repository's own files is ignored with one warning, like
server - and the key of an entry defaults to its label lowercased (
SSD→ssd), with an explicitkeytaking precedence.
- and a copy in a repository's own files is ignored with one warning, like
Verified by
Scenario#33.02: every configured disk gets its own gauge, history, peak, health reason, and sizing chip
So that the SSD and the HDD are judged side by side, and the page says which of the two to book more of.
- Given two entries,
SSDon the home directory andHDDon the storage tree - When the collector samples
- Then each entry is read against its own filesystem's quota lines
- and its series are
disk.<key>.usedGibanddisk.<key>.freeGibin the state, the history, andGET /api/system/history - and
GET /api/systemcarriesdisks[]with key, label, total, source, and the aggregates, pluscapacity.disks[]with the advice per disk - and the page shows one gauge per disk in the configured order, one footer total each, and one chip each in the verdict panel
- and the machine verdict names the disk by its label (
HDD at 85 % (8.50 of 10.00 GiB)).
- and its series are
- Given no
metrics.diskssection - Then the page shows the checkout's filesystem as before, labelled
Disk, under the keydisk.
Verified by
Scenario#33.03: the history recorded for the single disk continues as the first configured disk
So that switching a running instance to two disks keeps the weeks of history the sizing advice needs.
- Given a state file written before this PR, with
diskUsedGib/diskFreeGibseries and adiskSourceKey - When the collector loads it under a configuration whose first disk has the key
ssd - Then the series and the history columns continue as
disk.ssd.usedGib/disk.ssd.freeGib- and the binding-source reset of Scenario#16.05 still fires only when that disk's source changes
- and the other disks start their columns empty.
Verified by
Scenario#33.04: the measurement series stay in the instance's state home
So that moving the artifacts to a slow, large volume does not move the history with them.
- Given a state file still in the served repository's artifact root
- When the server starts with this PR
- Then the file is moved once to
${XDG_STATE_HOME:-~/.local/state}/werkator/system-metrics-state.json- and the series continue there, whatever
artifacts.rootDirsays.
- and the series continue there, whatever
Verified by
The Solution
metrics.disks is a new instance-level section next to server and executor: ConfigLoader reads it from the home file alone and drops it from repository files with the same warning, so a branch cannot reach it.
Each entry becomes a MonitoredDisk (key, label, directory), resolved against the instance's home directory by MetricsConfiguration; without the section the served checkout is the one disk under the key disk.
SystemMetrics carries the disks as a list (DiskMetrics) instead of the flat fields.
The collector reads the quota output once per sample and matches every disk's file store against it, so two filesystems with two group quotas are two budgets, and keeps the binding source per disk key for the reset of PR#16.
The history tiers take their columns from what is sampled instead of a fixed key list, so a disk added later starts with nulls and one removed ages out.
HistoryPeaks, SystemHealth, and CapacitySizing iterate the disks: a reason names the disk by its label, the sizing advice is capacity.disks[] with key and label beside the verdict.
The page renders the gauges, the footer totals, and the chips from those lists; werkator.js resolves a card or chip keyed disk.<key> against disks[] of the polled snapshot and draws its chart from the disk.<key>.usedGib column.
A state file from before this PR is migrated on load: diskUsedGib/diskFreeGib become the first configured disk's series in the aggregates and in all three tiers, and the old diskSourceKey becomes that disk's entry.
The state file itself moves out of the artifact root into the instance's state home, once, because the series are the instance's and the artifacts are about to go elsewhere.
Open Questions
- The default disk without configuration is keyed
disk, so its series aredisk.disk.usedGib; a nicer default key would only matter for an API consumer. - A bare
~in YAML is null, not a path; the documentation and the template quote it.
Additional Changes
- The
.werkator.ymltemplate ofinitcarries the section commented out, anddocs/configuration.mddocuments it under the instance file.
Prerequisite PRs
- PR#29 (system page with history) and PR#32 (capacity advice).