Files
werkator/docs/prs/2026-10-10-PR#146-swap-out-card.md
claude-code 41da4413b5
werkator build succeeded after 02:09
werkator/deploy build succeeded after 00:08
Swap-out card with history on the System page, cards and tiles in one row (v1.39.0) (#146)
Co-authored-by: claude-code <claude-code@javagil.de>
2026-10-10 16:07:40 +02:00

145 lines
7.6 KiB
Markdown

> **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 showed swap as a small tile: the swap in use, and the swap traffic over five minutes.
The tile had no history, so a night of swapping was invisible the next morning.
The traffic counted pages swapped in and out.
On Hostsharing only swap-out costs: a page written to the swap changes a block that DRBD sends to the mirror and the backup stores.
A page read back changes nothing.
The swap in use matters less than both: pages that stay in the swap cost nothing.
A fifth card did not fit the row of four, and the eight tiles already broke into a second row at 1180 px.
## Non-Goals
- No RAM sizing rule on swap-out yet; the series is recorded first, as RFC 0004 asks.
- The health verdict is unchanged; it still names half the swap in use as pressure.
- The page stays 1180 px wide; the cards and tiles get narrower instead.
- The `swapTraffic` series of PR#102 is not migrated into `swapOut`: it counted the reads as well, so its numbers mean something else.
## The Scenarios
### Feature: Swap-out as a card with history
#### Scenario#146.01: Swap-out counts only the pages written to the swap
So that the card shows what costs the mirror and the backup, not what is read back.
- **Given** the `pswpin` and `pswpout` counters of `/proc/vmstat`
- **When** samples are taken a minute apart, ten minutes apart, and after the counter went backwards
- **Then** the swap-out is null for a single sample, the pages written per second in MiB/s, zero once the burst left the window, and null again after a reboot
- **And** `pswpin` changes none of these numbers
##### Verified by
- [SystemMetricsCollectorUnitTest](../../src/test/kotlin/de/hoennig/werkator/metrics/SystemMetricsCollectorUnitTest.kt) — Scenario#146.01
#### Scenario#146.02: Swap-out turns the swap readings amber and red, the occupancy only amber
So that only what costs the mirror can raise the alarm.
- **Given** snapshots with swap-out below, at the warning, and at the critical threshold, and with a lot of swap in use but no swap-out
- **When** the swap level is judged
- **Then** it is calm, amber, and red, and amber for the occupancy alone
- **And** a threshold set to null never fires
- **And** the home rail's tile reads `Swap-out 1.25 MiB/s`, with the swap in use as its tooltip
##### Verified by
- [UiViewsUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/UiViewsUnitTest.kt) — Scenario#146.02
#### Scenario#146.03: The swap-out card stands between RAM and the disks, scaled against the critical threshold
So that swap-out has a history and a peak like CPU, RAM and the disks, and its bar says how close it is to red.
- **Given** a host with swap and one monitored disk
- **When** the System page is rendered
- **Then** the cards are CPU, RAM, Swap-out and the disk, in this order
- **And** the swap-out card reads `0.25 MiB/s`, its detail line `0.10 of 4.00 GiB in use`, and its bar stands at 25 % of the critical 1.0 MiB/s
- **And** without a critical threshold the warning scales the bar, and a swap-out above it fills the bar, no more
- **And** before two samples the card reads `n/a` without a unit
- **And** the swap tile is gone, and a host without swap has no swap-out card
##### Verified by
- [UiViewsUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/UiViewsUnitTest.kt) — Scenario#146.03, and the updated Scenario#27.05 and Scenario#101.01
#### Scenario#146.04: The card says why the swap in use makes it amber
So that an amber card with `0.00 MiB/s` on it does not look like a mistake.
- **Given** more swap in use than `metrics.swapWarningGib`, or half the swap in use
- **When** the System page is rendered
- **Then** the swap-out card is amber with the note `more than 0.50 GiB in use` or `half the swap or more in use`
- **And** the tiles and the health verdict stay calm, as before
##### Verified by
- [UiViewsUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/UiViewsUnitTest.kt) — Scenario#146.04
#### Scenario#146.05: The thresholds are renamed, the old names still read
So that the key says what it judges, and a host file written for PR#102 keeps working.
- **Given** an instance file without the keys, with `swapOutWarningMibPerSecond` and `swapOutCriticalMibPerSecond`, and with the old `swapTraffic…` names
- **When** the configuration is loaded
- **Then** the thresholds are 0.1 and 1.0, or what the file says under either name, and null turns one off
- **And** where a file has both names, the new one wins
- **And** the old names log a warning once per file
##### Verified by
- [ConfigLoaderUnitTest](../../src/test/kotlin/de/hoennig/werkator/config/ConfigLoaderUnitTest.kt) — Scenario#146.05
#### Scenario#146.06: The swap-out's peak is judged against the thresholds
So that the peak line under the card says `watch` or `critical` like the card itself.
- **Given** a history tier with swap-out averages and maxima
- **When** the peaks are computed
- **Then** the swap-out's peak is the highest maximum with its time, has no total, and its level follows today's thresholds
- **And** a tier without swap-out values has no swap-out peak
##### Verified by
- [HistoryPeaksUnitTest](../../src/test/kotlin/de/hoennig/werkator/metrics/HistoryPeaksUnitTest.kt) — Scenario#146.06
#### Scenario#146.07: The cards and the tiles each stand in one row on a desktop screen
So that the whole machine is visible at a glance, however many disks a host shows.
- **Given** a window at least 1100 px wide
- **When** the System page shows five cards and seven tiles
- **Then** the cards stand in one row and the tiles in another
- **And** below 1100 px they wrap as before
##### Verified by
- checked by hand in the browser at 1280, 1100 and 900 px against a demo instance with two disks
## The Solution
- `SystemMetricsCollector` reads only `pswpout` now; the swap-out goes into the snapshot as `swapOutMibPerSecond` and into the history as the series `swapOut`.
- `metrics.swapOutWarningMibPerSecond` (0.1) and `…CriticalMibPerSecond` (1.0) replace the `swapTraffic…` keys.
`ConfigLoader` reads the old names under the new ones on the raw map of every layer, like the `bwrap` section, with a warning.
- `GaugeView.swapOut` builds the card: the value in MiB/s with the unit in small type (`valueUnit`), the bar against the critical threshold, the swap in use as detail line, and the occupancy as note.
It stands between RAM and the disks, so the memory cards stand together.
- `HistoryPeaks` adds the swap-out's peak, judged by `SystemHealth.swapOutLevel`.
- `werkator.js` mirrors it: `renderSwapOutCard`, and `drawSpark` takes `limits` for a rate — the dashed lines at the two thresholds, the colours by them, and the critical line at 90 % of the height as for a total.
The average beside the value is the bare number, since the value already carries the unit.
- The home rail keeps swap-out as a tile, not a row: `Swap-out` and `0.02 MiB/s` fit neither the label nor the value column of a row.
- From 1100 px window width the cards and the tiles each flow into one row (`grid-auto-flow: column`); the tile values are 16 px there, since `0 of 1 running` breaks at 18 px.
At 1100 px five cards are 196 px wide and seven tiles 139 px.
## Additional Changes
- `docs/configuration.md`, the `init` template and the architecture skill describe the new keys and the card.
- Version 1.39.0 with its release note.
## Follow-up PRs
- A RAM sizing rule on swap-out, once a few weeks of the `swapOut` series show what its numbers mean.