One System page sample taken in named steps: host readings, disks with repository size, snapshot, history (v1.34.8) #111

Merged
mi merged 1 commits from mihoe/metrics-sample-by-resource into main 2026-10-04 18:35:09 +02:00
Owner

The Problem

After PR#110 SystemMetricsCollector.sample had the highest Mutation-CRAP score of Werkator: 35.36 at a complexity of 35.
A score is never below the complexity, so no test could bring it under the limit of 15.

The method did everything one sample of the System page needs in one body:
it read the processors, the memory and swap, the run queue and the stall times,
measured every disk against its budget and reset a disk's series when that budget changed,
built the snapshot, recorded the history, and persisted the state.
Most of its complexity was null handling: each of about twenty cpu?.usedCores, ram?.usedGib, reading?.space?.usedBytes asked again whether its source had been readable.

Non-Goals

  • No change in behaviour, in the snapshot, in the history, or in the persisted state.

The Scenarios

Feature: One sample of the System page, taken in named steps

Scenario#111.01: A sample still measures what it measured before

So that the System page, the health verdict and the sizing see the same numbers after the refactoring.

  • Given the existing collector tests: CPU, RAM, swap, swap traffic, load, pressure, OOM kills, disks with quotas, the usage walk, aggregates, persistence, and unreadable sources
  • When they run against the split sample
  • Then all of them pass unchanged
Verified by

The Solution

sample now reads as its domain steps:

Step What it does
readHost what the kernel says about the machine: processors, memory and swap, run queue, stalls, OOM kills, swap traffic
sampleStorage every monitored disk against its budget, and the repository size from the same usage walk
bindSource remembers what a disk was measured against, and restarts its series when that changed (Scenario#16.05)
diskMetricsOf the gauge of one disk; an unreadable disk keeps its key and label
snapshotOf the snapshot the UI and the health verdict read
historyValues the columns a host reading and a storage sample add to the history

A host reading that failed now holds its unread value (CpuLoad.UNREAD, RamUsage.UNREAD, LoadAverage.UNREAD) instead of null.
A gauge asks for its number, and the question whether the source was readable is answered once, where it is read.
RunningAggregates names the running min/max/avg per metric that sample kept as a local map and a local function.
DiskMetrics.unread is the gauge of a disk not measured, shared with the empty snapshot before the first sample.

Measured with PIT as the nightly build runs it:

Method Before After
SystemMetricsCollector.sample 35.36 3.04
diskMetricsOf — 4
RunningAggregates.record — 4
bindSource — 3
sampleStorage — 3
StorageSample.historyValues — 2
readHost — 1.13
snapshotOf — 1
HostReading.historyValues — 1

No new method of the collector scores above 4, and none is INCONCLUSIVE.

The gate maxMutationCrap falls from 36 to 35: the highest score left is BuildDefinition.applyTo at 34.

Additional Changes

  • Version 1.34.8 with its release note.

Follow-up PRs

  • BuildDefinition.applyTo (34) and CapacitySizing.cpu (33.17), each in its own PR.

🤖 Generated with Claude Code

## The Problem After PR#110 `SystemMetricsCollector.sample` had the highest Mutation-CRAP score of Werkator: 35.36 at a complexity of 35. A score is never below the complexity, so no test could bring it under the limit of 15. The method did everything one sample of the System page needs in one body: it read the processors, the memory and swap, the run queue and the stall times, measured every disk against its budget and reset a disk's series when that budget changed, built the snapshot, recorded the history, and persisted the state. Most of its complexity was null handling: each of about twenty `cpu?.usedCores`, `ram?.usedGib`, `reading?.space?.usedBytes` asked again whether its source had been readable. ## Non-Goals - No change in behaviour, in the snapshot, in the history, or in the persisted state. ## The Scenarios ### Feature: One sample of the System page, taken in named steps #### Scenario#111.01: A sample still measures what it measured before So that the System page, the health verdict and the sizing see the same numbers after the refactoring. - **Given** the existing collector tests: CPU, RAM, swap, swap traffic, load, pressure, OOM kills, disks with quotas, the usage walk, aggregates, persistence, and unreadable sources - **When** they run against the split `sample` - **Then** all of them pass unchanged ##### Verified by - [SystemMetricsCollectorUnitTest](../../src/test/kotlin/de/hoennig/werkator/metrics/SystemMetricsCollectorUnitTest.kt) — all 31 tests, unchanged ## The Solution `sample` now reads as its domain steps: | Step | What it does | | --------------- | --------------------------------------------------------------------------------------------------------------- | | `readHost` | what the kernel says about the machine: processors, memory and swap, run queue, stalls, OOM kills, swap traffic | | `sampleStorage` | every monitored disk against its budget, and the repository size from the same usage walk | | `bindSource` | remembers what a disk was measured against, and restarts its series when that changed (Scenario#16.05) | | `diskMetricsOf` | the gauge of one disk; an unreadable disk keeps its key and label | | `snapshotOf` | the snapshot the UI and the health verdict read | | `historyValues` | the columns a host reading and a storage sample add to the history | A host reading that failed now holds its unread value (`CpuLoad.UNREAD`, `RamUsage.UNREAD`, `LoadAverage.UNREAD`) instead of `null`. A gauge asks for its number, and the question whether the source was readable is answered once, where it is read. `RunningAggregates` names the running min/max/avg per metric that `sample` kept as a local map and a local function. `DiskMetrics.unread` is the gauge of a disk not measured, shared with the empty snapshot before the first sample. Measured with PIT as the nightly build runs it: | Method | Before | After | | ------------------------------- | ------ | ----- | | `SystemMetricsCollector.sample` | 35.36 | 3.04 | | `diskMetricsOf` | — | 4 | | `RunningAggregates.record` | — | 4 | | `bindSource` | — | 3 | | `sampleStorage` | — | 3 | | `StorageSample.historyValues` | — | 2 | | `readHost` | — | 1.13 | | `snapshotOf` | — | 1 | | `HostReading.historyValues` | — | 1 | No new method of the collector scores above 4, and none is `INCONCLUSIVE`. The gate `maxMutationCrap` falls from 36 to 35: the highest score left is `BuildDefinition.applyTo` at 34. ## Additional Changes - Version 1.34.8 with its release note. ## Follow-up PRs - `BuildDefinition.applyTo` (34) and `CapacitySizing.cpu` (33.17), each in its own PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
mi added 1 commit 2026-10-04 18:17:57 +02:00
SystemMetricsCollector.sample had complexity 35 and Mutation-CRAP 35.36,
which no test can bring below the limit of 15.

- readHost, sampleStorage, bindSource, diskMetricsOf, snapshotOf and the
  historyValues of HostReading/StorageSample are its domain steps.
- A failed host reading holds its UNREAD value instead of null, so a gauge
  no longer asks again whether its source was readable.
- RunningAggregates names the running min/max/avg per metric.
- DiskMetrics.unread is shared with the empty snapshot.

sample falls to 3.04; no new method scores above 4. Gate 36 -> 35.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mi merged commit c684a7906e into main 2026-10-04 18:35:09 +02:00
mi deleted branch mihoe/metrics-sample-by-resource 2026-10-04 18:35:09 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mi/werkator#111