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
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.
## 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)
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 main2026-10-04 18:35:09 +02:00
mi
deleted branch mihoe/metrics-sample-by-resource2026-10-04 18:35:09 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The Problem
After PR#110
SystemMetricsCollector.samplehad 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?.usedBytesasked again whether its source had been readable.Non-Goals
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.
sampleVerified by
The Solution
samplenow reads as its domain steps:readHostsampleStoragebindSourcediskMetricsOfsnapshotOfhistoryValuesA host reading that failed now holds its unread value (
CpuLoad.UNREAD,RamUsage.UNREAD,LoadAverage.UNREAD) instead ofnull.A gauge asks for its number, and the question whether the source was readable is answered once, where it is read.
RunningAggregatesnames the running min/max/avg per metric thatsamplekept as a local map and a local function.DiskMetrics.unreadis 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:
SystemMetricsCollector.samplediskMetricsOfRunningAggregates.recordbindSourcesampleStorageStorageSample.historyValuesreadHostsnapshotOfHostReading.historyValuesNo new method of the collector scores above 4, and none is
INCONCLUSIVE.The gate
maxMutationCrapfalls from 36 to 35: the highest score left isBuildDefinition.applyToat 34.Additional Changes
Follow-up PRs
BuildDefinition.applyTo(34) andCapacitySizing.cpu(33.17), each in its own PR.🤖 Generated with Claude Code