Skip to content

Scope pinned metric instruments to resources - #21711

Open
David Pine (IEvangelist) wants to merge 10 commits into
mainfrom
ievangelist-dashboard-resource-metric-pins
Open

David Pine (IEvangelist) wants to merge 10 commits into
mainfrom
ievangelist-dashboard-resource-metric-pins

Conversation

@IEvangelist

Copy link
Copy Markdown
Member

Description

Pinning a metric instrument from one resource previously pinned every resource that emitted the same meter and instrument. This made the pinned section broader than the user's action, especially for Aspire.TerminalHost metrics emitted by several terminal resources.

This change scopes new metric pins to the emitting telemetry resource key. Resources and replicas can now pin or unpin the same instrument independently in both all-resource and resource-scoped Metrics views. The card and chart pin controls pass the actual resource key through to MetricPinStore, and pinned group counts use the same resource-aware lookup.

Existing settings databases are migrated without guessing a resource. Legacy rows gain a nullable resource identity and remain explicit global pins, preserving their previous behavior. Unpinning a migrated global pin removes that global record; pinning again creates a resource-scoped record.

User-facing usage

Users can pin an instrument card for one resource without implicitly pinning matching cards from other resources or replicas. The selected resource remains pinned after dashboard reloads.

Screenshots / Recordings

390x844 768x1024 1440x900
Resource-scoped metric pin at 390x844 Resource-scoped metric pin at 768x1024 Resource-scoped metric pin at 1440x900

Validation

  • dotnet test --project tests\Aspire.Dashboard.Tests\Aspire.Dashboard.Tests.csproj --no-launch-profile -- --filter-class "*.MetricPinStoreTests" --filter-class "*.DashboardSettingsDatabaseTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true" - passed, 9 tests.
  • dotnet test --project tests\Aspire.Dashboard.Components.Tests\Aspire.Dashboard.Components.Tests.csproj --no-launch-profile -- --filter-class "*.MetricsTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true" - passed, 43 tests.
  • Live Terminals AppHost validation - pointer pinning affected only the selected resource; keyboard pin/unpin kept matching resources independent; reload preserved the scoped pin.

Fixes #21710

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 21711

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 21711"

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Replica-group pinning can produce no cards, persistence conflates null and empty instance IDs and loses pins after reload, and pooled test connections can prevent temporary database cleanup on Windows.

3 open issues
What changed in this PR

This PR makes dashboard metric pins resource-specific, addressing #21710 so matching instruments from different resources or replicas can be pinned independently. Existing global pins retain their previous behavior during migration.

Changes:

  • Adds resource identity to pin storage and migrates legacy records.
  • Passes resource keys through card and chart controls and pinned-group counts.
  • Updates tests for scoped persistence, independent pinning, and legacy migration.
File Description
tests/​Aspire.Dashboard.Tests/​Model/​MetricPinStoreTests.cs Tests resource isolation, persistence, and legacy migration.
tests/​Aspire.Dashboard.Tests/​Model/​DashboardSettingsDatabaseTests.cs Updates fallback assertions for resource-scoped pins.
tests/​Aspire.Dashboard.Components.Tests/​Pages/​MetricsTests.cs Adds independent card-pinning coverage and updates scoped assertions.
src/​Aspire.Dashboard/​Model/​MetricPinStore.cs Stores resource-scoped pins and migrates legacy global records.
src/​Aspire.Dashboard/​Components/​Controls/​MetricGroupList.razor.cs Counts pins using resource identity.
src/​Aspire.Dashboard/​Components/​Controls/​Chart/​MetricPinButton.razor Requires a resource key for pin operations.
src/​Aspire.Dashboard/​Components/​Controls/​Chart/​MeterOverview.razor Supplies each card’s emitting resource key.
src/​Aspire.Dashboard/​Components/​Controls/​Chart/​ChartContainer.razor Supplies a resource key to the chart pin control.

Review effort: Balanced

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Pin filtering and counts use deduplicated instrument summaries, so pinning only a non-retained replica can leave the replica-group pinned view empty; preserve per-replica summaries and add regression coverage.

1 open issue
3 resolved since last review

Review effort: Balanced

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

In replica-group views, pinned cards show group-aggregated metrics, and opening a non-first replica’s card targets the first replica’s chart and pin state instead.

2 open issues
1 resolved since last review

Review effort: Balanced

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Replica-group pinned cards lack identifying replica labels, and “Back to Pinned” restores only the emitting replica’s pins instead of the original group’s pins.

2 open issues
2 resolved since last review

Review effort: Balanced

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed to evaluate subtle navigation-state and replica-group pinning issues involving layout selection, null instance IDs, and resource-name casing.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The persistence problem below should be fixed before merge: pins scoped by instance ID are lost on each AppHost restart. The other comments are smaller and could follow up separately.

  • Correctness: 3 (pins lost across runs, Back in ResourcesLayout, aggregated cards pinning one replica)
  • Minor robustness: 2 (name comparison, schema creation race)

I found these by reading the code at 4dbd4aa and did not run them. CI is green for this commit, but its tests don't cover restarting with new instance IDs or running under the layout.

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The supplied diff omits MetricPinStore, its migration, and the main Metrics changes, preventing validation of persistence and independent replica-scoping acceptance criteria.

No open issues 🎉

Review effort: Balanced

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Active issues cause searches started from Pinned to pin only a replica instead of its group and indexed scoped-schema migrations to fail, leaving saved pins unloaded and persistence disabled.

2 open issues

Review effort: Balanced

Keep replica-group pin identity across filtered and chart views, stabilize telemetry-only pins, simplify schema migration, and clarify group unpin behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Telemetry-only resource pins can move to another replica when an earlier-sorting instance joins because identity is derived from list position rather than an instance-specific fallback.

