Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Lite's portable ZIP is self-contained, which HALVED it** ([#2501]) - `Publish Lite` is now `-r win-x64 --self-contained` in both `build.yml` and `nightly.yml`, so neither Lite artifact has a .NET prerequisite any more and the failure [#2489] documented stops existing: a tester who unzips onto a stock Windows Server no longer meets the .NET host's bare `You must install .NET to run this application` before a line of our code runs. **The size went the opposite way from what bundling a runtime suggests.** The old publish was RID-agnostic, so it copied every platform its packages ship - **537 MB of `runtimes\` on a 565 MB tree** (osx 130, linux-x64 116, linux-arm64 70, win-arm64 56, then win-x86, musl, loongarch64 and riscv64), of which only the **52 MB `win-x64`** folder could ever load on Windows. `DuckDB.NET.Bindings.Full` is most of it, SkiaSharp and SqlClient behind it. Dropping ~485 MB of unloadable native payload beats the cost of bundling .NET, WPF and ASP.NET Core by roughly two to one: measured on one commit and one SDK, **565 MB tree / 212.7 MB zipped becomes 277 MB / 114.2 MB**. It matters most for the **nightly** ZIP, which is the UAT download and is not offered as a `Setup.exe` at all. **A RID-specific publish needed two more files than the flag.** `Lite/packages.lock.json` had only a `net10.0-windows7.0` target, and a RID restore adds `net10.0-windows7.0/win-x64` to it - after which the `dotnet restore --locked-mode` that BOTH workflows run before the publish fails `NU1004: the project's runtime identifiers have changed`, because locked mode compares the PROJECT's RID set (empty) against the lock file's (win-x64). Reproduced locally; that is a red CI run on every PR, not the future `--no-restore` trap it was filed as. The fix is `<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>` in `PerformanceMonitorLite.csproj`, so the project itself asks for that graph and one committed lock file satisfies the RID-less locked-mode restore and the RID publish alike; `RuntimeIdentifiers` (plural) sets no RID on the build, so a plain `dotnet build` stays RID-agnostic and `Lite.Tests` is untouched. **SignPath needed nothing** - the `Lite` artifact-configuration slug already receives both shapes today, and the signed re-zip reads `signed/Lite/*`, inheriting whatever shape `publish/Lite` has. Auto-update is unaffected; the ZIP is not a Velopack channel. `LiteRuntimePrerequisiteDocsTests` went red on the flag alone (3 of its 7 facts) and was rewritten to state every claim BOTH ways round: [#2499]'s version asserted only that the docs DID name the runtimes, so two of its facts stayed green while the prose went stale. It now also derives the lock file's RID coverage from the `-r` flags in the workflows, and every new assertion was proven red with its fix reverted.

### Fixed
- **Web dashboard: the composer's partial-window notice read "1 days" on a one-day store** ([#2785]) - the day count in BuildRetentionNotice was interpolated with a hardcoded "days" plural, so a store reaching back a single day read "reaches back about 1 days, but the requested window starts 1 days back". It now pluralises on the rendered number - "1" singular, "1.5"/"30" plural.
- **Web dashboard: an offline server no longer shows a green "Collectors OK"** ([#2779]) - the summary Collectors chip keyed its verdict on the FAILING count alone, and a stale collector counts as neither healthy nor failing, so a server that had stopped collecting kept a green "Collectors OK - N healthy - 0 failing" even while its own header read "no recent collection" and its Collection Health tab showed every collector STALE. The chip now reuses the reachability signal the card already carries (is_online, the same one that bands the card Offline): an offline server reads "Stale - no recent collection" in the neutral tone instead. One shared metricBands builder, so the fleet cards and the per-server detail header are both fixed at once - and no new stale-count threshold to over-fire on normally-infrequent collectors.
- **Web dashboard: an over-long time range showed a raw API validation error instead of a range hint** ([#2780]) - the Range dropdown offers "last 30 days", but the per-server CPU/query reads cap at 168 hours and return an error for anything wider, which the web dashboard mounted verbatim ("hours_back value '720' exceeds maximum of 168 hours..."). A shared read-error renderer (used by every panel, inline or composite) now recognises that specific over-range error and renders a notice naming the window the view keeps ("This view keeps up to 168 hours (7 days) of history - pick a shorter range") rather than the API's developer wording.
- **Web dashboard: Alert History dropped the meaningless "tray" delivery channel** ([#2781]) - the status cell appended the notification channel, which the store always records as "tray" (the Lite/Dashboard system-tray toast) - a channel the headless web surface has no equivalent for, so "Not sent - tray" read as noise. The "tray" channel is now suppressed on the web; a real channel (email/webhook) still renders. Fixed on both the Alert History page and the triage deep-link page, which mirror the same status cell.
Expand Down Expand Up @@ -3101,6 +3102,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#2749]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/2749
[#2764]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/2764
[#2766]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/2766
[#2785]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/2785
[#2779]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/2779
[#2776]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/2776
[#2772]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/2772
Expand Down
16 changes: 16 additions & 0 deletions Darling/Darling.Tests/DarlingComposeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2665,6 +2665,22 @@ 40 days used to be past the horizon and is now comfortably inside it. */
hourlyNotice,
StringComparison.Ordinal);

/* Singular grammar (#2779-session dogfood): a store that only reaches back ONE day must read "1 day", not
"1 days". Measured hourly floor at now-1d, window 2 days back — held rounds to "1" (singular), window
to "2" (plural), so both arms are exercised in one notice. */
var oneDayCoverage = new RollupCoverage(
new Dictionary<string, DateTime>(StringComparer.Ordinal)
{
[TimescaleSupport.QueryStatsHourlyView] = now.AddDays(-1),
},
new Dictionary<string, DateTime>(StringComparer.Ordinal));
var singularNotice = ComposeStoreAvailability.BuildRetentionNotice(
"query_stats", hourlyRoute, now.AddDays(-2), now, RollupAvailability.All, oneDayCoverage);
Assert.NotNull(singularNotice);
Assert.Contains("reaches back about 1 day,", singularNotice, StringComparison.Ordinal);
Assert.Contains("2 days back", singularNotice, StringComparison.Ordinal);
Assert.DoesNotContain("1 days", singularNotice, StringComparison.Ordinal);

/* Hourly route, window INSIDE the horizon: silent. */
Assert.Null(ComposeStoreAvailability.BuildRetentionNotice("query_stats", hourlyRoute, now.AddDays(-10), now, RollupAvailability.All, RollupCoverage.Unknown));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,14 @@ can still start after the window does (#1759). Measured only: with no floor ther

var windowDays = (nowUtc - windowStartUtc).TotalDays;
var heldDays = (nowUtc - oldestHeld).TotalDays;
/* Pluralise on the RENDERED number, not the raw double: heldDays formats as a whole number and windowDays
to one decimal, so "1" is singular ("1 day") while "1.5" and "30" are plural — a one-day store no
longer reads "reaches back about 1 days". */
var heldText = heldDays.ToString("0", CultureInfo.InvariantCulture);
var windowText = windowDays.ToString("0.#", CultureInfo.InvariantCulture);
return string.Create(
CultureInfo.InvariantCulture,
$"partial window: this panel read the {tierName} tier, which on this store reaches back about {heldDays:0} days, but the requested window starts {windowDays:0.#} days back — older points are not included.");
$"partial window: this panel read the {tierName} tier, which on this store reaches back about {heldText} day{(heldText == "1" ? "" : "s")}, but the requested window starts {windowText} day{(windowText == "1" ? "" : "s")} back — older points are not included.");
}

/// <summary>
Expand Down
Loading