mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
PERF: Monitor modules files asynchronously
Monitoring virtual dependency files for modules takes requires a lot of file I/O, and can be a significant bottleneck on heavily loaded disk sub-system (5-10 secs). Making this process asynchronous during startup decreases startup time by almost that amount of time. --HG-- branch : 1.x
This commit is contained in:
@@ -19,6 +19,7 @@ namespace Orchard.Environment {
|
|||||||
private readonly IRunningShellTable _runningShellTable;
|
private readonly IRunningShellTable _runningShellTable;
|
||||||
private readonly IProcessingEngine _processingEngine;
|
private readonly IProcessingEngine _processingEngine;
|
||||||
private readonly IExtensionLoaderCoordinator _extensionLoaderCoordinator;
|
private readonly IExtensionLoaderCoordinator _extensionLoaderCoordinator;
|
||||||
|
private readonly IExtensionMonitoringCoordinator _extensionMonitoringCoordinator;
|
||||||
private readonly ICacheManager _cacheManager;
|
private readonly ICacheManager _cacheManager;
|
||||||
private readonly object _syncLock = new object();
|
private readonly object _syncLock = new object();
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ namespace Orchard.Environment {
|
|||||||
IRunningShellTable runningShellTable,
|
IRunningShellTable runningShellTable,
|
||||||
IProcessingEngine processingEngine,
|
IProcessingEngine processingEngine,
|
||||||
IExtensionLoaderCoordinator extensionLoaderCoordinator,
|
IExtensionLoaderCoordinator extensionLoaderCoordinator,
|
||||||
|
IExtensionMonitoringCoordinator extensionMonitoringCoordinator,
|
||||||
ICacheManager cacheManager,
|
ICacheManager cacheManager,
|
||||||
IHostLocalRestart hostLocalRestart ) {
|
IHostLocalRestart hostLocalRestart ) {
|
||||||
_shellSettingsManager = shellSettingsManager;
|
_shellSettingsManager = shellSettingsManager;
|
||||||
@@ -37,6 +39,7 @@ namespace Orchard.Environment {
|
|||||||
_runningShellTable = runningShellTable;
|
_runningShellTable = runningShellTable;
|
||||||
_processingEngine = processingEngine;
|
_processingEngine = processingEngine;
|
||||||
_extensionLoaderCoordinator = extensionLoaderCoordinator;
|
_extensionLoaderCoordinator = extensionLoaderCoordinator;
|
||||||
|
_extensionMonitoringCoordinator = extensionMonitoringCoordinator;
|
||||||
_cacheManager = cacheManager;
|
_cacheManager = cacheManager;
|
||||||
_hostLocalRestart = hostLocalRestart;
|
_hostLocalRestart = hostLocalRestart;
|
||||||
|
|
||||||
@@ -151,7 +154,7 @@ namespace Orchard.Environment {
|
|||||||
// on disk, and we need to reload new/updated extensions.
|
// on disk, and we need to reload new/updated extensions.
|
||||||
_cacheManager.Get("OrchardHost_Extensions",
|
_cacheManager.Get("OrchardHost_Extensions",
|
||||||
ctx => {
|
ctx => {
|
||||||
_extensionLoaderCoordinator.MonitorExtensions(ctx.Monitor);
|
_extensionMonitoringCoordinator.MonitorExtensions(ctx.Monitor);
|
||||||
_hostLocalRestart.Monitor(ctx.Monitor);
|
_hostLocalRestart.Monitor(ctx.Monitor);
|
||||||
DisposeShellContext();
|
DisposeShellContext();
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ namespace Orchard.Environment.Extensions {
|
|||||||
if (duplicates.Count() > 0) {
|
if (duplicates.Count() > 0) {
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
sb.Append(T("There are multiple extensions with the same name installed in this instance of Orchard.\r\n"));
|
sb.Append(T("There are multiple extensions with the same name installed in this instance of Orchard.\r\n"));
|
||||||
foreach(var dup in duplicates) {
|
foreach (var dup in duplicates) {
|
||||||
sb.Append(T("Extension '{0}' has been found from the following locations: {1}.\r\n", dup.Key, string.Join(", ", dup.Select(e => e.Location + "/" + e.Id))));
|
sb.Append(T("Extension '{0}' has been found from the following locations: {1}.\r\n", dup.Key, string.Join(", ", dup.Select(e => e.Location + "/" + e.Id))));
|
||||||
}
|
}
|
||||||
sb.Append(T("This issue can be usually solved by removing or renaming the conflicting extension."));
|
sb.Append(T("This issue can be usually solved by removing or renaming the conflicting extension."));
|
||||||
@@ -300,21 +300,5 @@ namespace Orchard.Environment.Extensions {
|
|||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MonitorExtensions(Action<IVolatileToken> monitor) {
|
|
||||||
Logger.Information("Start monitoring extension files...");
|
|
||||||
// Monitor add/remove of any module/theme
|
|
||||||
monitor(_virtualPathMonitor.WhenPathChanges("~/Modules"));
|
|
||||||
monitor(_virtualPathMonitor.WhenPathChanges("~/Themes"));
|
|
||||||
|
|
||||||
// Give loaders a chance to monitor any additional changes
|
|
||||||
var extensions = _extensionManager.AvailableExtensions().Where(d => DefaultExtensionTypes.IsModule(d.ExtensionType) || DefaultExtensionTypes.IsTheme(d.ExtensionType)).ToList();
|
|
||||||
foreach (var extension in extensions) {
|
|
||||||
foreach (var loader in _loaders) {
|
|
||||||
loader.Monitor(extension, monitor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Logger.Information("Done monitoring extension files...");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using Orchard.Caching;
|
||||||
|
using Orchard.Environment.Extensions.Loaders;
|
||||||
|
using Orchard.Environment.Extensions.Models;
|
||||||
|
using Orchard.FileSystems.VirtualPath;
|
||||||
|
using Orchard.Logging;
|
||||||
|
|
||||||
|
namespace Orchard.Environment.Extensions {
|
||||||
|
public class ExtensionMonitoringCoordinator : IExtensionMonitoringCoordinator {
|
||||||
|
private readonly IVirtualPathMonitor _virtualPathMonitor;
|
||||||
|
private readonly IExtensionManager _extensionManager;
|
||||||
|
private readonly IEnumerable<IExtensionLoader> _loaders;
|
||||||
|
|
||||||
|
public ExtensionMonitoringCoordinator(IVirtualPathMonitor virtualPathMonitor,
|
||||||
|
IExtensionManager extensionManager,
|
||||||
|
IEnumerable<IExtensionLoader> loaders) {
|
||||||
|
|
||||||
|
_virtualPathMonitor = virtualPathMonitor;
|
||||||
|
_extensionManager = extensionManager;
|
||||||
|
_loaders = loaders;
|
||||||
|
|
||||||
|
Logger = NullLogger.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
|
||||||
|
public void MonitorExtensions(Action<IVolatileToken> monitor) {
|
||||||
|
// We may be disabled by custom host configuration for performance reasons
|
||||||
|
if (Disabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//PREF: Monitor extensions asynchronously. IsCurrent will be 'true'
|
||||||
|
// until all tokens are collected by the work function.
|
||||||
|
var token = new AsyncVolativeToken(MonitorExtensionsWork, Logger);
|
||||||
|
monitor(token);
|
||||||
|
token.QueueWorkItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MonitorExtensionsWork(Action<IVolatileToken> monitor) {
|
||||||
|
Logger.Information("Start monitoring extension files...");
|
||||||
|
// Monitor add/remove of any module/theme
|
||||||
|
monitor(_virtualPathMonitor.WhenPathChanges("~/Modules"));
|
||||||
|
monitor(_virtualPathMonitor.WhenPathChanges("~/Themes"));
|
||||||
|
|
||||||
|
// Give loaders a chance to monitor any additional changes
|
||||||
|
var extensions = _extensionManager.AvailableExtensions().Where(d => DefaultExtensionTypes.IsModule(d.ExtensionType) || DefaultExtensionTypes.IsTheme(d.ExtensionType)).ToList();
|
||||||
|
foreach (var extension in extensions) {
|
||||||
|
foreach (var loader in _loaders) {
|
||||||
|
loader.Monitor(extension, monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger.Information("Done monitoring extension files...");
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AsyncVolativeToken : IVolatileToken {
|
||||||
|
private readonly Action<Action<IVolatileToken>> _task;
|
||||||
|
private readonly List<IVolatileToken> _taskTokens = new List<IVolatileToken>();
|
||||||
|
private volatile Exception _taskException;
|
||||||
|
private volatile bool _isTaskFinished;
|
||||||
|
|
||||||
|
public AsyncVolativeToken(Action<Action<IVolatileToken>> task, ILogger logger) {
|
||||||
|
_task = task;
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
|
|
||||||
|
public void QueueWorkItem() {
|
||||||
|
// Start a work item to collect tokens in our internal array
|
||||||
|
ThreadPool.QueueUserWorkItem((state) => {
|
||||||
|
try {
|
||||||
|
_task(token => _taskTokens.Add(token));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Logger.Error(e, "Error while monitoring extension files. Assuming extensions are not current.");
|
||||||
|
_taskException = e;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
_isTaskFinished = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCurrent {
|
||||||
|
get {
|
||||||
|
// We are current until the task has finished
|
||||||
|
if (_taskException != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (_isTaskFinished) {
|
||||||
|
return _taskTokens.All(t => t.IsCurrent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
using System;
|
namespace Orchard.Environment.Extensions {
|
||||||
using Orchard.Caching;
|
|
||||||
|
|
||||||
namespace Orchard.Environment.Extensions {
|
|
||||||
public interface IExtensionLoaderCoordinator {
|
public interface IExtensionLoaderCoordinator {
|
||||||
void SetupExtensions();
|
void SetupExtensions();
|
||||||
void MonitorExtensions(Action<IVolatileToken> monitor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
using Orchard.Caching;
|
||||||
|
|
||||||
|
namespace Orchard.Environment.Extensions {
|
||||||
|
public interface IExtensionMonitoringCoordinator {
|
||||||
|
void MonitorExtensions(Action<IVolatileToken> monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,7 @@ namespace Orchard.Environment {
|
|||||||
{
|
{
|
||||||
builder.RegisterType<ShellContainerRegistrations>().As<IShellContainerRegistrations>().SingleInstance();
|
builder.RegisterType<ShellContainerRegistrations>().As<IShellContainerRegistrations>().SingleInstance();
|
||||||
builder.RegisterType<ExtensionLoaderCoordinator>().As<IExtensionLoaderCoordinator>().SingleInstance();
|
builder.RegisterType<ExtensionLoaderCoordinator>().As<IExtensionLoaderCoordinator>().SingleInstance();
|
||||||
|
builder.RegisterType<ExtensionMonitoringCoordinator>().As<IExtensionMonitoringCoordinator>().SingleInstance();
|
||||||
builder.RegisterType<ExtensionManager>().As<IExtensionManager>().SingleInstance();
|
builder.RegisterType<ExtensionManager>().As<IExtensionManager>().SingleInstance();
|
||||||
{
|
{
|
||||||
builder.RegisterType<ModuleFolders>().As<IExtensionFolders>().SingleInstance()
|
builder.RegisterType<ModuleFolders>().As<IExtensionFolders>().SingleInstance()
|
||||||
|
|||||||
@@ -175,6 +175,8 @@
|
|||||||
<Compile Include="DisplayManagement\Shapes\ShapeDebugView.cs" />
|
<Compile Include="DisplayManagement\Shapes\ShapeDebugView.cs" />
|
||||||
<Compile Include="DisplayManagement\Shapes\ITagBuilderFactory.cs" />
|
<Compile Include="DisplayManagement\Shapes\ITagBuilderFactory.cs" />
|
||||||
<Compile Include="Environment\CollectionOrderModule.cs" />
|
<Compile Include="Environment\CollectionOrderModule.cs" />
|
||||||
|
<Compile Include="Environment\Extensions\ExtensionMonitoringCoordinator.cs" />
|
||||||
|
<Compile Include="Environment\Extensions\IExtensionMonitoringCoordinator.cs" />
|
||||||
<Compile Include="Environment\Extensions\OrchardSuppressDependencyAttribute.cs" />
|
<Compile Include="Environment\Extensions\OrchardSuppressDependencyAttribute.cs" />
|
||||||
<Compile Include="Environment\Features\IFeatureManager.cs" />
|
<Compile Include="Environment\Features\IFeatureManager.cs" />
|
||||||
<Compile Include="Environment\IAssemblyNameResolver.cs" />
|
<Compile Include="Environment\IAssemblyNameResolver.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user