Simplify caching of NHibernate configuration

--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-07-15 14:59:17 -07:00
parent ea6d5849ec
commit 3982bbdb87
9 changed files with 115 additions and 48 deletions

View File

@@ -18,7 +18,6 @@ namespace Orchard.Tests.FileSystems.Dependencies {
builder.RegisterType<StubClock>().As<IClock>().SingleInstance();
builder.RegisterType<StubAppDataFolder>().As<IAppDataFolder>().SingleInstance();
builder.RegisterType<StubCacheManager>().As<ICacheManager>().SingleInstance();
builder.RegisterType<SessionConfigurationCache>().As<ISessionConfigurationCache>().SingleInstance();
builder.RegisterType<DefaultDependenciesFolder>().As<IDependenciesFolder>();
return builder.Build();
}

View File

@@ -1,8 +1,8 @@
using System;
using NHibernate.Cfg;
using Orchard.Environment.ShellBuilders.Models;
namespace Orchard.Data {
public interface ISessionConfigurationCache {
Configuration GetConfiguration(ShellBlueprint shellBlueprint);
public interface ISessionConfigurationCache : ISingletonDependency {
Configuration GetConfiguration(Func<Configuration> builder);
}
}

View File

@@ -1,50 +1,108 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using NHibernate.Cfg;
using Orchard.Environment.Configuration;
using Orchard.Environment.ShellBuilders.Models;
using Orchard.FileSystems.AppData;
using Orchard.Logging;
using Orchard.Utility;
namespace Orchard.Data {
public class SessionConfigurationCache : ISessionConfigurationCache {
private readonly ShellSettings _shellSettings;
private readonly ShellBlueprint _shellBlueprint;
private readonly IAppDataFolder _appDataFolder;
public SessionConfigurationCache(IAppDataFolder appDataFolder) {
public SessionConfigurationCache(ShellSettings shellSettings, ShellBlueprint shellBlueprint, IAppDataFolder appDataFolder) {
_shellSettings = shellSettings;
_shellBlueprint = shellBlueprint;
_appDataFolder = appDataFolder;
Logger = NullLogger.Instance;
}
public void StoreConfiguration(string shellName, Configuration config) {
var pathName = GetPathName(shellName);
public ILogger Logger { get; set; }
public Configuration GetConfiguration(Func<Configuration> builder) {
var hash = ComputeHash(_shellBlueprint).Value;
// Return previous configuration if it exsists and has the same hash as
// the current blueprint.
var previousConfig = ReadConfiguration(_shellSettings.Name);
if (previousConfig != null) {
if (previousConfig.ShellName == _shellSettings.Name && previousConfig.Hash == hash) {
return previousConfig.Configuration;
}
}
// Create cache and persist it
var cache = new ConfigurationCache {
ShellName = _shellSettings.Name,
Hash = hash,
Configuration = builder()
};
StoreConfiguration(cache);
return cache.Configuration;
}
[Serializable]
public class ConfigurationCache {
public string ShellName { get; set; }
public string Hash { get; set; }
public Configuration Configuration { get; set; }
}
private void StoreConfiguration(ConfigurationCache cache) {
var pathName = GetPathName(cache.ShellName);
using (var stream = _appDataFolder.CreateFile(pathName)) {
new BinaryFormatter().Serialize(stream, config);
new BinaryFormatter().Serialize(stream, cache);
}
}
public void DeleteConfiguration(string shellName) {
var pathName = GetPathName(shellName);
_appDataFolder.DeleteFile(pathName);
}
public void DeleteAll() {
if (!_appDataFolder.DirectoryExists("Sites"))
return;
foreach (var shellName in _appDataFolder.ListDirectories("Sites"))
DeleteConfiguration(shellName);
}
public Configuration GetConfiguration(string shellName) {
private ConfigurationCache ReadConfiguration(string shellName) {
var pathName = GetPathName(shellName);
if (!_appDataFolder.FileExists(pathName)) {
if (!_appDataFolder.FileExists(pathName))
return null;
try {
using (var stream = _appDataFolder.OpenFile(pathName)) {
return new BinaryFormatter().Deserialize(stream) as ConfigurationCache;
}
}
catch (Exception e) {
for (var scan = e; scan != null; scan = scan.InnerException)
Logger.Warning("The cached NHibernate configuration cache can't be read: {0}", scan.Message);
return null;
}
}
using (var stream = _appDataFolder.OpenFile(pathName)) {
return new BinaryFormatter().Deserialize(stream) as Configuration;
private Hash ComputeHash(ShellBlueprint shellBlueprint) {
// We need to hash the assemnly names, record names and property names
var hash = new Hash();
foreach (var tableName in shellBlueprint.Records.Select(x => x.TableName)) {
hash.AddString(tableName);
}
foreach (var recordType in shellBlueprint.Records.Select(x => x.Type)) {
hash.AddTypeReference(recordType);
foreach (var property in recordType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public)) {
hash.AddString(property.Name);
hash.AddTypeReference(property.PropertyType);
}
}
return hash;
}
private string GetPathName(string shellName) {
return _appDataFolder.Combine("Sites", shellName, "mappings.bin");
}
}
}
}

View File

@@ -55,8 +55,8 @@ namespace Orchard.Data {
}
public Configuration GetConfiguration() {
lock ( this ) {
if ( _configuration == null ) {
lock (this) {
if (_configuration == null) {
_configuration = BuildConfiguration();
}
}
@@ -73,15 +73,10 @@ namespace Orchard.Data {
private Configuration BuildConfiguration() {
var parameters = GetSessionFactoryParameters();
var config = _sessionConfigurationCache.GetConfiguration(_shellSettings.Name);
if ( config == null ) {
config = _dataServicesProviderFactory
var config = _sessionConfigurationCache.GetConfiguration(() =>
_dataServicesProviderFactory
.CreateProvider(parameters)
.BuildConfiguration(parameters);
_sessionConfigurationCache.StoreConfiguration(_shellSettings.Name, config);
}
.BuildConfiguration(parameters));
return config;
}

View File

@@ -53,7 +53,6 @@ namespace Orchard.Environment {
builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>().SingleInstance();
{
builder.RegisterType<ShellSettingsManager>().As<IShellSettingsManager>().SingleInstance();
builder.RegisterType<SessionConfigurationCache>().As<ISessionConfigurationCache>().SingleInstance();
builder.RegisterType<ShellContextFactory>().As<IShellContextFactory>().SingleInstance();
{

View File

@@ -1,7 +1,5 @@
using System;
using System.Linq;
using Autofac;
using Orchard.Data;
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor;
using Orchard.Environment.Descriptor.Models;
@@ -36,17 +34,14 @@ namespace Orchard.Environment.ShellBuilders {
private readonly IShellDescriptorCache _shellDescriptorCache;
private readonly ICompositionStrategy _compositionStrategy;
private readonly IShellContainerFactory _shellContainerFactory;
private readonly ISessionConfigurationCache _sessionConfigurationCache;
public ShellContextFactory(
IShellDescriptorCache shellDescriptorCache,
ICompositionStrategy compositionStrategy,
IShellContainerFactory shellContainerFactory,
ISessionConfigurationCache sessionConfigurationCache) {
IShellContainerFactory shellContainerFactory) {
_shellDescriptorCache = shellDescriptorCache;
_compositionStrategy = compositionStrategy;
_shellContainerFactory = shellContainerFactory;
_sessionConfigurationCache = sessionConfigurationCache;
Logger = NullLogger.Instance;
}
@@ -74,7 +69,6 @@ namespace Orchard.Environment.ShellBuilders {
if (currentDescriptor != null && knownDescriptor.SerialNumber != currentDescriptor.SerialNumber) {
Logger.Information("Newer descriptor obtained. Rebuilding shell container.");
_sessionConfigurationCache.DeleteConfiguration(settings.Name);
_shellDescriptorCache.Store(settings.Name, currentDescriptor);
blueprint = _compositionStrategy.Compose(settings, currentDescriptor);
shellScope = _shellContainerFactory.CreateContainer(settings, blueprint);

View File

@@ -13,13 +13,11 @@ namespace Orchard.FileSystems.Dependencies {
private const string FileName = "dependencies.xml";
private readonly ICacheManager _cacheManager;
private readonly IAppDataFolder _appDataFolder;
private readonly ISessionConfigurationCache _sessionConfigurationCache;
private readonly InvalidationToken _writeThroughToken;
public DefaultDependenciesFolder(ICacheManager cacheManager, IAppDataFolder appDataFolder, ISessionConfigurationCache sessionConfigurationCache) {
public DefaultDependenciesFolder(ICacheManager cacheManager, IAppDataFolder appDataFolder) {
_cacheManager = cacheManager;
_appDataFolder = appDataFolder;
_sessionConfigurationCache = sessionConfigurationCache;
_writeThroughToken = new InvalidationToken();
T = NullLocalizer.Instance;
}
@@ -52,8 +50,8 @@ namespace Orchard.FileSystems.Dependencies {
public void StoreDescriptors(IEnumerable<DependencyDescriptor> dependencyDescriptors) {
var existingDescriptors = LoadDescriptors().OrderBy(d => d.Name);
var newDescriptors = dependencyDescriptors.OrderBy(d => d.Name);
if (!newDescriptors.SequenceEqual(existingDescriptors, new DependencyDescriptorComparer())) {
_sessionConfigurationCache.DeleteAll();
WriteDependencies(PersistencePath, dependencyDescriptors);
}
}

View File

@@ -360,6 +360,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="ContentManagement\DataMigrations\FrameworkDataMigration.cs" />
<Compile Include="Utility\Hash.cs" />
<Compile Include="Data\ISessionConfigurationCache.cs" />
<Compile Include="Data\Migration\Generator\ISchemaCommandGenerator.cs" />
<Compile Include="Data\Migration\Interpreters\AbstractDataMigrationInterpreter.cs" />

View File

@@ -0,0 +1,23 @@
using System;
namespace Orchard.Utility {
/// <summary>
/// Compute an (almost) unique hash value from various sources.
/// This allows computing hash keys that are easily storable
/// and comparable from heterogenous components.
/// </summary>
public class Hash {
private long _hash;
public string Value { get { return _hash.ToString(); } }
public void AddString(string value) {
_hash += value.GetHashCode();
}
public void AddTypeReference(Type type) {
AddString(type.AssemblyQualifiedName);
AddString(type.FullName);
}
}
}