github robintra/perf-sentinel v0.14.0

latest release: chart-v0.14.0
5 hours ago

What's new in v0.14.0

v0.14.0 is about the workloads the carbon table did not know how to price, and about the gap that had opened between the two ways of reading a report. A deployment on OVHcloud, Scaleway or 3DS OUTSCALE fell into the synthetic unknown bucket and contributed zero operational CO2, which is not a missing figure but a wrong one, on three European sovereign clouds. And the terminal browser had been left behind by the dashboard over several releases, on sorting, filtering and per-finding statistics.

Three sovereign clouds contributed zero CO2

42 region keys join the intensity table, next to AWS, GCP and Azure. 36 of them are not hand-entered: they come from the same Ember refresh by ISO3 country code that already feeds the other providers, so the script stays the single source and a new grid year updates them all at once. The remaining 6 sit on subnational grids a country average would misprice, Quebec hydro, PJM, Oregon and CAISO, and join the existing hand-kept rows in MANUAL_CARBON_ROWS exactly as the AWS and Azure US regions already do.

OVHcloud names one datacenter three ways depending on which API answers, GRA11 for the OpenStack Public Cloud region, GRA for the zone code and gra for the S3 location string. All three are keyed, because which one reaches cloud.region depends on the SDK the caller instruments, not on the datacenter. OUTSCALE keys carry an outscale- prefix instead, because OUTSCALE reuses AWS region identifiers for different places: its eu-west-2 is Paris where the AWS one is London, 41.5 against 217.4 gCO2/kWh on the same string. An unprefixed row would have scored a French deployment on the British grid without anything in the output to say so.

PUE is per provider, as it already was for the other three: 1.24 for OVHcloud, the FY25 group average measured over twelve months and audited by Apave against ISO 30134-2, and 1.375 for Scaleway, the 2024 average over ten datacenters. OUTSCALE publishes none and rents capacity rather than operating its own datacenters, so its rows ride the generic prior rather than a number invented for them. The 1.2 its partner advertises is a design figure for one site, and the 1.64 that circulates traces back to a 2021 infographic that does not state it.

Each new region also gets its own hourly-profile alias. Without one it would have resolved to the flat annual value while looking fully supported, which is the failure mode this release exists to remove. The datacenter-level PUE that both OVHcloud and Scaleway publish is deliberately not modeled: an 8% spread around a group average sits far inside the 2x bracket the whole estimate already carries.

A power profile where no wattage is published

65 Scaleway instance types get an embedded power profile, and [green.cloud] accepts provider = "scaleway". Cloud Carbon Footprint publishes coefficients for the three hyperscalers only, which is why this table had held nothing else. The blocker turned out to be narrower than "no data exists": Scaleway publishes no wattage, but its Product Catalog API names the exact CPU of every offer without authentication, AMD EPYC 7543 rather than a generic family, alongside the vCPU count and whether the vCPUs are shared. Each offer is therefore priced by the CCF coefficient for that CPU's architecture, the same vCPU-times-coefficient arithmetic the AWS, GCP and Azure rows already use. That adds one assumption rather than a measurement: that a coefficient computed on hyperscaler fleets transfers to comparable silicon elsewhere, which is what CCF already assumes between AWS and GCP.

35 POP2 rows come from the AWS CSV as EPYC 3rd Gen. The 30 COMPUTE3, MEMORY3 and STANDARD3 rows ride the same SPECpower-direct figure as GCP c4d, being the same EPYC 9005 generation, because the CCF EPYC 5th Gen row reads 3.68 idle watts per vCPU against 0.74 for the neighbouring generation. That is the upstream error the m8a family already works around.

Three groups are excluded rather than approximated, each named in the docs. Shared-vCPU ranges (DEV1, PLAY2, BASIC2, BASIC3), where attributing a whole vCPU to one tenant overstates what it draws. GPU offers (H100, L4, L40S, RENDER), because the table models no accelerator for any provider and one H100 outweighs the entire CPU budget. And the AmpereOne and Granite Rapids ranges, absent from the CSVs. An excluded type behaves as any unlisted type does, warning once and falling back to the provider average. The offer list is read live at refresh time rather than pinned by hand, so a new Scaleway size lands in the table on the next refresh-datasets run and only the CPU-to-architecture mapping stays hand-maintained.

OVHcloud and OUTSCALE stay out of this table. OVHcloud documents no CPU per range, and the only third-party attribution, Boavizta's, is flagged CPU not verified by Boavizta itself on all 12 of its OVHcloud rows, one of which credits a two-socket rack server with a mobile Intel Core i7-4940MX. OUTSCALE stops at (Region, service category) and appears nowhere in Boavizta. A fallback would be a fabricated wattage, so those machines stay covered by Alumet or Scaphandre.

The terminal browser had fallen behind

An audit of the two report surfaces found the terminal browser quietly out of date. The dashboard gained sorting, filtering and per-finding statistics across several releases while inspect kept the shape it shipped with, and no CHANGELOG entry ever claimed a terminal port for them, where recurrence (0.10.0) and grouping (0.11.0) explicitly did.

The Detail panel now shows the per-span timing (p50, p99, CV), how an n+1 was classified, the observation window and the finding's confidence, all announced in 0.11.0 and 0.5.4 and all HTML-only until now. The first two also reach the text report, which was missing them as well. s sorts the findings inside the selected trace and not just the trace list, on the comparator analyze --sort and the dashboard already share, and query inspect accepts --sort where it had no ordering control at all. f cycles the Findings panel through the severities, the family the dashboard puts on pills.

