Updated the build manager to find instances based on persisted cache of previous lookups but uses BuildManager to do so, after

some benchmark unit tests against loading a type from an assembly this is about 100% faster. Updated the UmbracoModule to write more diagnostics
for benchmark debugging. Have basically reduced the startup time to approx 50% of what it used to be based on the current
benchmarks run. Previously app startup on my machine was about 5 seconds, now after the first startup it is about 2.5 seconds.
This commit is contained in:
Shannon Deminick
2012-11-12 08:10:12 +05:00
parent 083bab0528
commit 156f145c69
11 changed files with 183 additions and 36 deletions

View File

@@ -7,6 +7,8 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Web.Caching;
using System.Web.Compilation;
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
@@ -33,8 +35,29 @@ namespace Umbraco.Core
/// </remarks>
internal class PluginManager
{
private readonly ApplicationContext _appContext;
internal PluginManager()
/// <summary>
/// Creates a new PluginManager with an ApplicationContext instance which ensures that the plugin xml
/// file is cached temporarily until app startup completes.
/// </summary>
/// <param name="appContext"></param>
/// <param name="detectBinChanges"></param>
internal PluginManager(ApplicationContext appContext, bool detectBinChanges = true)
: this(detectBinChanges)
{
if (appContext == null) throw new ArgumentNullException("appContext");
_appContext = appContext;
}
/// <summary>
/// Creates a new PluginManager
/// </summary>
/// <param name="detectBinChanges">
/// If true will detect changes in the /bin folder and therefor load plugins from the
/// cached plugins file if one is found. If false will never use the cache file for plugins
/// </param>
internal PluginManager(bool detectBinChanges = true)
{
_tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
//create the folder if it doesn't exist
@@ -42,13 +65,24 @@ namespace Umbraco.Core
{
Directory.CreateDirectory(_tempFolder);
}
//do the check if they've changed
HaveAssembliesChanged = CachedAssembliesHash != CurrentAssembliesHash;
//if they have changed, we need to write the new file
if (HaveAssembliesChanged)
if (detectBinChanges)
{
WriteCachePluginsHash();
//first check if the cached hash is 0, if it is then we ne
//do the check if they've changed
HaveAssembliesChanged = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0;
//if they have changed, we need to write the new file
if (HaveAssembliesChanged)
{
WriteCachePluginsHash();
}
}
else
{
//always set to true if we're not detecting (generally only for testing)
HaveAssembliesChanged = true;
}
}
static PluginManager _resolver;
@@ -72,7 +106,9 @@ namespace Umbraco.Core
if (_resolver == null)
{
l.UpgradeToWriteLock();
_resolver = new PluginManager();
_resolver = ApplicationContext.Current == null
? new PluginManager()
: new PluginManager(ApplicationContext.Current);
}
return _resolver;
}
@@ -180,37 +216,49 @@ namespace Umbraco.Core
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
internal Attempt<IEnumerable<Tuple<string, string>>> TryGetCachedPluginsFromFile<T>()
internal Attempt<IEnumerable<string>> TryGetCachedPluginsFromFile<T>()
{
var filePath = Path.Combine(_tempFolder, "umbraco-plugins.list");
if (!File.Exists(filePath))
return Attempt<IEnumerable<Tuple<string, string>>>.False;
return Attempt<IEnumerable<string>>.False;
try
{
var xml = XDocument.Load(filePath);
//we will load the xml document, if the app context exist, we will load it from the cache (which is only around for 5 minutes)
//while the app boots up, this should save some IO time on app startup when the app context is there (which is always unless in unit tests)
XDocument xml;
if (_appContext != null)
{
xml = _appContext.ApplicationCache.GetCacheItem("umbraco-plugins.list",
new TimeSpan(0, 0, 5, 0),
() => XDocument.Load(filePath));
}
else
{
xml = XDocument.Load(filePath);
}
if (xml.Root == null)
return Attempt<IEnumerable<Tuple<string, string>>>.False;
return Attempt<IEnumerable<string>>.False;
var typeElement = xml.Root.Elements()
.SingleOrDefault(x =>
x.Name.LocalName == "baseType"
&& ((string) x.Attribute("type")) == typeof (T).FullName);
if (typeElement == null)
return Attempt<IEnumerable<Tuple<string, string>>>.False;
return Attempt<IEnumerable<string>>.False;
//return success
return new Attempt<IEnumerable<Tuple<string, string>>>(
return new Attempt<IEnumerable<string>>(
true,
typeElement.Elements("add")
.Select(x => new Tuple<string, string>(
(string) x.Attribute("type"),
(string) x.Attribute("assembly"))));
.Select(x => (string) x.Attribute("type")));
}
catch (Exception)
{
//if the file is corrupted, etc... return false
return Attempt<IEnumerable<Tuple<string, string>>>.False;
return Attempt<IEnumerable<string>>.False;
}
}
@@ -268,10 +316,9 @@ namespace Umbraco.Core
//now we have the type element, we need to clear any previous types as children and add/update it with new ones
typeElement.ReplaceNodes(typesFound
.Select(x =>
new XElement("add",
new XAttribute("type", x.FullName),
new XAttribute("assembly", x.Assembly.FullName))));
.Select(x =>
new XElement("add",
new XAttribute("type", x.AssemblyQualifiedName))));
//save the xml file
xml.Save(filePath);
}
@@ -444,15 +491,17 @@ namespace Umbraco.Core
{
try
{
var type = Assembly.Load(t.Item2).GetType(t.Item1);
typeList.AddType(type);
//we use the build manager to ensure we get all types loaded, this is slightly slower than
//Type.GetType but if the types in the assembly aren't loaded yet then we have problems with that.
var type = BuildManager.GetType(t, true);
typeList.AddType(type);
}
catch (Exception ex)
{
//if there are any exceptions loading types, we have to exist, this should never happen so
//we will need to revert to scanning for types.
successfullyLoadedFromCache = false;
LogHelper.Error<PluginManager>("Could not load a cached plugin type: " + t.Item1 + " in assembly: " + t.Item2 + " now reverting to re-scanning assemblies for the base type: " + typeof (T).FullName, ex);
LogHelper.Error<PluginManager>("Could not load a cached plugin type: " + t + " now reverting to re-scanning assemblies for the base type: " + typeof (T).FullName, ex);
break;
}
}
@@ -461,6 +510,10 @@ namespace Umbraco.Core
//we need to manually load by scanning if loading from the file was not successful.
LoadViaScanningAndUpdateCacheFile<T>(typeList, finder);
}
else
{
LogHelper.Debug<PluginManager>("Loaded plugin types " + typeof(T).FullName + " from persisted cache");
}
}
}
else