Skip to content

Commit

Permalink
Improve DSC summary view loading performance (#3397)
Browse files Browse the repository at this point in the history
  • Loading branch information
AmelBawa-msft authored Jul 24, 2024
1 parent 8440838 commit 6ce663a
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace DevHome.Services.DesiredStateConfiguration.Contracts;

Expand All @@ -13,10 +15,15 @@ public interface IDSCUnit
public string Type { get; }

/// <summary>
/// Gets an identifier used to uniquely identify the instance of a configuration unit on the system.
/// Gets the identifier name of this instance within the set.
/// </summary>
public string Id { get; }

/// <summary>
/// Gets an identifier used to uniquely identify the instance of a configuration unit on the system.
/// </summary>
public Guid InstanceId { get; }

/// <summary>
/// Gets a description of the configuration unit.
/// </summary>
Expand All @@ -27,6 +34,11 @@ public interface IDSCUnit
/// </summary>
public string Intent { get; }

/// <summary>
/// Gets the name of the module that this configuration unit belongs to.
/// </summary>
public string ModuleName { get; }

/// <summary>
/// Gets the <see cref="Id"/> values of the configuration units that this unit depends on.
/// </summary>
Expand All @@ -43,7 +55,8 @@ public interface IDSCUnit
public IList<KeyValuePair<string, string>> Metadata { get; }

/// <summary>
/// Gets the information on the origin of the configuration unit if available.
/// Gets the details of the configuration unit.
/// </summary>
public IDSCUnitDetails Details { get; }
/// <returns>The details of the configuration unit.</returns>
public Task<IDSCUnitDetails> GetDetailsAsync();
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ namespace DevHome.Services.DesiredStateConfiguration.Models;

internal sealed class DSCSet : IDSCSet
{
/// <inheritdoc/>
public Guid InstanceIdentifier { get; }

/// <inheritdoc/>
public string Name { get; }

/// <inheritdoc />
public IReadOnlyList<IDSCUnit> Units => UnitsInternal.AsReadOnly();

/// <summary>
/// Gets the list of units in this set.
/// </summary>
/// <remarks>
/// This list maintains the concrete type of the objects which is internal
/// to this service project.
/// </remarks>
internal IList<DSCUnit> UnitsInternal { get; }

public DSCSet(ConfigurationSet configSet)
{
// Constructor copies all the required data from the out-of-proc COM
Expand All @@ -19,12 +37,6 @@ public DSCSet(ConfigurationSet configSet)
// longer available (e.g. AppInstaller service is no longer running).
InstanceIdentifier = configSet.InstanceIdentifier;
Name = configSet.Name;
Units = configSet.Units.Select(unit => new DSCUnit(unit)).ToList();
UnitsInternal = configSet.Units.Select(unit => new DSCUnit(unit)).ToList();
}

public Guid InstanceIdentifier { get; }

public string Name { get; }

public IReadOnlyList<IDSCUnit> Units { get; }
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DevHome.Services.DesiredStateConfiguration.Contracts;
using Microsoft.Management.Configuration;

Expand All @@ -12,6 +14,35 @@ internal sealed class DSCUnit : IDSCUnit
{
private const string DescriptionMetadataKey = "description";
private const string ModuleMetadataKey = "module";
private readonly IDSCUnitDetails _defaultDetails;
private Task<IDSCUnitDetails> _loadDetailsTask;

/// <inheritdoc/>
public string Type { get; }

/// <inheritdoc/>
public string Id { get; }

/// <inheritdoc/>
public Guid InstanceId { get; }

/// <inheritdoc/>
public string Description { get; }

/// <inheritdoc/>
public string Intent { get; }

/// <inheritdoc/>
public string ModuleName { get; }

/// <inheritdoc/>
public IList<string> Dependencies { get; }

/// <inheritdoc/>
public IList<KeyValuePair<string, string>> Settings { get; }

/// <inheritdoc/>
public IList<KeyValuePair<string, string>> Metadata { get; }

public DSCUnit(ConfigurationUnit unit)
{
Expand All @@ -21,6 +52,7 @@ public DSCUnit(ConfigurationUnit unit)
// longer available (e.g. AppInstaller service is no longer running).
Type = unit.Type;
Id = unit.Identifier;
InstanceId = unit.InstanceIdentifier;
Intent = unit.Intent.ToString();
Dependencies = [.. unit.Dependencies];

Expand All @@ -32,33 +64,27 @@ public DSCUnit(ConfigurationUnit unit)
Settings = unit.Settings.Select(s => new KeyValuePair<string, string>(s.Key, s.Value.ToString())).ToList();
Metadata = unit.Metadata.Select(m => new KeyValuePair<string, string>(m.Key, m.Value.ToString())).ToList();

// Load details if available, otherwise create empty details with just
// the module name if available
if (unit.Details == null)
{
// Get module name from metadata
unit.Metadata.TryGetValue(ModuleMetadataKey, out var moduleObj);
Details = new DSCUnitDetails(moduleObj?.ToString() ?? string.Empty);
}
else
{
Details = new DSCUnitDetails(unit.Details);
}
}

public string Type { get; }
// Get module name from metadata
ModuleName = Metadata.FirstOrDefault(m => m.Key == ModuleMetadataKey).Value?.ToString() ?? string.Empty;

public string Id { get; }

public string Description { get; }

public string Intent { get; }

public IList<string> Dependencies { get; }

public IList<KeyValuePair<string, string>> Settings { get; }
// Build default details
_defaultDetails = unit.Details == null ? new DSCUnitDetails(ModuleName) : new DSCUnitDetails(unit.Details);
_loadDetailsTask = Task.FromResult(_defaultDetails);
}

public IList<KeyValuePair<string, string>> Metadata { get; }
/// <inheritdoc/>
public async Task<IDSCUnitDetails> GetDetailsAsync()
{
var loadedDetails = await _loadDetailsTask;
return loadedDetails ?? _defaultDetails;
}

public IDSCUnitDetails Details { get; }
/// <summary>
/// Set an asynchronous task to load the details for the unit in the background.
/// </summary>
/// <param name="loadDetailsTask">Task to load the details</param>
internal void SetLoadDetailsTask(Task<IDSCUnitDetails> loadDetailsTask)
{
_loadDetailsTask = loadDetailsTask;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Linq;
using System.Threading.Tasks;
using DevHome.Services.DesiredStateConfiguration.Contracts;
using DevHome.Services.DesiredStateConfiguration.Exceptions;
Expand Down Expand Up @@ -44,8 +45,32 @@ public async Task<IDSCSet> GetConfigurationUnitDetailsAsync(IDSCFile file)
var configSet = await OpenConfigurationSetAsync(file, processor);

_logger.LogInformation("Getting configuration unit details");
await processor.GetSetDetailsAsync(configSet, ConfigurationUnitDetailFlags.ReadOnly);
return new DSCSet(configSet);
var detailsOperation = processor.GetSetDetailsAsync(configSet, ConfigurationUnitDetailFlags.ReadOnly);
var detailsOperationTask = detailsOperation.AsTask();

var set = new DSCSet(configSet);

// For each DSC unit, create a task to get the details asynchronously
// in the background
foreach (var unit in set.UnitsInternal)
{
unit.SetLoadDetailsTask(Task.Run<IDSCUnitDetails>(async () =>
{
try
{
await detailsOperationTask;
_logger.LogInformation($"Settings details for unit {unit.InstanceId}");
return GetCompleteUnitDetails(configSet, unit.InstanceId);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to get details for unit {unit.InstanceId}");
return null;
}
}));
}

return set;
}

/// <inheritdoc />
Expand Down Expand Up @@ -157,4 +182,30 @@ private static async Task<InMemoryRandomAccessStream> StringToStreamAsync(string
result.Seek(0);
return result;
}

/// <summary>
/// Gets the complete details for a unit if available.
/// </summary>
/// <param name="configSet">Configuration set</param>
/// <param name="instanceId">Unit instance ID</param>
/// <returns>Complete unit details if available, otherwise null</returns>
private DSCUnitDetails GetCompleteUnitDetails(ConfigurationSet configSet, Guid instanceId)
{
var unitFound = configSet.Units.FirstOrDefault(u => u.InstanceIdentifier == instanceId);
if (unitFound == null)
{
_logger.LogWarning($"Unit {instanceId} not found in the configuration set. No further details will be available to the unit.");
return null;
}

if (unitFound.Details == null)
{
_logger.LogWarning($"Details for unit {instanceId} not found. No further details will be available to the unit.");
return null;
}

// After GetSetDetailsAsync completes, the Details property will be
// populated if the details were found.
return new DSCUnitDetails(unitFound.Details);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using DevHome.Services.DesiredStateConfiguration.Contracts;

namespace DevHome.SetupFlow.ViewModels;

public class DSCConfigurationUnitDetailsViewModel
{
private readonly IDSCUnitDetails _unitDetails;

public DSCConfigurationUnitDetailsViewModel(IDSCUnitDetails unitDetails)
{
_unitDetails = unitDetails;
}

public string UnitType => _unitDetails.UnitType;

public string UnitDescription => _unitDetails.UnitDescription;

public string UnitDocumentationUri => _unitDetails.UnitDocumentationUri;

public string ModuleName => _unitDetails.ModuleName;

public string ModuleType => _unitDetails.ModuleType;

public string ModuleSource => _unitDetails.ModuleSource;

public string ModuleDescription => _unitDetails.ModuleDescription;

public string ModuleDocumentationUri => _unitDetails.ModuleDocumentationUri;

public string PublishedModuleUri => _unitDetails.PublishedModuleUri;

public string Version => _unitDetails.Version;

public bool IsLocal => _unitDetails.IsLocal;

public string Author => _unitDetails.Author;

public string Publisher => _unitDetails.Publisher;

public bool IsPublic => _unitDetails.IsPublic;
}
Loading

0 comments on commit 6ce663a

Please sign in to comment.