mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-12-03 20:13:52 +08:00
PERF: Re-use cached filehash for dynamic module hash
Before recompiling a dynamic module (.csproj), the BuildManager verifies that the file hash (returned from the VPP) has changed from the previous compilation. Instead of returning of the file hash of the .csproj file and all its virtual dependencies (source files and references), use the file hash value stored in the file managed by IExtensionDependenciesManager. This improves startup time by 10+ seconds on file i/o bound machines. --HG-- branch : 1.x
This commit is contained in:
@@ -85,14 +85,37 @@ namespace Orchard.Environment.Extensions {
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFileHash(ExtensionLoadingContext context, string path) {
|
||||
private string GetFileHash(ExtensionLoadingContext context, string extensionId) {
|
||||
var hash = new Hash();
|
||||
hash.AddString(path);
|
||||
hash.AddString(extensionId);
|
||||
|
||||
DateTime dateTime;
|
||||
if (context.VirtualPathModficationDates.TryGetValue(path, out dateTime)) {
|
||||
hash.AddDateTime(dateTime);
|
||||
{
|
||||
ExtensionProbeEntry extensionProbe;
|
||||
if (context.ProcessedExtensions.TryGetValue(extensionId, out extensionProbe)) {
|
||||
if (extensionProbe != null) {
|
||||
var virtualPathDependencies = extensionProbe.VirtualPathDependencies;
|
||||
foreach (var virtualpathDependency in virtualPathDependencies) {
|
||||
DateTime dateTime;
|
||||
if (context.VirtualPathModficationDates.TryGetValue(virtualpathDependency, out dateTime)) {
|
||||
hash.AddDateTime(dateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ExtensionReferenceProbeEntry extensionReferenceProbe;
|
||||
if (context.ProcessedReferences.TryGetValue(extensionId, out extensionReferenceProbe)) {
|
||||
if (extensionReferenceProbe != null) {
|
||||
DateTime dateTime;
|
||||
if (context.VirtualPathModficationDates.TryGetValue(extensionReferenceProbe.VirtualPath, out dateTime)) {
|
||||
hash.AddDateTime(dateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hash.Value;
|
||||
}
|
||||
|
||||
@@ -309,8 +332,8 @@ namespace Orchard.Environment.Extensions {
|
||||
|
||||
// Activate the binary ref
|
||||
if (bestBinaryReference != null) {
|
||||
if (!context.ProcessedReferences.Contains(bestBinaryReference.Entry.Name)) {
|
||||
context.ProcessedReferences.Add(bestBinaryReference.Entry.Name);
|
||||
if (!context.ProcessedReferences.ContainsKey(bestBinaryReference.Entry.Name)) {
|
||||
context.ProcessedReferences.Add(bestBinaryReference.Entry.Name, bestBinaryReference.Entry);
|
||||
bestBinaryReference.Entry.Loader.ReferenceActivated(context, bestBinaryReference.Entry);
|
||||
}
|
||||
activatedReferences.Add(new DependencyReferenceDescriptor {
|
||||
|
||||
@@ -9,14 +9,14 @@ namespace Orchard.Environment.Extensions {
|
||||
public class ExtensionLoadingContext {
|
||||
public ExtensionLoadingContext() {
|
||||
ProcessedExtensions = new Dictionary<string, ExtensionProbeEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
ProcessedReferences = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
ProcessedReferences = new Dictionary<string, ExtensionReferenceProbeEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
DeleteActions = new List<Action>();
|
||||
CopyActions = new List<Action>();
|
||||
NewDependencies = new List<DependencyDescriptor>();
|
||||
}
|
||||
|
||||
public IDictionary<string, ExtensionProbeEntry> ProcessedExtensions { get; private set; }
|
||||
public ISet<string> ProcessedReferences { get; private set; }
|
||||
public IDictionary<string, ExtensionReferenceProbeEntry> ProcessedReferences { get; private set; }
|
||||
|
||||
public IList<DependencyDescriptor> NewDependencies { get; private set; }
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.Caching;
|
||||
using Orchard.FileSystems.AppData;
|
||||
using Orchard.FileSystems.VirtualPath;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.FileSystems.Dependencies {
|
||||
@@ -14,12 +13,17 @@ namespace Orchard.FileSystems.Dependencies {
|
||||
/// the file stored by this component will also change.
|
||||
/// </summary>
|
||||
public class DefaultExtensionDependenciesManager : IExtensionDependenciesManager {
|
||||
private readonly IAppDataFolder _appDataFolder;
|
||||
private const string BasePath = "Dependencies";
|
||||
private const string FileName = "Dependencies.ModuleCompilation.xml";
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly IAppDataFolder _appDataFolder;
|
||||
private readonly InvalidationToken _writeThroughToken;
|
||||
|
||||
public DefaultExtensionDependenciesManager(IAppDataFolder appDataFolder) {
|
||||
public DefaultExtensionDependenciesManager(ICacheManager cacheManager, IAppDataFolder appDataFolder) {
|
||||
_cacheManager = cacheManager;
|
||||
_appDataFolder = appDataFolder;
|
||||
_writeThroughToken = new InvalidationToken();
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
@@ -53,6 +57,23 @@ namespace Orchard.FileSystems.Dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
public ActivatedExtensionDescriptor GetDescriptor(string extensionId) {
|
||||
return LoadDescriptors().FirstOrDefault(d => d.ExtensionId.Equals(extensionId, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public IEnumerable<ActivatedExtensionDescriptor> LoadDescriptors() {
|
||||
return _cacheManager.Get(PersistencePath,
|
||||
ctx => {
|
||||
_appDataFolder.CreateDirectory(BasePath);
|
||||
ctx.Monitor(_appDataFolder.WhenPathChanges(ctx.Key));
|
||||
|
||||
_writeThroughToken.IsCurrent = true;
|
||||
ctx.Monitor(_writeThroughToken);
|
||||
|
||||
return ReadDescriptors(ctx.Key).ToList();
|
||||
});
|
||||
}
|
||||
|
||||
private XDocument CreateDocument(IEnumerable<DependencyDescriptor> dependencies, Func<string, string> fileHashProvider) {
|
||||
Func<string, XName> ns = (name => XName.Get(name));
|
||||
|
||||
@@ -60,21 +81,42 @@ namespace Orchard.FileSystems.Dependencies {
|
||||
document.Add(new XElement(ns("Dependencies")));
|
||||
var elements = FilterDependencies(dependencies).Select(
|
||||
d => new XElement("Dependency",
|
||||
new XElement(ns("ModuleName"), d.Name),
|
||||
new XElement(ns("ExtensionId"), d.Name),
|
||||
new XElement(ns("LoaderName"), d.LoaderName),
|
||||
new XElement(ns("VirtualPath"), d.VirtualPath),
|
||||
new XElement(ns("FileHash"), fileHashProvider(d.VirtualPath)),
|
||||
new XElement(ns("FileHash"), fileHashProvider(d.Name)),
|
||||
new XElement(ns("References"), FilterReferences(d.References)
|
||||
.Select(r => new XElement(ns("Reference"),
|
||||
new XElement(ns("Name"), r.Name),
|
||||
new XElement(ns("ReferenceId"), r.Name),
|
||||
new XElement(ns("LoaderName"), r.LoaderName),
|
||||
new XElement(ns("VirtualPath"), r.VirtualPath),
|
||||
new XElement(ns("FileHash"), fileHashProvider(r.VirtualPath)))).ToArray())));
|
||||
new XElement(ns("FileHash"), fileHashProvider(r.Name)))).ToArray())));
|
||||
|
||||
document.Root.Add(elements);
|
||||
return document;
|
||||
}
|
||||
|
||||
private IEnumerable<ActivatedExtensionDescriptor> ReadDescriptors(string persistancePath) {
|
||||
Func<string, XName> ns = (name => XName.Get(name));
|
||||
Func<XElement, string, string> elem = (e, name) => e.Element(ns(name)).Value;
|
||||
|
||||
XDocument document = ReadDocument(persistancePath);
|
||||
return document
|
||||
.Elements(ns("Dependencies"))
|
||||
.Elements(ns("Dependency"))
|
||||
.Select(e => new ActivatedExtensionDescriptor {
|
||||
ExtensionId = elem(e, "ExtensionId"),
|
||||
VirtualPath = elem(e, "VirtualPath"),
|
||||
LoaderName = elem(e, "LoaderName"),
|
||||
FileHash = elem(e, "FileHash"),
|
||||
//References = e.Elements(ns("References")).Elements(ns("Reference")).Select(r => new DependencyReferenceDescriptor {
|
||||
// Name = elem(r, "Name"),
|
||||
// LoaderName = elem(r, "LoaderName"),
|
||||
// VirtualPath = elem(r, "VirtualPath")
|
||||
//})
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<DependencyDescriptor> FilterDependencies(IEnumerable<DependencyDescriptor> dependencies) {
|
||||
return dependencies.Where(dep => IsSupportedLoader(dep.LoaderName));
|
||||
}
|
||||
@@ -87,11 +129,12 @@ namespace Orchard.FileSystems.Dependencies {
|
||||
//Note: this is hard-coded for now, to avoid adding more responsibilities to the IExtensionLoader
|
||||
// implementations.
|
||||
return
|
||||
loaderName == "DynamicExtensionLoader" ||
|
||||
loaderName == "DynamicExtensionLoader" ||
|
||||
loaderName == "PrecompiledExtensionLoader";
|
||||
}
|
||||
|
||||
private void WriteDocument(string persistancePath, XDocument document) {
|
||||
_writeThroughToken.IsCurrent = false;
|
||||
using (var stream = _appDataFolder.CreateFile(persistancePath)) {
|
||||
document.Save(stream, SaveOptions.None);
|
||||
stream.Close();
|
||||
@@ -107,7 +150,7 @@ namespace Orchard.FileSystems.Dependencies {
|
||||
return XDocument.Load(stream);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
catch (Exception e) {
|
||||
Logger.Information(e, "Error reading file '{0}'", persistancePath);
|
||||
return new XDocument();
|
||||
}
|
||||
|
||||
@@ -14,25 +14,17 @@ namespace Orchard.FileSystems.Dependencies {
|
||||
/// served from the "~/Modules" or "~/Themes" directory.
|
||||
/// </summary>
|
||||
public class DynamicModuleVirtualPathProvider : VirtualPathProvider, ICustomVirtualPathProvider {
|
||||
private readonly IDependenciesFolder _dependenciesFolder;
|
||||
private readonly IExtensionDependenciesManager _extensionDependenciesManager;
|
||||
private readonly IEnumerable<IExtensionLoader> _loaders;
|
||||
|
||||
public DynamicModuleVirtualPathProvider(IDependenciesFolder dependenciesFolder, IEnumerable<IExtensionLoader> loaders) {
|
||||
_dependenciesFolder = dependenciesFolder;
|
||||
public DynamicModuleVirtualPathProvider(IExtensionDependenciesManager extensionDependenciesManager, IEnumerable<IExtensionLoader> loaders) {
|
||||
_extensionDependenciesManager = extensionDependenciesManager;
|
||||
_loaders = loaders;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public override bool DirectoryExists(string virtualDir) {
|
||||
return Previous.DirectoryExists(virtualDir);
|
||||
}
|
||||
|
||||
public override bool FileExists(string virtualPath) {
|
||||
return Previous.FileExists(virtualPath);
|
||||
}
|
||||
|
||||
public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies) {
|
||||
var result = GetFileHashWorker(virtualPath, virtualPathDependencies);
|
||||
Logger.Debug("GetFileHash(\"{0}\"): {1}", virtualPath, result);
|
||||
@@ -42,48 +34,29 @@ namespace Orchard.FileSystems.Dependencies {
|
||||
private string GetFileHashWorker(string virtualPath, IEnumerable virtualPathDependencies) {
|
||||
virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
|
||||
|
||||
var desc = GetDependencyDescriptor(virtualPath);
|
||||
var desc = GetExtensionDescriptor(virtualPath);
|
||||
if (desc != null) {
|
||||
// We are only interested in ".csproj" files loaded from "DynamicExtensionLoader"
|
||||
var dynamicExtensionLoader = _loaders.Where(l => l.Name == desc.LoaderName).FirstOrDefault() as DynamicExtensionLoader;
|
||||
if (dynamicExtensionLoader != null) {
|
||||
|
||||
if (virtualPath.Equals(desc.VirtualPath, StringComparison.OrdinalIgnoreCase)) {
|
||||
|
||||
var otherDependencies = dynamicExtensionLoader.GetFileHashDependencies(desc);
|
||||
if (otherDependencies.Any()) {
|
||||
|
||||
var allDependencies = virtualPathDependencies.OfType<string>().Concat(otherDependencies).ToList();
|
||||
|
||||
if (Logger.IsEnabled(LogLevel.Debug)) {
|
||||
Logger.Debug("GetFileHash(\"{0}\") - virtual path dependencies:", virtualPath);
|
||||
foreach (var dependency in allDependencies) {
|
||||
Logger.Debug(" Dependency: \"{0}\"", dependency);
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetFileHash(virtualPath, allDependencies);
|
||||
}
|
||||
return desc.FileHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
return base.GetFileHash(virtualPath, virtualPathDependencies);
|
||||
}
|
||||
|
||||
public override VirtualFile GetFile(string virtualPath) {
|
||||
return Previous.GetFile(virtualPath);
|
||||
}
|
||||
|
||||
private DependencyDescriptor GetDependencyDescriptor(string virtualPath) {
|
||||
private ActivatedExtensionDescriptor GetExtensionDescriptor(string virtualPath) {
|
||||
var prefix = PrefixMatch(virtualPath, DynamicExtensionLoader.ExtensionsVirtualPathPrefixes);
|
||||
if (prefix == null)
|
||||
return null;
|
||||
|
||||
var moduleName = ModuleMatch(virtualPath, prefix);
|
||||
if (moduleName == null)
|
||||
var moduleId = ModuleMatch(virtualPath, prefix);
|
||||
if (moduleId == null)
|
||||
return null;
|
||||
|
||||
return _dependenciesFolder.GetDescriptor(moduleName);
|
||||
return _extensionDependenciesManager.GetDescriptor(moduleId);
|
||||
}
|
||||
|
||||
private static string ModuleMatch(string virtualPath, string prefix) {
|
||||
@@ -91,8 +64,8 @@ namespace Orchard.FileSystems.Dependencies {
|
||||
if (index < 0)
|
||||
return null;
|
||||
|
||||
var moduleName = virtualPath.Substring(prefix.Length, index - prefix.Length);
|
||||
return (string.IsNullOrEmpty(moduleName) ? null : moduleName);
|
||||
var moduleId = virtualPath.Substring(prefix.Length, index - prefix.Length);
|
||||
return (string.IsNullOrEmpty(moduleId) ? null : moduleId);
|
||||
}
|
||||
|
||||
private static string PrefixMatch(string virtualPath, params string[] prefixes) {
|
||||
|
||||
@@ -3,8 +3,17 @@ using System.Collections.Generic;
|
||||
using Orchard.Caching;
|
||||
|
||||
namespace Orchard.FileSystems.Dependencies {
|
||||
public class ActivatedExtensionDescriptor {
|
||||
public string ExtensionId { get; set; }
|
||||
public string LoaderName { get; set; }
|
||||
public string VirtualPath { get; set; }
|
||||
public string FileHash { get; set; }
|
||||
}
|
||||
|
||||
public interface IExtensionDependenciesManager : IVolatileProvider {
|
||||
void StoreDependencies(IEnumerable<DependencyDescriptor> dependencyDescriptors, Func<string, string> fileHashProvider);
|
||||
|
||||
IEnumerable<string> GetVirtualPathDependencies(DependencyDescriptor descriptor);
|
||||
ActivatedExtensionDescriptor GetDescriptor(string extensionId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user