github robintra/perf-sentinel v0.5.0

latest releases: chart-v0.2.48, v0.8.3, chart-v0.2.47...
one month ago

What's new in v0.5.0

Major feature release: v0.5.0 introduces the report subcommand and its single-file HTML dashboard, a new GET /api/export/report daemon endpoint that pairs with it for live snapshots, a fourth pg_stat ranking (by_io_blocks), and a check-versions CI guard that refuses to publish on a tag / Cargo.toml mismatch. The Report tree also gains Deserialize so saved baselines round-trip back in. 1248 tests across the workspace, clippy strict (-D warnings, #![warn(clippy::pedantic)]) clean, zero open SonarCloud issues on the shipped code.

Added

  • report subcommand: single-file HTML dashboard. perf-sentinel report --input traces.json --output report.html produces a self-contained HTML file with six possible tabs (Findings, Explain, GreenOps unconditionally, pg_stat / Diff / Correlations when the relevant data is present). Works offline from file://, zero CDN / fonts / external resources, no build step. The sink embeds findings-only traces and trims to a ~5 MB target via a linear-time prefix-sum scan that keeps the highest-IIS traces first. --input accepts a trace file, a pre-computed Report JSON, or - for stdin (auto-detects array-of-events vs Report object, BOM-tolerant). Security model: user-controlled data is injected inside a <script type="application/json"> block, rendered exclusively via Element.textContent, and guarded by a build-time no_forbidden_apis_in_template test that rejects innerHTML, insertAdjacentHTML, outerHTML, eval, new Function, DOMParser, createContextualFragment and setAttribute("on*"). The </ substring is escaped to <\/ in the serialized payload so a user value cannot close the script block early. Full design rationale in docs/design/07-CLI-CONFIG-RELEASE.md § report subcommand. Implemented in crates/sentinel-core/src/report/html.rs and html_template.html.
  • --pg-stat FILE and --pg-stat-prometheus URL flags on report. Embed a PostgreSQL pg_stat_statements hotspot tab alongside the trace findings. Four rankings driven by a sub-switcher in the dashboard: by_total_time, by_calls, by_mean_time, by_io_blocks. The Prometheus path reuses the one-shot fetch_from_prometheus helper. --pg-stat and --pg-stat-prometheus are mutually exclusive at the clap level.
  • --before baseline.json flag on report. Compare the current run against a baseline Report and light up a Diff tab with four sections: new findings (clickable, open Explain), resolved findings, severity changes and per-endpoint I/O op deltas. Identity matching is (finding_type, service, source_endpoint, pattern.template).
  • Daemon endpoint GET /api/export/report. Returns the daemon's current state as a Report JSON snapshot, shape-identical to analyze --format json. Pipe curl -s http://daemon:4318/api/export/report | perf-sentinel report --input - for a browser dashboard of live production state. Cold-start behavior: HTTP 503 with {"error": "daemon has not yet processed any events"} until the first OTLP batch has been processed, so a fresh daemon never renders misleading zero-counter views. Snapshot is not atomic across findings and correlations, the two collections can be one batch apart. Documented in docs/QUERY-API.md + FR and docs/design/06-INGESTION-AND-DAEMON.md.
  • Dashboard ergonomics. Per-tab Export CSV button on Findings, pg_stat, Diff and Correlations, with RFC 4180 escaping plus an OWASP formula-injection guard that prefixes an apostrophe on cells starting with =, +, -, @, or tab. Deep-link hash encodes the active tab, search term, pg_stat ranking and Findings severity / service chips, so a shared URL like report.html#pgstat&ranking=mean_time&search=payment restores the exact filtered view. sessionStorage persistence for theme and last-active pg_stat ranking, tab-scoped to avoid file:// origin collisions. Native <dialog> cheatsheet modal opened via ? with focus trap managed by the browser, vim-style g f / g e / g p / g d / g c / g r tab-switch shortcuts with a 1000ms timeout on the pending g and autorepeat guarded. Four-tier Esc ladder: close cheatsheet -> close search -> back from Explain -> clear active filter chips. Findings pagination replaces a hard 500-row cap with a Show N more findings (remaining M) button; every filter, search or hash apply resets the visible count.
  • Fourth pg_stat ranking: by_io_blocks (shared_blks_hit + shared_blks_read). Cache-pressure signal that complements by_total_time: flags queries that touch the most shared-buffer pages regardless of whether they were hit or miss. The stable ranking order is [by_total_time, by_calls, by_mean_time, by_io_blocks], new rankings are appended, existing indices never reassign so the HTML sub-switcher and any other position-indexed consumer stay stable. Documented in docs/design/06-INGESTION-AND-DAEMON.md § "Four-ranking output".
  • CI: check-versions release workflow guard. A new job runs first on any v* tag push and refuses to build or publish anything if the tag does not match workspace.package.version in the root Cargo.toml and every crates/*/Cargo.toml (hardcoded versions honored, version.workspace = true resolves to the workspace). Logic lives in scripts/check-tag-version.sh so the same guard runs locally before tagging: ./scripts/check-tag-version.sh v0.5.0. Every downstream job transitively depends on it via needs: check-versions on build, so a version mismatch aborts the workflow in ~10 seconds before any GitHub Release is created, any binary is uploaded, or any crate is pushed to crates.io.

Changed

  • Report tree now derives Deserialize. Cascade across Report, Analysis, GreenSummary, QualityGate, QualityRule, TopOffender, CarbonReport, CarbonEstimate, RegionBreakdown, IntensitySource so a saved Report JSON can be fed back into perf-sentinel report --before baseline.json for diff mode. Three &'static str fields (CarbonEstimate::model, CarbonEstimate::methodology, RegionBreakdown::status) become String for serde round-trip. A module-level invariant comment in crates/sentinel-core/src/report/mod.rs pins the "every new field must be Option<T> or carry #[serde(default)]" rule so stored baselines keep parsing across future minor versions. deny_unknown_fields deliberately not applied, the trade-off is documented.

Docs

  • New documentation for the v0.5.0 surface across EN + FR mirrors. docs/INTEGRATION.md + FR gain a dedicated HTML dashboard section with the keyboard ladder, pagination and sharing semantics. docs/CONFIGURATION.md + FR subcommand table adds report, plus the tempo and calibrate rows that were missing. docs/QUERY-API.md + FR document GET /api/export/report and its cold-start contract. docs/RUNBOOK.md + FR add a /api/export/report returns 503 or an empty report troubleshooting entry. docs/LIMITATIONS.md + FR document the CSV formula-injection guard. docs/design/06-INGESTION-AND-DAEMON.md + FR add the four-ranking section, the --pg-stat-prometheus integration paragraph, and the /api/export/report snapshot semantics. docs/design/07-CLI-CONFIG-RELEASE.md + FR describe the report subcommand and its client-side ergonomics. Mermaid diagrams cli-commands.mmd and query-api.mmd updated to depict the new subcommands and endpoint (SVGs regenerated).
  • SECURITY.md grows six HTML-dashboard-specific bullets covering textContent-only rendering, the </ script-tag escape, prototype-pollution hardening via Object.create(null), CSV formula-injection guard, deep-link hash allowlist, and offline self-contained output with zero CDN/font fetches. Supported-versions table bumped to 0.5.x.
  • CONTRIBUTING.md grows a Release process section pointing at scripts/check-tag-version.sh and listing every non-Cargo.toml file that also takes a version bump.
  • README + README-FR gain dedicated HTML dashboard report and PR regression diff subsections with representative command-line examples.
  • CHANGELOG.md introduced at the repo root in Keep-a-Changelog format, seeded with the [0.5.0] section that mirrors this release body.

Install

Prebuilt binaries (Linux amd64 / arm64, macOS arm64, Windows amd64):

curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.0/perf-sentinel-linux-amd64
chmod +x perf-sentinel-linux-amd64
sudo mv perf-sentinel-linux-amd64 /usr/local/bin/perf-sentinel

Linux binaries are statically linked against musl and run on any distribution (Alpine, Debian, RHEL, Ubuntu any version) regardless of glibc version, and inside FROM scratch images.

From crates.io:

cargo install perf-sentinel

Docker:

docker run --rm -p 4317:4317 -p 4318:4318 \
  ghcr.io/robintra/perf-sentinel:0.5.0 watch --listen-address 0.0.0.0

Also available on Docker Hub: robintrassard/perf-sentinel:0.5.0.

Verify the binary against SHA256SUMS.txt:

curl -LO https://github.com/robintra/perf-sentinel/releases/download/v0.5.0/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missing

Full diff: v0.4.8...v0.5.0

Don't miss a new perf-sentinel release

NewReleases is sending notifications on new releases.