1 open issue
2 resolved since last review
1 file not reviewed
  • src/Aspire.Dashboard/Resources/Metrics.Designer.cs: Generated file

Review effort: Balanced

Keep pins attached to the emitting instance when earlier-sorting telemetry resources arrive, while retaining app-model replica identities.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Tests selector

14 / 101 PR test projects · 2 PR jobs, from 30 changed files.

Selected PR test projects (14 / 101)

Aspire.Dashboard.Components.Tests, Aspire.Dashboard.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Playground.Tests, Aspire.Templates.Tests, Infrastructure.Tests

Selected PR jobs (2)

extension-e2e, native-dashboard-validation


How these were chosen — grouped by what changed

⚠️ 10 of the 14 selected test projects come from a single change — src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor.

🔧 src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor (changed source)
→ 10 via the project graph: Aspire.Hosting.Azure.Kubernetes.Tests (3 hops), Aspire.Hosting.Azure.Tests (2 hops), Aspire.Hosting.Blazor.Tests (2 hops), Aspire.Hosting.Dotnet.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Radius.Tests (2 hops), Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Playground.Tests (2 hops)

📦 affected project Aspire.Dashboard
→ 1 test: Aspire.Templates.Tests

🔧 src/Aspire.Dashboard/Resources/Metrics.resx (changed source)
→ 1 directly: Infrastructure.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Pages/MetricsTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Tests/Model/DashboardSettingsDatabaseTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Dashboard.Tests/Model/MetricPinStoreTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Tests

Job reasons

Job Triggered by
extension-e2e • src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor, src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor.cs, src/Aspire.Dashboard/Components/Controls/Chart/MeterOverview.razor, src/Aspire.Dashboard/Components/Controls/Chart/MeterOverview.razor.cs, src/Aspire.Dashboard/Components/Controls/Chart/MetricPinButton.razor, src/Aspire.Dashboard/Components/Controls/MetricGroupList.razor.cs, src/Aspire.Dashboard/Components/Pages/Metrics.razor, src/Aspire.Dashboard/Components/Pages/Metrics.razor.cs, src/Aspire.Dashboard/Components/Pages/ResourceOverview.razor, src/Aspire.Dashboard/Components/Pages/ResourceOverview.razor.cs, src/Aspire.Dashboard/Model/MetricPinStore.cs, src/Aspire.Dashboard/Resources/Metrics.Designer.cs, src/Aspire.Dashboard/Resources/Metrics.resx, src/Aspire.Dashboard/Resources/xlf/Metrics.cs.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.de.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.es.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.fr.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.it.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.ja.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.ko.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.pl.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.pt-BR.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.ru.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.tr.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.zh-Hans.xlf, src/Aspire.Dashboard/Resources/xlf/Metrics.zh-Hant.xlf
• affected project Aspire.Dashboard
native-dashboard-validation affected project Aspire.Dashboard

Selection computed for commit b2de2a4.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed because timing-dependent interactions between replica discovery and instrument refresh can leave saved metric pins temporarily missing.

No open issues 🎉

1 resolved since last review
1 file not reviewed
  • src/Aspire.Dashboard/Resources/Metrics.Designer.cs: Generated file

Review effort: Balanced

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a rework before it can merge. The PR now conflicts with main: #21840 moved metric pins from the settings database to browser local storage (LocalStorageSetting<List<OtlpInstrumentKey>>) and deleted DashboardSettingsDatabaseTests.cs. The SQLite schema, migration and tests in MetricPinStore can't be carried over as they are.

The resource-scoped pins will need a new local-storage shape. Values already stored as a plain list of instrument keys should still read as global pins, which is what the SQLite migration did for the old table. My earlier approval was for the SQLite version, so the persistence code will need another look once it's rebuilt.

I checked the changes since c0823e492b:

  • Fixed: filter results opened from the Pinned view, the aggregate chart, the Resolve lookup cost and the development-schema migration.
  • Kept as a tradeoff: telemetry-only resources still save their instance ID, which the new code comment explains. The reply on that thread still describes the earlier ordinal fallback, which was later reverted.
  • Not fixed: the label on replica cards for group pins (inline below).

This comes from reading the diff at b2de2a4102 and a local merge check against main. I didn't run the dashboard.


@{
var label = _isPinned ? Loc[nameof(MetricsResources.MetricsUnpinInstrument)] : Loc[nameof(MetricsResources.MetricsPinInstrument)];
var label = ResourceKey.IsGroup

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replica cards still don't say that Unpin removes the pin for all replicas. This label depends on the button's own key, but the case from the earlier thread uses replica keys. In a replica group's Pinned view, OverviewGroupPinResourceKey is null, so each card's button gets (api, 1) or (api, 2), and IsGroup is false. The tooltip says "Unpin metric", yet SetPinned finds the group pin through Matches and deletes it, so the other replica's card disappears too.

The new test checks the tooltip only on a group card, where the button already has the group key. The label needs to come from the pin that matched, for example through a MetricPinStore method that reports whether the matching pin is a group pin.

public MetricPinResourceKey? GroupPinResourceKey { get; set; }

[Parameter]
public IReadOnlyList<OtlpResource>? Resources { get; set; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing reads this parameter: Resolve no longer takes a telemetry resource list, and neither MeterOverview nor LiveSparklinesBase uses Resources. Removing it, along with the Resources="@_resources" in Metrics.razor and the two builder.Add(component => component.Resources, ...) calls in MetricsTests, avoids suggesting that cards depend on it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dashboard: scope pinned metric instruments to the selected resource

3 participants