| File: ServiceClient\DashboardDataSource.cs | Web Access |
| Project: src\src\Aspire.Dashboard\Aspire.Dashboard.csproj (Aspire.Dashboard) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Aspire.Dashboard.Otlp.Storage; namespace Aspire.Dashboard.ServiceClient; /// <summary> /// Controls the dashboard run selected in the current scope. /// </summary> public interface IDashboardRunSelection { /// <summary> /// Gets the selected dashboard run. /// </summary> DashboardRunDescriptor SelectedRun { get; } /// <summary> /// Selects the dashboard run with the specified identifier. /// </summary> /// <param name="runId">The run identifier, or <see langword="null"/> to select the current run.</param> void SelectRun(string? runId); } /// <summary> /// Provides repositories for the dashboard run selected in the current scope. /// </summary> public sealed class DashboardDataSource : IDashboardRunSelection, IDisposable { private readonly IDashboardRunStore _runStore; private readonly ILogger<DashboardDataSource> _logger; private readonly DashboardDataSourcePool _dataSourcePool; private DashboardDataSourcePool.Lease? _historicalDataSourceLease; /// <summary> /// Initializes a new instance of the <see cref="DashboardDataSource"/> class. /// </summary> /// <param name="runStore">The store that provides available dashboard runs.</param> /// <param name="logger">The logger used to record dashboard run selection.</param> /// <param name="dataSourcePool">The caller-owned pool that provides dashboard run data sources.</param> public DashboardDataSource( IDashboardRunStore runStore, ILogger<DashboardDataSource> logger, DashboardDataSourcePool dataSourcePool) { _runStore = runStore; _logger = logger; _dataSourcePool = dataSourcePool; SelectRun(runId: null); } internal DashboardRunDescriptor SelectedRun { get; private set; } = null!; /// <summary> /// Gets the telemetry repository for the selected dashboard run. /// </summary> public ITelemetryRepository TelemetryRepository { get; private set; } = null!; /// <summary> /// Gets the resource repository for the selected dashboard run. /// </summary> public IResourceRepository ResourceRepository { get; private set; } = null!; internal bool IsReadOnly { get; private set; } internal void EnsureWritable() { if (IsReadOnly) { throw new InvalidOperationException("Historical dashboard data is read-only."); } } DashboardRunDescriptor IDashboardRunSelection.SelectedRun => SelectedRun; void IDashboardRunSelection.SelectRun(string? runId) => SelectRun(runId); internal void SelectRun(string? runId) { var currentRun = _runStore.GetCurrentRun(); var selectedRun = runId is not null ? _runStore.GetRunById(runId) : null; if (selectedRun is null) { if (!string.IsNullOrEmpty(runId)) { _logger.LogWarning("Failed to switch to dashboard run '{RunId}' because it is no longer available.", runId); } selectedRun = currentRun; } if (SelectedRun?.RunId == selectedRun.RunId) { return; } var previousRun = SelectedRun; if (!selectedRun.IsCurrent) { DashboardDataSourcePool.Lease? historicalDataSourceLease; try { historicalDataSourceLease = _dataSourcePool.TryAcquire(selectedRun); } catch (Exception exception) { _logger.LogWarning(exception, "Failed to switch to dashboard run '{RunId}'.", selectedRun.RunId); throw; } if (historicalDataSourceLease is null) { _logger.LogWarning("Failed to switch to dashboard run '{RunId}' because it is no longer available.", selectedRun.RunId); return; } var previousHistoricalDataSourceLease = _historicalDataSourceLease; _historicalDataSourceLease = historicalDataSourceLease; TelemetryRepository = historicalDataSourceLease.TelemetryRepository; ResourceRepository = historicalDataSourceLease.ResourceRepository; IsReadOnly = true; SelectedRun = selectedRun; previousHistoricalDataSourceLease?.Dispose(); } else { var previousHistoricalDataSourceLease = _historicalDataSourceLease; _historicalDataSourceLease = null; SelectCurrentRun(selectedRun); previousHistoricalDataSourceLease?.Dispose(); } LogRunSwitch(previousRun, selectedRun); } public void Dispose() { DisposeHistoricalDataSource(); } private void DisposeHistoricalDataSource() { _historicalDataSourceLease?.Dispose(); _historicalDataSourceLease = null; } private void SelectCurrentRun(DashboardRunDescriptor currentRun) { var currentDataSource = _dataSourcePool.Current; TelemetryRepository = currentDataSource.TelemetryRepository; ResourceRepository = currentDataSource.ResourceRepository; IsReadOnly = false; SelectedRun = currentRun; } private void LogRunSwitch(DashboardRunDescriptor? previousRun, DashboardRunDescriptor selectedRun) { if (previousRun is not null && !string.Equals(previousRun.RunId, selectedRun.RunId, StringComparison.Ordinal)) { _logger.LogDebug( "Switched dashboard run from '{PreviousRunId}' to '{RunId}'.", previousRun.RunId, selectedRun.RunId); } } }