The Analyze view leads with the report's warnings, which say what the figures below them do not cover and which every other surface has always printed, and it ranks the worst findings by impact next to the top offenders it already listed, two cards the Overview has had since 0.9.10. The operator monitor (query monitor) is a separate surface with a separate audience and is deliberately untouched.

Severity filters that combine

The dashboard's severity pills were the last filter family still behaving as a radiogroup. A click on warnings dropped the critical selection, so "show me everything but the infos" was not expressible, while the three other families had accepted several values at once since the toolbar was reorganised. They are toggles now, OR'd like the rest, and no pill pressed already means no restriction, so nothing has to be pressed to see everything.

The All pill is gone, and a Clear filters button closes the row instead. All had been doing two jobs and only one of them survives multi-select: it emptied the whole toolbar, severities, services, types and grouping together, and dropping it would have left a pointer user un-pressing every pill and unticking every box one at a time. The button sits at the end of the row, appears only once something is filtered, and carries no fill or border so it never reads as one more pill waiting to be pressed. Escape is its keyboard equivalent, as before.

The Overview severity cards set the row rather than adding to it, so the list they open still holds the number printed on the card. The URL carries the selection as a list (severity=critical,info) on the same encoding as the other families, and a link written before this change with a single value restores as it did. A severity naming no finding is dropped on restore rather than applied, matching how the service, type and grouping values already behave.

Two smaller corrections in the same surface. The Overview's view all and open diff buttons drop their underline: they are pills with a border, a fill, a hover state and a trailing arrow, so the underline was a third affordance on two that already read as clickable, and it borrowed a hyperlink's signal for a control that navigates a tab rather than a document. And the dashboard counters agree with their own noun at one, so a single finding no longer reads 1 findings. g m opens the mysql_stat tab, the only tab left without a shortcut since it shipped in 0.9.6.

Both sides of a correlation

Each cross-trace correlation card is split into two click zones, each resolved against the findings on type, service, grouping and template. The demo dashboard built its endpoints by hand with no grouping at all, while every demo finding carries one, so once the match tightened to require agreement on both sides nothing resolved. The left zone rendered with no cursor, no hover and no handler, the right one survived only through its sample-trace fallback, all six pills fell back to the default amber whatever the finding's severity, and the cards stopped sorting by severity because every card ranked the same.

The endpoints now borrow the template and the grouping from the finding they name, so they follow whatever grouping attribute the run is configured with instead of restating one, and a test asserts every side resolves. The browser fixture carried the mirror image of the defect, endpoints naming a grouping its findings did not have, and it tagged only one of its two trace sets: the correlation test asserted a live zone somewhere on the panel rather than on each card, which is how a panel with every left half dead still passed. A side that genuinely resolves nothing, on a report whose findings were truncated out of the embedded payload, now says so on hover rather than looking clickable and doing nothing.

Upgrade impact

Carbon figures change on identical data for any workload on OVHcloud, Scaleway or OUTSCALE. They were zero, in the unknown bucket, so a report from 0.13.1 and the same report from 0.14.0 are not comparable for those deployments. Nothing moves for AWS, GCP, Azure or unrecognised regions.

An OUTSCALE deployment must declare the prefixed key. [green] default_region = "outscale-eu-west-2" scores Paris, where the unprefixed eu-west-2 remains the AWS London region it has always been. No existing configuration changes meaning.

[green.cloud] accepts provider = "scaleway". It still refuses OVHcloud and OUTSCALE, which no CPU-level data supports.

A dashboard link shared from 0.13.1 still restores. The severity family now carries a comma-separated list where it carried one value, and a single value parses as a list of one.

query inspect gains --sort, and the TUI gains s and f. Nothing that was bound before is rebound.

The Prometheus label sets are unchanged, the daemon API surface is unchanged, the file-based --input paths are unchanged, and the minimum supported Rust version stays 1.97.1.

Validation

The full Rust suite passes (3268 tests), clippy is clean at -D warnings across all targets, cargo fmt --check passes, and the default and --no-default-features builds both pass, along with the single-feature tui and daemon combinations.

The dashboard work rests on the Playwright suite, 49 specs against a real browser, covering the combined severity selection, the URL round trip and both click zones of every correlation card. The region and PUE tables are covered by unit tests pinning each provider's intensity against a same-country neighbour, plus a regression test asserting that no outscale- key ever resolves to its AWS namesake. The Scaleway power rows are checked against the architecture they borrow from.

No simulation-lab run was recorded for this revision. The release changes scoring inputs, the report surfaces and the terminal browser, and touches no daemon lifecycle, listener or endpoint.

Verifying this release

# Binary integrity via SLSA Build L3 attestation
gh attestation verify perf-sentinel-linux-amd64 \
  --repo robintra/perf-sentinel

# A periodic disclosure produced by this binary
perf-sentinel verify-hash --report perf-sentinel-report.json \
  --expected-identity "https://github.com/robintra/perf-sentinel/.github/workflows/release.yml@refs/tags/v0.14.0" \
  --expected-issuer "https://token.actions.githubusercontent.com" \
  --verify-binary ./perf-sentinel-linux-amd64

gh CLI 2.49 or newer required for gh attestation verify.

Don't miss a new perf-sentinel release

NewReleases is sending notifications on new releases.