From 225698d2a78dd74ff038cd0fb7496b466fe45e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 8 Dec 2017 21:20:55 +0100 Subject: [PATCH 01/10] Fix cache build for honoring mapped concurrency --- ...ryCacheFixture.cs => BuildCacheFixture.cs} | 12 +- ...ryCacheFixture.cs => BuildCacheFixture.cs} | 67 +++++++- .../CacheTest/EntitiesInSameRegion.cs | 20 +++ .../CacheTest/EntitiesInSameRegion.hbm.xml | 30 ++++ .../Async/Impl/SessionFactoryImpl.cs | 2 - src/NHibernate/Cache/CacheFactory.cs | 57 +++++-- src/NHibernate/Cache/IQueryCacheFactory.cs | 59 ++++++- src/NHibernate/Cache/StandardQueryCache.cs | 47 ++++-- .../Cache/StandardQueryCacheFactory.cs | 20 ++- src/NHibernate/Cache/UpdateTimestampsCache.cs | 29 +++- src/NHibernate/Impl/SessionFactoryImpl.cs | 146 ++++++++++++------ 11 files changed, 401 insertions(+), 88 deletions(-) rename src/NHibernate.Test/Async/CacheTest/{GetQueryCacheFixture.cs => BuildCacheFixture.cs} (87%) rename src/NHibernate.Test/CacheTest/{GetQueryCacheFixture.cs => BuildCacheFixture.cs} (50%) create mode 100644 src/NHibernate.Test/CacheTest/EntitiesInSameRegion.cs create mode 100644 src/NHibernate.Test/CacheTest/EntitiesInSameRegion.hbm.xml diff --git a/src/NHibernate.Test/Async/CacheTest/GetQueryCacheFixture.cs b/src/NHibernate.Test/Async/CacheTest/BuildCacheFixture.cs similarity index 87% rename from src/NHibernate.Test/Async/CacheTest/GetQueryCacheFixture.cs rename to src/NHibernate.Test/Async/CacheTest/BuildCacheFixture.cs index e53b9fc4d7b..d944b825c81 100644 --- a/src/NHibernate.Test/Async/CacheTest/GetQueryCacheFixture.cs +++ b/src/NHibernate.Test/Async/CacheTest/BuildCacheFixture.cs @@ -9,12 +9,13 @@ using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using NHibernate.Cache; using NHibernate.Cfg; +using NHibernate.Engine; +using NHibernate.Util; using NUnit.Framework; using Environment = NHibernate.Cfg.Environment; @@ -22,9 +23,14 @@ namespace NHibernate.Test.CacheTest { using System.Threading.Tasks; [TestFixture] - public class GetQueryCacheFixtureAsync : TestCase + public class BuildCacheFixtureAsync : TestCase { - protected override string[] Mappings => new[] { "Simple.hbm.xml" }; + protected override string MappingsAssembly => "NHibernate.Test"; + + protected override string[] Mappings => new[] { "CacheTest.EntitiesInSameRegion.hbm.xml" }; + + // Disable the TestCase cache overrides. + protected override string CacheConcurrencyStrategy => null; protected override void Configure(Configuration configuration) { diff --git a/src/NHibernate.Test/CacheTest/GetQueryCacheFixture.cs b/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs similarity index 50% rename from src/NHibernate.Test/CacheTest/GetQueryCacheFixture.cs rename to src/NHibernate.Test/CacheTest/BuildCacheFixture.cs index 73120bdf537..c936a122664 100644 --- a/src/NHibernate.Test/CacheTest/GetQueryCacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs @@ -1,19 +1,25 @@ using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using NHibernate.Cache; using NHibernate.Cfg; +using NHibernate.Engine; +using NHibernate.Util; using NUnit.Framework; using Environment = NHibernate.Cfg.Environment; namespace NHibernate.Test.CacheTest { [TestFixture] - public class GetQueryCacheFixture : TestCase + public class BuildCacheFixture : TestCase { - protected override string[] Mappings => new[] { "Simple.hbm.xml" }; + protected override string MappingsAssembly => "NHibernate.Test"; + + protected override string[] Mappings => new[] { "CacheTest.EntitiesInSameRegion.hbm.xml" }; + + // Disable the TestCase cache overrides. + protected override string CacheConcurrencyStrategy => null; protected override void Configure(Configuration configuration) { @@ -21,6 +27,61 @@ protected override void Configure(Configuration configuration) configuration.SetProperty(Environment.CacheProvider, typeof(LockedCacheProvider).AssemblyQualifiedName); } + [Theory] + public void CommonRegionHasExpectedConcurrency(bool withPrefix) + { + const string prefix = "Prefix"; + const string region = "Common"; + var fullRegion = (withPrefix ? prefix + "." : "") + region; + ISessionFactoryImplementor sfi = null; + if (withPrefix) + cfg.SetProperty(Environment.CacheRegionPrefix, prefix); + try + { + sfi = withPrefix ? BuildSessionFactory() : Sfi; + var commonRegionCache = sfi.GetSecondLevelCacheRegion(fullRegion); + var entityAName = typeof(EntityA).FullName; + var entityAConcurrencyCache = sfi.GetEntityPersister(entityAName).Cache; + var entityACache = entityAConcurrencyCache.Cache; + var entityBName = typeof(EntityB).FullName; + var entityBConcurrencyCache = sfi.GetEntityPersister(entityBName).Cache; + var entityBCache = entityBConcurrencyCache.Cache; + var relatedAConcurrencyCache = + sfi.GetCollectionPersister(StringHelper.Qualify(entityAName, nameof(EntityA.Related))).Cache; + var relatedACache = relatedAConcurrencyCache.Cache; + var relatedBConcurrencyCache = + sfi.GetCollectionPersister(StringHelper.Qualify(entityBName, nameof(EntityB.Related))).Cache; + var relatedBCache = relatedBConcurrencyCache.Cache; + var queryCache = sfi.GetQueryCache(region).Cache; + Assert.Multiple( + () => + { + Assert.That(commonRegionCache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for common region"); + Assert.That(entityACache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for EntityA"); + Assert.That(entityBCache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for EntityB"); + Assert.That(relatedACache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for RelatedA"); + Assert.That(relatedBCache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for RelatedB"); + Assert.That(queryCache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for query cache"); + }); + Assert.Multiple( + () => + { + Assert.That(entityAConcurrencyCache, Is.InstanceOf(), "Unexpected concurrency for EntityA"); + Assert.That(relatedAConcurrencyCache, Is.InstanceOf(), "Unexpected concurrency for RelatedA"); + Assert.That(entityBConcurrencyCache, Is.InstanceOf(), "Unexpected concurrency for EntityB"); + Assert.That(relatedBConcurrencyCache, Is.InstanceOf(), "Unexpected concurrency for RelatedB"); + }); + } + finally + { + if (withPrefix) + { + cfg.Properties.Remove(Environment.CacheRegionPrefix); + sfi?.Dispose(); + } + } + } + [Test] public void RetrievedQueryCacheMatchesGloballyStoredOne() { diff --git a/src/NHibernate.Test/CacheTest/EntitiesInSameRegion.cs b/src/NHibernate.Test/CacheTest/EntitiesInSameRegion.cs new file mode 100644 index 00000000000..3610ede845a --- /dev/null +++ b/src/NHibernate.Test/CacheTest/EntitiesInSameRegion.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace NHibernate.Test.CacheTest +{ + public class EntityInSameRegion + { + public virtual int Id { get; set; } + public virtual string Description { get; set; } + public virtual int Value { get; set; } + public virtual ISet Related { get; set; } + } + + public class EntityA : EntityInSameRegion + { + } + + public class EntityB : EntityInSameRegion + { + } +} diff --git a/src/NHibernate.Test/CacheTest/EntitiesInSameRegion.hbm.xml b/src/NHibernate.Test/CacheTest/EntitiesInSameRegion.hbm.xml new file mode 100644 index 00000000000..67c760f6ef4 --- /dev/null +++ b/src/NHibernate.Test/CacheTest/EntitiesInSameRegion.hbm.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + from EntityA + + diff --git a/src/NHibernate/Async/Impl/SessionFactoryImpl.cs b/src/NHibernate/Async/Impl/SessionFactoryImpl.cs index 8dcb9b347da..4ab92b975fe 100644 --- a/src/NHibernate/Async/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Async/Impl/SessionFactoryImpl.cs @@ -85,8 +85,6 @@ public sealed partial class SessionFactoryImpl : ISessionFactoryImplementor, IOb if (settings.IsQueryCacheEnabled) { - queryCache.Destroy(); - foreach (var cache in queryCaches.Values) { cache.Value.Destroy(); diff --git a/src/NHibernate/Cache/CacheFactory.cs b/src/NHibernate/Cache/CacheFactory.cs index 8a7d3b08641..c4ebcb7997a 100644 --- a/src/NHibernate/Cache/CacheFactory.cs +++ b/src/NHibernate/Cache/CacheFactory.cs @@ -1,4 +1,4 @@ - +using System; using NHibernate.Cfg; using System.Collections.Generic; @@ -30,13 +30,44 @@ public static class CacheFactory /// Used to retrieve the global cache region prefix. /// Properties the cache provider can use to configure the cache. /// An to use for this object in the . - public static ICacheConcurrencyStrategy CreateCache(string usage, string name, bool mutable, Settings settings, - IDictionary properties) + // Since v5.2 + [Obsolete("Please use overload with a CacheBase builder parameter.")] + public static ICacheConcurrencyStrategy CreateCache( + string usage, + string name, + bool mutable, + Settings settings, + IDictionary properties) { - if (usage == null || !settings.IsSecondLevelCacheEnabled) return null; //no cache + var cache = CreateCache( + usage, name, settings, + (r, u) => + { + var c = settings.CacheProvider.BuildCache(r, properties); + return c as CacheBase ?? new ObsoleteCacheWrapper(c); + }); - string prefix = settings.CacheRegionPrefix; - if (prefix != null) name = prefix + '.' + name; + if (cache != null && mutable && usage == ReadOnly) + log.Warn("read-only cache configured for mutable: {0}", name); + + return cache; + } + + /// + /// Creates an from the parameters. + /// + /// The name of the strategy that should use for the class. + /// The name of the cache region the strategy is being created for. + /// Used to retrieve the global cache region prefix. + /// The delegate for obtaining the to use for the region. + /// An to use for this object in the . + public static ICacheConcurrencyStrategy CreateCache( + string usage, + string name, + Settings settings, + Func regionAndUsageCacheGetter) + { + if (usage == null || !settings.IsSecondLevelCacheEnabled) return null; //no cache if (log.IsDebugEnabled()) { @@ -47,10 +78,6 @@ public static ICacheConcurrencyStrategy CreateCache(string usage, string name, b switch (usage) { case ReadOnly: - if (mutable) - { - log.Warn("read-only cache configured for mutable: {0}", name); - } ccs = new ReadOnlyCache(); break; case ReadWrite: @@ -59,17 +86,17 @@ public static ICacheConcurrencyStrategy CreateCache(string usage, string name, b case NonstrictReadWrite: ccs = new NonstrictReadWriteCache(); break; - //case CacheFactory.Transactional: - // ccs = new TransactionalCache(); - // break; + //case CacheFactory.Transactional: + // ccs = new TransactionalCache(); + // break; default: throw new MappingException( - "cache usage attribute should be read-write, read-only, nonstrict-read-write, or transactional"); + "cache usage attribute should be read-write, read-only or nonstrict-read-write"); } try { - ccs.Cache = settings.CacheProvider.BuildCache(name, properties); + ccs.Cache = regionAndUsageCacheGetter(name, usage); } catch (CacheException e) { diff --git a/src/NHibernate/Cache/IQueryCacheFactory.cs b/src/NHibernate/Cache/IQueryCacheFactory.cs index 1b735e4899a..6a615f8103a 100644 --- a/src/NHibernate/Cache/IQueryCacheFactory.cs +++ b/src/NHibernate/Cache/IQueryCacheFactory.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NHibernate.Cfg; @@ -9,7 +10,59 @@ namespace NHibernate.Cache /// public interface IQueryCacheFactory { - IQueryCache GetQueryCache(string regionName, UpdateTimestampsCache updateTimestampsCache, Settings settings, - IDictionary props); + // Since v5.2 + [Obsolete("Please use extension overload with a CacheBase parameter.")] + IQueryCache GetQueryCache( + string regionName, + UpdateTimestampsCache updateTimestampsCache, + Settings settings, + IDictionary props); } -} \ No newline at end of file + + // 6.0 TODO: move to interface. + public static class QueryCacheFactoryExtension + { + private static readonly INHibernateLogger Logger = NHibernateLogger.For(typeof(QueryCacheFactoryExtension)); + + /// + /// Build a query cache. + /// + /// The query cache factory. + /// The cache of updates timestamps. + /// The NHibernate settings properties. + /// The to use for the region. + /// A query cache. null if does not implement a + /// public IQueryCache GetQueryCache(UpdateTimestampsCache, IDictionary<string, string> props, CacheBase) + /// method. + public static IQueryCache GetQueryCache( + this IQueryCacheFactory factory, + UpdateTimestampsCache updateTimestampsCache, + IDictionary props, + CacheBase regionCache) + { + if (factory is StandardQueryCacheFactory standardFactory) + { + return standardFactory.GetQueryCache(updateTimestampsCache, props, regionCache); + } + + // Use reflection for supporting custom factories. + var factoryType = factory.GetType(); + var getQueryCacheMethod = factoryType.GetMethod( + nameof(StandardQueryCacheFactory.GetQueryCache), + new[] { typeof(UpdateTimestampsCache), typeof(IDictionary), typeof(CacheBase) }); + if (getQueryCacheMethod != null) + { + return (IQueryCache) getQueryCacheMethod.Invoke( + factory, + new object[] { updateTimestampsCache, props, regionCache }); + } + + // Caller has to call the obsolete method. + Logger.Warn( + "{0} does not implement 'IQueryCache GetQueryCache(UpdateTimestampsCache, IDictionary<string, " + + "string> props, CacheBase)', its obsolete overload will be used.", + factoryType); + return null; + } + } +} diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index c419995ae29..47d9cc0773b 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -23,22 +23,49 @@ public partial class StandardQueryCache : IQueryCache, IBatchableQueryCache // 6.0 TODO: remove private readonly CacheBase _cache; - public StandardQueryCache(Settings settings, IDictionary props, UpdateTimestampsCache updateTimestampsCache, string regionName) + // Since v5.2 + [Obsolete("Please use overload with an ICache parameter.")] + public StandardQueryCache( + Settings settings, + IDictionary props, + UpdateTimestampsCache updateTimestampsCache, + string regionName) + : this( + updateTimestampsCache, + settings.CacheProvider.BuildCache( + (!string.IsNullOrEmpty(settings.CacheRegionPrefix) ? settings.CacheRegionPrefix + '.' : "") + + (regionName ?? typeof (StandardQueryCache).FullName), + props)) { - if (regionName == null) - regionName = typeof(StandardQueryCache).FullName; + } - var prefix = settings.CacheRegionPrefix; - if (!string.IsNullOrEmpty(prefix)) - regionName = prefix + '.' + regionName; + // Since v5.2 + [Obsolete] + private StandardQueryCache( + UpdateTimestampsCache updateTimestampsCache, + ICache cache) + : this(updateTimestampsCache, cache as CacheBase ?? new ObsoleteCacheWrapper(cache)) + { + } - Log.Info("starting query cache at region: {0}", regionName); + /// + /// Build a query cache. + /// + /// The cache of updates timestamps. + /// The to use for the region. + public StandardQueryCache( + UpdateTimestampsCache updateTimestampsCache, + CacheBase regionCache) + { + if (regionCache == null) + throw new ArgumentNullException(nameof(regionCache)); - Cache = settings.CacheProvider.BuildCache(regionName, props); - _cache = Cache as CacheBase ?? new ObsoleteCacheWrapper(Cache); + _regionName = regionCache.RegionName; + Log.Info("starting query cache at region: {0}", _regionName); + Cache = regionCache; + _cache = regionCache; _updateTimestampsCache = updateTimestampsCache; - _regionName = regionName; } #region IQueryCache Members diff --git a/src/NHibernate/Cache/StandardQueryCacheFactory.cs b/src/NHibernate/Cache/StandardQueryCacheFactory.cs index efb23d20441..6867b2ae21e 100644 --- a/src/NHibernate/Cache/StandardQueryCacheFactory.cs +++ b/src/NHibernate/Cache/StandardQueryCacheFactory.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NHibernate.Cfg; @@ -9,6 +10,8 @@ namespace NHibernate.Cache /// public class StandardQueryCacheFactory : IQueryCacheFactory { + // Since v5.2 + [Obsolete("Please use overload with an CacheBase parameter.")] public IQueryCache GetQueryCache(string regionName, UpdateTimestampsCache updateTimestampsCache, Settings settings, @@ -16,5 +19,20 @@ public IQueryCache GetQueryCache(string regionName, { return new StandardQueryCache(settings, props, updateTimestampsCache, regionName); } + + /// + /// Build a query cache. + /// + /// The cache of updates timestamps. + /// The NHibernate settings properties. + /// The to use for the region. + /// A query cache. + public virtual IQueryCache GetQueryCache( + UpdateTimestampsCache updateTimestampsCache, + IDictionary props, + CacheBase regionCache) + { + return new StandardQueryCache(updateTimestampsCache, regionCache); + } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Cache/UpdateTimestampsCache.cs index b6d462328e9..e1b0486f5a1 100644 --- a/src/NHibernate/Cache/UpdateTimestampsCache.cs +++ b/src/NHibernate/Cache/UpdateTimestampsCache.cs @@ -20,20 +20,35 @@ public partial class UpdateTimestampsCache private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(UpdateTimestampsCache)); private readonly CacheBase _updateTimestamps; - private readonly string regionName = typeof(UpdateTimestampsCache).Name; - public virtual void Clear() { _updateTimestamps.Clear(); } + // Since v5.2 + [Obsolete("Please use overload with an ICache parameter.")] public UpdateTimestampsCache(Settings settings, IDictionary props) + : this( + settings.CacheProvider.BuildCache( + (settings.CacheRegionPrefix == null ? "" : settings.CacheRegionPrefix + '.') + + typeof(UpdateTimestampsCache).Name, + props)) {} + + // Since v5.2 + [Obsolete] + private UpdateTimestampsCache(ICache cache) + : this(cache as CacheBase ?? new ObsoleteCacheWrapper(cache)) + { + } + + /// + /// Build the update timestamps cache. + /// x + /// The to use. + public UpdateTimestampsCache(CacheBase cache) { - var prefix = settings.CacheRegionPrefix; - regionName = prefix == null ? regionName : prefix + '.' + regionName; - log.Info("starting update timestamps cache at region: {0}", regionName); - var updateTimestamps = settings.CacheProvider.BuildCache(regionName, props); - _updateTimestamps = updateTimestamps as CacheBase ?? new ObsoleteCacheWrapper(updateTimestamps); + log.Info("starting update timestamps cache at region: {0}", cache.RegionName); + _updateTimestamps = cache; } //Since v5.1 diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index 4f4b4504423..1ed3077f2ec 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -97,8 +97,8 @@ public void HandleEntityNotFound(string entityName, object id) [NonSerialized] // 6.0 TODO: type as CacheBase instead #pragma warning disable 618 - private readonly ConcurrentDictionary allCacheRegions = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary> allCachePerRegionThenType = + new ConcurrentDictionary>(); #pragma warning restore 618 [NonSerialized] @@ -151,6 +151,8 @@ public void HandleEntityNotFound(string entityName, object id) [NonSerialized] private readonly IQueryCache queryCache; + private const string QueryCacheType = "QueryCache"; + [NonSerialized] private readonly ConcurrentDictionary> queryCaches; [NonSerialized] @@ -235,7 +237,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings #region Persisters - Dictionary caches = new Dictionary(); + var caches = new Dictionary, ICacheConcurrencyStrategy>(); entityPersisters = new Dictionary(); implementorToEntityName = new Dictionary(); @@ -244,22 +246,12 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings foreach (PersistentClass model in cfg.ClassMappings) { model.PrepareTemporaryTables(mapping, settings.Dialect); - string cacheRegion = model.RootClazz.CacheRegionName; - ICacheConcurrencyStrategy cache; - if (!caches.TryGetValue(cacheRegion, out cache)) - { - cache = - CacheFactory.CreateCache(model.CacheConcurrencyStrategy, cacheRegion, model.IsMutable, settings, properties); - if (cache != null) - { - caches.Add(cacheRegion, cache); - if (!allCacheRegions.TryAdd(cache.RegionName, cache.Cache)) - { - throw new HibernateException("An item with the same key has already been added to allCacheRegions."); - } - } - } - IEntityPersister cp = PersisterFactory.CreateClassPersister(model, cache, this, mapping); + var cache = GetCacheConcurrencyStrategy( + model.RootClazz.CacheRegionName, + model.CacheConcurrencyStrategy, + model.IsMutable, + caches); + var cp = PersisterFactory.CreateClassPersister(model, cache, this, mapping); entityPersisters[model.EntityName] = cp; classMeta[model.EntityName] = cp.ClassMetadata; @@ -274,14 +266,12 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings collectionPersisters = new Dictionary(); foreach (Mapping.Collection model in cfg.CollectionMappings) { - ICacheConcurrencyStrategy cache = - CacheFactory.CreateCache(model.CacheConcurrencyStrategy, model.CacheRegionName, model.Owner.IsMutable, settings, - properties); - if (cache != null) - { - allCacheRegions[cache.RegionName] = cache.Cache; - } - ICollectionPersister persister = PersisterFactory.CreateCollectionPersister(model, cache, this); + var cache = GetCacheConcurrencyStrategy( + model.CacheRegionName, + model.CacheConcurrencyStrategy, + model.Owner.IsMutable, + caches); + var persister = PersisterFactory.CreateCollectionPersister(model, cache, this); collectionPersisters[model.Role] = persister; IType indexType = persister.IndexType; if (indexType != null && indexType.IsAssociationType && !indexType.IsAnyType) @@ -382,9 +372,12 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings if (settings.IsQueryCacheEnabled) { - updateTimestampsCache = new UpdateTimestampsCache(settings, properties); - queryCache = settings.QueryCacheFactory.GetQueryCache(null, updateTimestampsCache, settings, properties); + var updateTimestampsCacheName = typeof(UpdateTimestampsCache).Name; + updateTimestampsCache = new UpdateTimestampsCache(BuildCache(updateTimestampsCacheName, updateTimestampsCacheName)); + var queryCacheName = typeof(StandardQueryCache).FullName; + queryCache = BuildQueryCache(queryCacheName); queryCaches = new ConcurrentDictionary>(); + queryCaches.TryAdd(queryCacheName, new Lazy(() => queryCache)); } else { @@ -421,6 +414,47 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings entityNotFoundDelegate = enfd; } + private IQueryCache BuildQueryCache(string queryCacheName) + { + return + settings.QueryCacheFactory.GetQueryCache( + updateTimestampsCache, + properties, + BuildCache(queryCacheName, QueryCacheType)) + // 6.0 TODO: remove the coalesce once IQueryCacheFactory todos are done +#pragma warning disable 618 + ?? settings.QueryCacheFactory.GetQueryCache( +#pragma warning restore 618 + queryCacheName, + updateTimestampsCache, + settings, + properties); + } + + private ICacheConcurrencyStrategy GetCacheConcurrencyStrategy( + string cacheRegion, + string strategy, + bool isMutable, + Dictionary, ICacheConcurrencyStrategy> caches) + { + var cacheKey = new Tuple(cacheRegion, strategy); + if (!caches.TryGetValue(cacheKey, out var cache)) + { + cache = CacheFactory.CreateCache( + strategy, + cacheRegion, + settings, + BuildCache); + if (cache != null) + caches.Add(cacheKey, cache); + } + + if (cache != null && isMutable && strategy == CacheFactory.ReadOnly) + log.Warn("read-only cache configured for mutable: {0}", name); + + return cache; + } + public EventListeners EventListeners { get { return eventListeners; } @@ -848,8 +882,6 @@ public void Close() if (settings.IsQueryCacheEnabled) { - queryCache.Destroy(); - foreach (var cache in queryCaches.Values) { cache.Value.Destroy(); @@ -1041,8 +1073,12 @@ public UpdateTimestampsCache UpdateTimestampsCache public IDictionary GetAllSecondLevelCacheRegions() #pragma warning restore 618 { - // ToArray creates a moment in time snapshot - return allCacheRegions.ToArray().ToDictionary(kv => kv.Key, kv => kv.Value); + return + allCachePerRegionThenType + // ToArray creates a moment in time snapshot + .ToArray() + // Caches are not unique per region, take the first one. + .ToDictionary(kv => kv.Key, kv => kv.Value.Values.First()); } // 6.0 TODO: return CacheBase instead @@ -1050,8 +1086,33 @@ public IDictionary GetAllSecondLevelCacheRegions() public ICache GetSecondLevelCacheRegion(string regionName) #pragma warning restore 618 { - allCacheRegions.TryGetValue(regionName, out var result); - return result; + if (!allCachePerRegionThenType.TryGetValue(regionName, out var result)) + return null; + // Caches are not unique per region, take the first one. + return result.Values.First(); + } + + private CacheBase BuildCache(string cacheRegion, string type) + { + // If run concurrently for the same region and type, this may built many caches for the same region and type. + // Currently only GetQueryCache may be run concurrently, and its implementation prevents + // concurrent creation call for the same region, so this will not happen. + // Otherwise the dictionary will have to be changed for using a lazy, see + // https://stackoverflow.com/a/31637510/1178314 + var prefix = settings.CacheRegionPrefix; + if (!string.IsNullOrEmpty(prefix)) + cacheRegion = prefix + '.' + cacheRegion; +#pragma warning disable 618 + var cachesPerType = allCachePerRegionThenType.GetOrAdd(cacheRegion, cr => new ConcurrentDictionary()); +#pragma warning restore 618 + var cache = settings.CacheProvider.BuildCache(cacheRegion, properties); + if (!cachesPerType.TryAdd(type, cache)) + { + cache.Destroy(); + throw new InvalidOperationException($"A cache has already been built for region {cacheRegion} and type {type}."); + } + + return cache as CacheBase ?? new ObsoleteCacheWrapper(cache); } /// Statistics SPI @@ -1079,15 +1140,12 @@ public IQueryCache GetQueryCache(string cacheRegion) // The factory may be run concurrently by threads trying to get the same region. // But the GetOrAdd will yield the same lazy for all threads, so only one will // initialize. https://stackoverflow.com/a/31637510/1178314 - return queryCaches.GetOrAdd( - cacheRegion, - cr => new Lazy( - () => - { - var currentQueryCache = settings.QueryCacheFactory.GetQueryCache(cr, updateTimestampsCache, settings, properties); - allCacheRegions[currentQueryCache.RegionName] = currentQueryCache.Cache; - return currentQueryCache; - })).Value; + return + queryCaches + .GetOrAdd( + cacheRegion, + cr => new Lazy(() => BuildQueryCache(cr))) + .Value; } public void EvictQueries() From 602b4357bb6bc94e51f0e700160b5078220441f0 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Fri, 23 Nov 2018 16:37:16 +1300 Subject: [PATCH 02/10] Cleanup wrapping of ICache to CacheBase --- src/NHibernate/Cache/CacheExtensions.cs | 16 ++++++++++++++++ src/NHibernate/Cache/CacheFactory.cs | 6 +----- .../Cache/ICacheConcurrencyStrategy.cs | 5 +---- src/NHibernate/Cache/NonstrictReadWriteCache.cs | 6 +----- src/NHibernate/Cache/ReadOnlyCache.cs | 6 +----- src/NHibernate/Cache/ReadWriteCache.cs | 6 +----- src/NHibernate/Cache/StandardQueryCache.cs | 9 +++------ src/NHibernate/Cache/UpdateTimestampsCache.cs | 4 ++-- src/NHibernate/Impl/SessionFactoryImpl.cs | 2 +- 9 files changed, 27 insertions(+), 33 deletions(-) create mode 100644 src/NHibernate/Cache/CacheExtensions.cs diff --git a/src/NHibernate/Cache/CacheExtensions.cs b/src/NHibernate/Cache/CacheExtensions.cs new file mode 100644 index 00000000000..1b212572557 --- /dev/null +++ b/src/NHibernate/Cache/CacheExtensions.cs @@ -0,0 +1,16 @@ +using System; + +namespace NHibernate.Cache +{ + //6.0 TODO: Remove + internal static class CacheExtensions + { +#pragma warning disable 618 + public static CacheBase AsCacheBase(this ICache cache) +#pragma warning restore 618 + { + if (cache == null) throw new ArgumentNullException(nameof(cache)); + return cache as CacheBase ?? new ObsoleteCacheWrapper(cache); + } + } +} \ No newline at end of file diff --git a/src/NHibernate/Cache/CacheFactory.cs b/src/NHibernate/Cache/CacheFactory.cs index c4ebcb7997a..d167d76b3df 100644 --- a/src/NHibernate/Cache/CacheFactory.cs +++ b/src/NHibernate/Cache/CacheFactory.cs @@ -41,11 +41,7 @@ public static ICacheConcurrencyStrategy CreateCache( { var cache = CreateCache( usage, name, settings, - (r, u) => - { - var c = settings.CacheProvider.BuildCache(r, properties); - return c as CacheBase ?? new ObsoleteCacheWrapper(c); - }); + (r, u) => settings.CacheProvider.BuildCache(r, properties).AsCacheBase()); if (cache != null && mutable && usage == ReadOnly) log.Warn("read-only cache configured for mutable: {0}", name); diff --git a/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs b/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs index 41319d62ad7..34b0ce449c2 100644 --- a/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs +++ b/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs @@ -206,10 +206,7 @@ internal static CacheBase GetCacheBase(this ICacheConcurrencyStrategy cache) { if (cache is IBatchableCacheConcurrencyStrategy batchableCache) return batchableCache.Cache; - var concreteCache = cache.Cache; - if (concreteCache == null) - return null; - return concreteCache as CacheBase ?? new ObsoleteCacheWrapper(concreteCache); + return cache.Cache?.AsCacheBase(); } } } diff --git a/src/NHibernate/Cache/NonstrictReadWriteCache.cs b/src/NHibernate/Cache/NonstrictReadWriteCache.cs index e6af5d4982a..a74812f66fb 100644 --- a/src/NHibernate/Cache/NonstrictReadWriteCache.cs +++ b/src/NHibernate/Cache/NonstrictReadWriteCache.cs @@ -18,7 +18,6 @@ public partial class NonstrictReadWriteCache : IBatchableCacheConcurrencyStrateg { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(NonstrictReadWriteCache)); - // 6.0 TODO: remove private CacheBase _cache; /// @@ -35,10 +34,7 @@ public ICache Cache #pragma warning restore 618 { get { return _cache; } - set - { - _cache = value as CacheBase ?? new ObsoleteCacheWrapper(value); - } + set { _cache = value.AsCacheBase(); } } // 6.0 TODO: make implicit and switch to auto-property diff --git a/src/NHibernate/Cache/ReadOnlyCache.cs b/src/NHibernate/Cache/ReadOnlyCache.cs index dd2fde0e51a..6fcc3e43847 100644 --- a/src/NHibernate/Cache/ReadOnlyCache.cs +++ b/src/NHibernate/Cache/ReadOnlyCache.cs @@ -13,7 +13,6 @@ public partial class ReadOnlyCache : IBatchableCacheConcurrencyStrategy { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(ReadOnlyCache)); - // 6.0 TODO: remove private CacheBase _cache; /// @@ -30,10 +29,7 @@ public ICache Cache #pragma warning restore 618 { get { return _cache; } - set - { - _cache = value as CacheBase ?? new ObsoleteCacheWrapper(value); - } + set { _cache = value.AsCacheBase(); } } // 6.0 TODO: make implicit and switch to auto-property diff --git a/src/NHibernate/Cache/ReadWriteCache.cs b/src/NHibernate/Cache/ReadWriteCache.cs index 4a8fff66187..531ff12cc83 100644 --- a/src/NHibernate/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Cache/ReadWriteCache.cs @@ -35,7 +35,6 @@ public interface ILockable private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(ReadWriteCache)); private readonly object _lockObject = new object(); - // 6.0 TODO: remove private CacheBase _cache; private int _nextLockId; @@ -53,10 +52,7 @@ public ICache Cache #pragma warning restore 618 { get { return _cache; } - set - { - _cache = value as CacheBase ?? new ObsoleteCacheWrapper(value); - } + set { _cache = value.AsCacheBase(); } } // 6.0 TODO: make implicit and switch to auto-property diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index 47d9cc0773b..89a952730dd 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -20,11 +20,10 @@ public partial class StandardQueryCache : IQueryCache, IBatchableQueryCache private static readonly INHibernateLogger Log = NHibernateLogger.For(typeof (StandardQueryCache)); private readonly string _regionName; private readonly UpdateTimestampsCache _updateTimestampsCache; - // 6.0 TODO: remove private readonly CacheBase _cache; // Since v5.2 - [Obsolete("Please use overload with an ICache parameter.")] + [Obsolete("Please use overload with an CacheBase parameter.")] public StandardQueryCache( Settings settings, IDictionary props, @@ -41,10 +40,8 @@ public StandardQueryCache( // Since v5.2 [Obsolete] - private StandardQueryCache( - UpdateTimestampsCache updateTimestampsCache, - ICache cache) - : this(updateTimestampsCache, cache as CacheBase ?? new ObsoleteCacheWrapper(cache)) + private StandardQueryCache(UpdateTimestampsCache updateTimestampsCache, ICache cache) + : this(updateTimestampsCache, cache.AsCacheBase()) { } diff --git a/src/NHibernate/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Cache/UpdateTimestampsCache.cs index e1b0486f5a1..0d1af8d7474 100644 --- a/src/NHibernate/Cache/UpdateTimestampsCache.cs +++ b/src/NHibernate/Cache/UpdateTimestampsCache.cs @@ -26,7 +26,7 @@ public virtual void Clear() } // Since v5.2 - [Obsolete("Please use overload with an ICache parameter.")] + [Obsolete("Please use overload with an CacheBase parameter.")] public UpdateTimestampsCache(Settings settings, IDictionary props) : this( settings.CacheProvider.BuildCache( @@ -37,7 +37,7 @@ public UpdateTimestampsCache(Settings settings, IDictionary prop // Since v5.2 [Obsolete] private UpdateTimestampsCache(ICache cache) - : this(cache as CacheBase ?? new ObsoleteCacheWrapper(cache)) + : this(cache.AsCacheBase()) { } diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index 1ed3077f2ec..59582704bc3 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -1112,7 +1112,7 @@ private CacheBase BuildCache(string cacheRegion, string type) throw new InvalidOperationException($"A cache has already been built for region {cacheRegion} and type {type}."); } - return cache as CacheBase ?? new ObsoleteCacheWrapper(cache); + return cache.AsCacheBase(); } /// Statistics SPI From 6858890fc2beb00cd92ea4f7e97c2ac962f92fbe Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Fri, 23 Nov 2018 16:40:12 +1300 Subject: [PATCH 03/10] Resolve TODO --- src/NHibernate/Impl/SessionFactoryImpl.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index 59582704bc3..6d8e4070ebe 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -95,11 +95,8 @@ public void HandleEntityNotFound(string entityName, object id) private static readonly IIdentifierGenerator UuidGenerator = new UUIDHexGenerator(); [NonSerialized] - // 6.0 TODO: type as CacheBase instead -#pragma warning disable 618 - private readonly ConcurrentDictionary> allCachePerRegionThenType = - new ConcurrentDictionary>(); -#pragma warning restore 618 + private readonly ConcurrentDictionary> allCachePerRegionThenType = + new ConcurrentDictionary>(); [NonSerialized] private readonly IDictionary classMetadata; @@ -1078,7 +1075,9 @@ public IDictionary GetAllSecondLevelCacheRegions() // ToArray creates a moment in time snapshot .ToArray() // Caches are not unique per region, take the first one. - .ToDictionary(kv => kv.Key, kv => kv.Value.Values.First()); +#pragma warning disable 618 + .ToDictionary(kv => kv.Key, kv => (ICache)kv.Value.Values.First()); +#pragma warning restore 618 } // 6.0 TODO: return CacheBase instead @@ -1102,17 +1101,15 @@ private CacheBase BuildCache(string cacheRegion, string type) var prefix = settings.CacheRegionPrefix; if (!string.IsNullOrEmpty(prefix)) cacheRegion = prefix + '.' + cacheRegion; -#pragma warning disable 618 - var cachesPerType = allCachePerRegionThenType.GetOrAdd(cacheRegion, cr => new ConcurrentDictionary()); -#pragma warning restore 618 - var cache = settings.CacheProvider.BuildCache(cacheRegion, properties); + var cachesPerType = allCachePerRegionThenType.GetOrAdd(cacheRegion, cr => new ConcurrentDictionary()); + var cache = settings.CacheProvider.BuildCache(cacheRegion, properties).AsCacheBase(); if (!cachesPerType.TryAdd(type, cache)) { cache.Destroy(); throw new InvalidOperationException($"A cache has already been built for region {cacheRegion} and type {type}."); } - return cache.AsCacheBase(); + return cache; } /// Statistics SPI From 844cffd394b7f266b0854fe6e150f10651b55455 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Fri, 23 Nov 2018 17:03:50 +1300 Subject: [PATCH 04/10] Remove grouping of caches by type --- src/NHibernate/Cache/CacheFactory.cs | 6 ++--- src/NHibernate/Impl/SessionFactoryImpl.cs | 32 +++++++---------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/NHibernate/Cache/CacheFactory.cs b/src/NHibernate/Cache/CacheFactory.cs index d167d76b3df..026d0eb0850 100644 --- a/src/NHibernate/Cache/CacheFactory.cs +++ b/src/NHibernate/Cache/CacheFactory.cs @@ -41,7 +41,7 @@ public static ICacheConcurrencyStrategy CreateCache( { var cache = CreateCache( usage, name, settings, - (r, u) => settings.CacheProvider.BuildCache(r, properties).AsCacheBase()); + r => settings.CacheProvider.BuildCache(r, properties).AsCacheBase()); if (cache != null && mutable && usage == ReadOnly) log.Warn("read-only cache configured for mutable: {0}", name); @@ -61,7 +61,7 @@ public static ICacheConcurrencyStrategy CreateCache( string usage, string name, Settings settings, - Func regionAndUsageCacheGetter) + Func regionAndUsageCacheGetter) { if (usage == null || !settings.IsSecondLevelCacheEnabled) return null; //no cache @@ -92,7 +92,7 @@ public static ICacheConcurrencyStrategy CreateCache( try { - ccs.Cache = regionAndUsageCacheGetter(name, usage); + ccs.Cache = regionAndUsageCacheGetter(name); } catch (CacheException e) { diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index 6d8e4070ebe..8f461d22352 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -95,8 +95,8 @@ public void HandleEntityNotFound(string entityName, object id) private static readonly IIdentifierGenerator UuidGenerator = new UUIDHexGenerator(); [NonSerialized] - private readonly ConcurrentDictionary> allCachePerRegionThenType = - new ConcurrentDictionary>(); + private readonly ConcurrentDictionary _allCacheRegions = + new ConcurrentDictionary(); [NonSerialized] private readonly IDictionary classMetadata; @@ -148,8 +148,6 @@ public void HandleEntityNotFound(string entityName, object id) [NonSerialized] private readonly IQueryCache queryCache; - private const string QueryCacheType = "QueryCache"; - [NonSerialized] private readonly ConcurrentDictionary> queryCaches; [NonSerialized] @@ -370,7 +368,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings if (settings.IsQueryCacheEnabled) { var updateTimestampsCacheName = typeof(UpdateTimestampsCache).Name; - updateTimestampsCache = new UpdateTimestampsCache(BuildCache(updateTimestampsCacheName, updateTimestampsCacheName)); + updateTimestampsCache = new UpdateTimestampsCache(BuildCache(updateTimestampsCacheName)); var queryCacheName = typeof(StandardQueryCache).FullName; queryCache = BuildQueryCache(queryCacheName); queryCaches = new ConcurrentDictionary>(); @@ -417,7 +415,7 @@ private IQueryCache BuildQueryCache(string queryCacheName) settings.QueryCacheFactory.GetQueryCache( updateTimestampsCache, properties, - BuildCache(queryCacheName, QueryCacheType)) + BuildCache(queryCacheName)) // 6.0 TODO: remove the coalesce once IQueryCacheFactory todos are done #pragma warning disable 618 ?? settings.QueryCacheFactory.GetQueryCache( @@ -1071,12 +1069,11 @@ public IDictionary GetAllSecondLevelCacheRegions() #pragma warning restore 618 { return - allCachePerRegionThenType + _allCacheRegions // ToArray creates a moment in time snapshot .ToArray() - // Caches are not unique per region, take the first one. #pragma warning disable 618 - .ToDictionary(kv => kv.Key, kv => (ICache)kv.Value.Values.First()); + .ToDictionary(kv => kv.Key, kv => (ICache) kv.Value); #pragma warning restore 618 } @@ -1085,13 +1082,11 @@ public IDictionary GetAllSecondLevelCacheRegions() public ICache GetSecondLevelCacheRegion(string regionName) #pragma warning restore 618 { - if (!allCachePerRegionThenType.TryGetValue(regionName, out var result)) - return null; - // Caches are not unique per region, take the first one. - return result.Values.First(); + _allCacheRegions.TryGetValue(regionName, out var result); + return result; } - private CacheBase BuildCache(string cacheRegion, string type) + private CacheBase BuildCache(string cacheRegion) { // If run concurrently for the same region and type, this may built many caches for the same region and type. // Currently only GetQueryCache may be run concurrently, and its implementation prevents @@ -1101,15 +1096,8 @@ private CacheBase BuildCache(string cacheRegion, string type) var prefix = settings.CacheRegionPrefix; if (!string.IsNullOrEmpty(prefix)) cacheRegion = prefix + '.' + cacheRegion; - var cachesPerType = allCachePerRegionThenType.GetOrAdd(cacheRegion, cr => new ConcurrentDictionary()); - var cache = settings.CacheProvider.BuildCache(cacheRegion, properties).AsCacheBase(); - if (!cachesPerType.TryAdd(type, cache)) - { - cache.Destroy(); - throw new InvalidOperationException($"A cache has already been built for region {cacheRegion} and type {type}."); - } - return cache; + return _allCacheRegions.GetOrAdd(cacheRegion, cr => settings.CacheProvider.BuildCache(cr, properties).AsCacheBase()); } /// Statistics SPI From 001bd0aaa5757caa566985006da5713d13be186e Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Fri, 23 Nov 2018 18:41:12 +1300 Subject: [PATCH 05/10] Remove delegate based logic --- src/NHibernate/Cache/CacheFactory.cs | 41 +++++++++---------- src/NHibernate/Cache/StandardQueryCache.cs | 16 ++------ src/NHibernate/Cache/UpdateTimestampsCache.cs | 13 ++---- src/NHibernate/Cfg/Settings.cs | 8 ++++ src/NHibernate/Impl/SessionFactoryImpl.cs | 27 ++++++------ 5 files changed, 47 insertions(+), 58 deletions(-) diff --git a/src/NHibernate/Cache/CacheFactory.cs b/src/NHibernate/Cache/CacheFactory.cs index 026d0eb0850..5342dbacd11 100644 --- a/src/NHibernate/Cache/CacheFactory.cs +++ b/src/NHibernate/Cache/CacheFactory.cs @@ -39,36 +39,28 @@ public static ICacheConcurrencyStrategy CreateCache( Settings settings, IDictionary properties) { - var cache = CreateCache( - usage, name, settings, - r => settings.CacheProvider.BuildCache(r, properties).AsCacheBase()); + if (usage == null || !settings.IsSecondLevelCacheEnabled) return null; - if (cache != null && mutable && usage == ReadOnly) - log.Warn("read-only cache configured for mutable: {0}", name); + var cache = BuildCacheBase(name, settings, properties); + + var ccs = CreateCache(usage,cache); - return cache; + if (mutable && usage == ReadOnly) + log.Warn("read-only cache configured for mutable: {0}", name); + + return ccs; } /// /// Creates an from the parameters. /// /// The name of the strategy that should use for the class. - /// The name of the cache region the strategy is being created for. - /// Used to retrieve the global cache region prefix. - /// The delegate for obtaining the to use for the region. + /// The used for this strategy. /// An to use for this object in the . - public static ICacheConcurrencyStrategy CreateCache( - string usage, - string name, - Settings settings, - Func regionAndUsageCacheGetter) + public static ICacheConcurrencyStrategy CreateCache(string usage, CacheBase cache) { - if (usage == null || !settings.IsSecondLevelCacheEnabled) return null; //no cache - if (log.IsDebugEnabled()) - { - log.Debug("cache for: {0} usage strategy: {1}", name, usage); - } + log.Debug("cache for: {0} usage strategy: {1}", cache.RegionName, usage); ICacheConcurrencyStrategy ccs; switch (usage) @@ -90,16 +82,21 @@ public static ICacheConcurrencyStrategy CreateCache( "cache usage attribute should be read-write, read-only or nonstrict-read-write"); } + ccs.Cache = cache; + + return ccs; + } + + internal static CacheBase BuildCacheBase(string name, Settings settings, IDictionary properties) + { try { - ccs.Cache = regionAndUsageCacheGetter(name); + return settings.CacheProvider.BuildCache(name, properties).AsCacheBase(); } catch (CacheException e) { throw new HibernateException("Could not instantiate cache implementation", e); } - - return ccs; } } } diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index 89a952730dd..323f6a56e95 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -31,20 +31,13 @@ public StandardQueryCache( string regionName) : this( updateTimestampsCache, - settings.CacheProvider.BuildCache( - (!string.IsNullOrEmpty(settings.CacheRegionPrefix) ? settings.CacheRegionPrefix + '.' : "") + - (regionName ?? typeof (StandardQueryCache).FullName), + CacheFactory.BuildCacheBase( + settings.GetFullCacheRegionName(regionName ?? typeof(StandardQueryCache).FullName), + settings, props)) { } - // Since v5.2 - [Obsolete] - private StandardQueryCache(UpdateTimestampsCache updateTimestampsCache, ICache cache) - : this(updateTimestampsCache, cache.AsCacheBase()) - { - } - /// /// Build a query cache. /// @@ -60,7 +53,6 @@ public StandardQueryCache( _regionName = regionCache.RegionName; Log.Info("starting query cache at region: {0}", _regionName); - Cache = regionCache; _cache = regionCache; _updateTimestampsCache = updateTimestampsCache; } @@ -69,7 +61,7 @@ public StandardQueryCache( // 6.0 TODO: type as CacheBase instead #pragma warning disable 618 - public ICache Cache { get; } + public ICache Cache => _cache; #pragma warning restore 618 public string RegionName diff --git a/src/NHibernate/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Cache/UpdateTimestampsCache.cs index 0d1af8d7474..f3dd3fe3726 100644 --- a/src/NHibernate/Cache/UpdateTimestampsCache.cs +++ b/src/NHibernate/Cache/UpdateTimestampsCache.cs @@ -29,15 +29,10 @@ public virtual void Clear() [Obsolete("Please use overload with an CacheBase parameter.")] public UpdateTimestampsCache(Settings settings, IDictionary props) : this( - settings.CacheProvider.BuildCache( - (settings.CacheRegionPrefix == null ? "" : settings.CacheRegionPrefix + '.') + - typeof(UpdateTimestampsCache).Name, - props)) {} - - // Since v5.2 - [Obsolete] - private UpdateTimestampsCache(ICache cache) - : this(cache.AsCacheBase()) + CacheFactory.BuildCacheBase( + settings.GetFullCacheRegionName(nameof(UpdateTimestampsCache)), + settings, + props)) { } diff --git a/src/NHibernate/Cfg/Settings.cs b/src/NHibernate/Cfg/Settings.cs index 5ad3e3ca8e2..878b9b60605 100644 --- a/src/NHibernate/Cfg/Settings.cs +++ b/src/NHibernate/Cfg/Settings.cs @@ -137,5 +137,13 @@ public Settings() public IQueryModelRewriterFactory QueryModelRewriterFactory { get; internal set; } #endregion + + internal string GetFullCacheRegionName(string name) + { + var prefix = CacheRegionPrefix; + if (!string.IsNullOrEmpty(prefix)) + return prefix + '.' + name; + return name; + } } } \ No newline at end of file diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index 8f461d22352..be70ee0977a 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -433,18 +433,15 @@ private ICacheConcurrencyStrategy GetCacheConcurrencyStrategy( Dictionary, ICacheConcurrencyStrategy> caches) { var cacheKey = new Tuple(cacheRegion, strategy); - if (!caches.TryGetValue(cacheKey, out var cache)) - { - cache = CacheFactory.CreateCache( - strategy, - cacheRegion, - settings, - BuildCache); - if (cache != null) - caches.Add(cacheKey, cache); - } + if (strategy == null || !settings.IsSecondLevelCacheEnabled) + return null; + + if (caches.TryGetValue(cacheKey, out var cache)) + return cache; - if (cache != null && isMutable && strategy == CacheFactory.ReadOnly) + cache = CacheFactory.CreateCache(strategy, BuildCache(cacheRegion)); + caches.Add(cacheKey, cache); + if (isMutable && strategy == CacheFactory.ReadOnly) log.Warn("read-only cache configured for mutable: {0}", name); return cache; @@ -1093,11 +1090,11 @@ private CacheBase BuildCache(string cacheRegion) // concurrent creation call for the same region, so this will not happen. // Otherwise the dictionary will have to be changed for using a lazy, see // https://stackoverflow.com/a/31637510/1178314 - var prefix = settings.CacheRegionPrefix; - if (!string.IsNullOrEmpty(prefix)) - cacheRegion = prefix + '.' + cacheRegion; + cacheRegion = settings.GetFullCacheRegionName(cacheRegion); - return _allCacheRegions.GetOrAdd(cacheRegion, cr => settings.CacheProvider.BuildCache(cr, properties).AsCacheBase()); + return _allCacheRegions.GetOrAdd( + cacheRegion, + cr => CacheFactory.BuildCacheBase(cr, settings, properties)); } /// Statistics SPI From cbcaa28104fea84930b3c542531ad2f6b3438d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 23 Nov 2018 15:51:29 +0100 Subject: [PATCH 06/10] Test uniqueness of cache per region Added back from the old original commit which was later squashed with a commit giving-up this uniqueness. --- src/NHibernate.Test/CacheTest/BuildCacheFixture.cs | 7 ++++++- src/NHibernate/Cache/StandardQueryCache.cs | 2 +- src/NHibernate/Cache/StandardQueryCacheFactory.cs | 2 +- src/NHibernate/Cache/UpdateTimestampsCache.cs | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs b/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs index c936a122664..f0feea553c7 100644 --- a/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/BuildCacheFixture.cs @@ -28,7 +28,7 @@ protected override void Configure(Configuration configuration) } [Theory] - public void CommonRegionHasExpectedConcurrency(bool withPrefix) + public void CommonRegionHasOneUniqueCacheAndExpectedConcurrency(bool withPrefix) { const string prefix = "Prefix"; const string region = "Common"; @@ -70,6 +70,11 @@ public void CommonRegionHasExpectedConcurrency(bool withPrefix) Assert.That(relatedAConcurrencyCache, Is.InstanceOf(), "Unexpected concurrency for RelatedA"); Assert.That(entityBConcurrencyCache, Is.InstanceOf(), "Unexpected concurrency for EntityB"); Assert.That(relatedBConcurrencyCache, Is.InstanceOf(), "Unexpected concurrency for RelatedB"); + Assert.That(entityACache, Is.SameAs(commonRegionCache), "Unexpected cache for EntityA"); + Assert.That(entityBCache, Is.SameAs(commonRegionCache), "Unexpected cache for EntityB"); + Assert.That(relatedACache, Is.SameAs(commonRegionCache), "Unexpected cache for RelatedA"); + Assert.That(relatedBCache, Is.SameAs(commonRegionCache), "Unexpected cache for RelatedB"); + Assert.That(queryCache, Is.SameAs(commonRegionCache), "Unexpected cache for query cache"); }); } finally diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index 323f6a56e95..b7ff0fcea19 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -23,7 +23,7 @@ public partial class StandardQueryCache : IQueryCache, IBatchableQueryCache private readonly CacheBase _cache; // Since v5.2 - [Obsolete("Please use overload with an CacheBase parameter.")] + [Obsolete("Please use overload with a CacheBase parameter.")] public StandardQueryCache( Settings settings, IDictionary props, diff --git a/src/NHibernate/Cache/StandardQueryCacheFactory.cs b/src/NHibernate/Cache/StandardQueryCacheFactory.cs index 6867b2ae21e..8b3874aec8f 100644 --- a/src/NHibernate/Cache/StandardQueryCacheFactory.cs +++ b/src/NHibernate/Cache/StandardQueryCacheFactory.cs @@ -11,7 +11,7 @@ namespace NHibernate.Cache public class StandardQueryCacheFactory : IQueryCacheFactory { // Since v5.2 - [Obsolete("Please use overload with an CacheBase parameter.")] + [Obsolete("Please use overload with a CacheBase parameter.")] public IQueryCache GetQueryCache(string regionName, UpdateTimestampsCache updateTimestampsCache, Settings settings, diff --git a/src/NHibernate/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Cache/UpdateTimestampsCache.cs index f3dd3fe3726..0ca259eac4d 100644 --- a/src/NHibernate/Cache/UpdateTimestampsCache.cs +++ b/src/NHibernate/Cache/UpdateTimestampsCache.cs @@ -26,7 +26,7 @@ public virtual void Clear() } // Since v5.2 - [Obsolete("Please use overload with an CacheBase parameter.")] + [Obsolete("Please use overload with a CacheBase parameter.")] public UpdateTimestampsCache(Settings settings, IDictionary props) : this( CacheFactory.BuildCacheBase( From b55eb277053077d8af440d4580d939f9cb8ce759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 23 Nov 2018 16:22:49 +0100 Subject: [PATCH 07/10] Handle CacheBase.Destroy in the session factory --- src/NHibernate/Async/Impl/SessionFactoryImpl.cs | 5 ++++- src/NHibernate/Cache/CacheFactory.cs | 2 +- .../Cache/ICacheConcurrencyStrategy.cs | 5 ++++- src/NHibernate/Cache/IQueryCache.cs | 5 ++++- src/NHibernate/Cache/NonstrictReadWriteCache.cs | 11 +++-------- src/NHibernate/Cache/ReadOnlyCache.cs | 11 +++-------- src/NHibernate/Cache/ReadWriteCache.cs | 11 +++-------- src/NHibernate/Cache/StandardQueryCache.cs | 10 ++-------- src/NHibernate/Cache/UpdateTimestampsCache.cs | 12 ++++-------- src/NHibernate/Impl/SessionFactoryImpl.cs | 17 ++++++++++------- 10 files changed, 38 insertions(+), 51 deletions(-) diff --git a/src/NHibernate/Async/Impl/SessionFactoryImpl.cs b/src/NHibernate/Async/Impl/SessionFactoryImpl.cs index 4ab92b975fe..af52013f08b 100644 --- a/src/NHibernate/Async/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Async/Impl/SessionFactoryImpl.cs @@ -89,8 +89,11 @@ public sealed partial class SessionFactoryImpl : ISessionFactoryImplementor, IOb { cache.Value.Destroy(); } + } - updateTimestampsCache.Destroy(); + foreach (var cache in _allCacheRegions.Values) + { + cache.Destroy(); } settings.CacheProvider.Stop(); diff --git a/src/NHibernate/Cache/CacheFactory.cs b/src/NHibernate/Cache/CacheFactory.cs index 5342dbacd11..670aa4913dd 100644 --- a/src/NHibernate/Cache/CacheFactory.cs +++ b/src/NHibernate/Cache/CacheFactory.cs @@ -43,7 +43,7 @@ public static ICacheConcurrencyStrategy CreateCache( var cache = BuildCacheBase(name, settings, properties); - var ccs = CreateCache(usage,cache); + var ccs = CreateCache(usage, cache); if (mutable && usage == ReadOnly) log.Warn("read-only cache configured for mutable: {0}", name); diff --git a/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs b/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs index 34b0ce449c2..3c4e8755384 100644 --- a/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs +++ b/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs @@ -124,9 +124,12 @@ public partial interface ICacheConcurrencyStrategy void Clear(); /// - /// Clean up all resources. + /// Clean up resources. /// /// + /// + /// This method should not destroy . The session factory is responsible for it. + /// void Destroy(); /// diff --git a/src/NHibernate/Cache/IQueryCache.cs b/src/NHibernate/Cache/IQueryCache.cs index b770980c20f..6778c60be78 100644 --- a/src/NHibernate/Cache/IQueryCache.cs +++ b/src/NHibernate/Cache/IQueryCache.cs @@ -44,8 +44,11 @@ public partial interface IQueryCache IList Get(QueryKey key, ICacheAssembler[] returnTypes, bool isNaturalKeyLookup, ISet spaces, ISessionImplementor session); /// - /// Clean up all resources. + /// Clean up resources. /// + /// + /// This method should not destroy . The session factory is responsible for it. + /// void Destroy(); } diff --git a/src/NHibernate/Cache/NonstrictReadWriteCache.cs b/src/NHibernate/Cache/NonstrictReadWriteCache.cs index a74812f66fb..780464a74d8 100644 --- a/src/NHibernate/Cache/NonstrictReadWriteCache.cs +++ b/src/NHibernate/Cache/NonstrictReadWriteCache.cs @@ -203,14 +203,9 @@ public void Clear() public void Destroy() { - try - { - Cache.Destroy(); - } - catch (Exception e) - { - log.Warn(e, "Could not destroy cache"); - } + // The cache is externally provided and may be shared. Destroying the cache is + // not the responsibility of this class. + Cache = null; } /// diff --git a/src/NHibernate/Cache/ReadOnlyCache.cs b/src/NHibernate/Cache/ReadOnlyCache.cs index 6fcc3e43847..477ac3f8625 100644 --- a/src/NHibernate/Cache/ReadOnlyCache.cs +++ b/src/NHibernate/Cache/ReadOnlyCache.cs @@ -181,14 +181,9 @@ public void Remove(CacheKey key) public void Destroy() { - try - { - Cache.Destroy(); - } - catch (Exception e) - { - log.Warn(e, "Could not destroy cache"); - } + // The cache is externally provided and may be shared. Destroying the cache is + // not the responsibility of this class. + Cache = null; } /// diff --git a/src/NHibernate/Cache/ReadWriteCache.cs b/src/NHibernate/Cache/ReadWriteCache.cs index 531ff12cc83..ef3db827daf 100644 --- a/src/NHibernate/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Cache/ReadWriteCache.cs @@ -413,14 +413,9 @@ public void Remove(CacheKey key) public void Destroy() { - try - { - Cache.Destroy(); - } - catch (Exception e) - { - log.Warn(e, "Could not destroy cache"); - } + // The cache is externally provided and may be shared. Destroying the cache is + // not the responsibility of this class. + Cache = null; } /// diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index b7ff0fcea19..ae42e0fbbbc 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -281,14 +281,8 @@ public IList[] GetMany( public void Destroy() { - try - { - Cache.Destroy(); - } - catch (Exception e) - { - Log.Warn(e, "could not destroy query cache: {0}", _regionName); - } + // The cache is externally provided and may be shared. Destroying the cache is + // not the responsibility of this class. } #endregion diff --git a/src/NHibernate/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Cache/UpdateTimestampsCache.cs index 0ca259eac4d..b1ef75eb831 100644 --- a/src/NHibernate/Cache/UpdateTimestampsCache.cs +++ b/src/NHibernate/Cache/UpdateTimestampsCache.cs @@ -155,16 +155,12 @@ public virtual bool[] AreUpToDate(ISet[] spaces, long[] timestamps) return results; } + // Since v5.2 + [Obsolete("This method has no usages anymore")] public virtual void Destroy() { - try - { - _updateTimestamps.Destroy(); - } - catch (Exception e) - { - log.Warn(e, "could not destroy UpdateTimestamps cache"); - } + // The cache is externally provided and may be shared. Destroying the cache is + // not the responsibility of this class. } private bool IsOutdated(long? lastUpdate, long timestamp) diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index be70ee0977a..ae70fe57ad2 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -368,7 +368,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings if (settings.IsQueryCacheEnabled) { var updateTimestampsCacheName = typeof(UpdateTimestampsCache).Name; - updateTimestampsCache = new UpdateTimestampsCache(BuildCache(updateTimestampsCacheName)); + updateTimestampsCache = new UpdateTimestampsCache(GetCache(updateTimestampsCacheName)); var queryCacheName = typeof(StandardQueryCache).FullName; queryCache = BuildQueryCache(queryCacheName); queryCaches = new ConcurrentDictionary>(); @@ -415,7 +415,7 @@ private IQueryCache BuildQueryCache(string queryCacheName) settings.QueryCacheFactory.GetQueryCache( updateTimestampsCache, properties, - BuildCache(queryCacheName)) + GetCache(queryCacheName)) // 6.0 TODO: remove the coalesce once IQueryCacheFactory todos are done #pragma warning disable 618 ?? settings.QueryCacheFactory.GetQueryCache( @@ -432,14 +432,14 @@ private ICacheConcurrencyStrategy GetCacheConcurrencyStrategy( bool isMutable, Dictionary, ICacheConcurrencyStrategy> caches) { - var cacheKey = new Tuple(cacheRegion, strategy); if (strategy == null || !settings.IsSecondLevelCacheEnabled) return null; + var cacheKey = new Tuple(cacheRegion, strategy); if (caches.TryGetValue(cacheKey, out var cache)) return cache; - - cache = CacheFactory.CreateCache(strategy, BuildCache(cacheRegion)); + + cache = CacheFactory.CreateCache(strategy, GetCache(cacheRegion)); caches.Add(cacheKey, cache); if (isMutable && strategy == CacheFactory.ReadOnly) log.Warn("read-only cache configured for mutable: {0}", name); @@ -878,8 +878,11 @@ public void Close() { cache.Value.Destroy(); } + } - updateTimestampsCache.Destroy(); + foreach (var cache in _allCacheRegions.Values) + { + cache.Destroy(); } settings.CacheProvider.Stop(); @@ -1083,7 +1086,7 @@ public ICache GetSecondLevelCacheRegion(string regionName) return result; } - private CacheBase BuildCache(string cacheRegion) + private CacheBase GetCache(string cacheRegion) { // If run concurrently for the same region and type, this may built many caches for the same region and type. // Currently only GetQueryCache may be run concurrently, and its implementation prevents From 40e223c47faaa5878f1639c9708342c906a9d294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 23 Nov 2018 16:43:31 +0100 Subject: [PATCH 08/10] Do additional cleanup --- .../Async/Cache/NonstrictReadWriteCache.cs | 13 ++++++------- src/NHibernate/Async/Cache/ReadWriteCache.cs | 9 ++++----- src/NHibernate/Cache/CacheFactory.cs | 4 ++-- src/NHibernate/Cache/NonstrictReadWriteCache.cs | 1 - src/NHibernate/Cache/ReadWriteCache.cs | 1 - 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs b/src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs index b0462b1bcf5..d074aeb3c15 100644 --- a/src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs +++ b/src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs @@ -8,7 +8,6 @@ //------------------------------------------------------------------------------ -using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -169,7 +168,7 @@ public Task LockAsync(CacheKey key, object version, CancellationToken { return Task.FromResult(Lock(key, version)); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } @@ -189,7 +188,7 @@ public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken) } return Cache.RemoveAsync(key, cancellationToken); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } @@ -209,7 +208,7 @@ public Task ClearAsync(CancellationToken cancellationToken) } return Cache.ClearAsync(cancellationToken); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } @@ -232,7 +231,7 @@ public Task EvictAsync(CacheKey key, CancellationToken cancellationToken) } return Cache.RemoveAsync(key, cancellationToken); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } @@ -266,7 +265,7 @@ public Task ReleaseAsync(CacheKey key, ISoftLock @lock, CancellationToken cancel return Cache.RemoveAsync(key, cancellationToken); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } @@ -295,7 +294,7 @@ public Task AfterInsertAsync(CacheKey key, object value, object version, C { return Task.FromResult(AfterInsert(key, value, version)); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } diff --git a/src/NHibernate/Async/Cache/ReadWriteCache.cs b/src/NHibernate/Async/Cache/ReadWriteCache.cs index 28ec417b861..0813d2d8dd4 100644 --- a/src/NHibernate/Async/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Async/Cache/ReadWriteCache.cs @@ -8,7 +8,6 @@ //------------------------------------------------------------------------------ -using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -317,7 +316,7 @@ private Task DecrementLockAsync(object key, CacheLock @lock, CancellationToken c @lock.Unlock(Cache.NextTimestamp()); return Cache.PutAsync(key, @lock, cancellationToken); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } @@ -368,7 +367,7 @@ internal Task HandleLockExpiryAsync(object key, CancellationToken cancellationTo @lock.Unlock(ts); return Cache.PutAsync(key, @lock, cancellationToken); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } @@ -490,7 +489,7 @@ public Task EvictAsync(CacheKey key, CancellationToken cancellationToken) Evict(key); return Task.CompletedTask; } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } @@ -506,7 +505,7 @@ public Task UpdateAsync(CacheKey key, object value, object currentVersion, { return Task.FromResult(Update(key, value, currentVersion, previousVersion)); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } diff --git a/src/NHibernate/Cache/CacheFactory.cs b/src/NHibernate/Cache/CacheFactory.cs index 670aa4913dd..6499f64b46f 100644 --- a/src/NHibernate/Cache/CacheFactory.cs +++ b/src/NHibernate/Cache/CacheFactory.cs @@ -1,6 +1,6 @@ using System; -using NHibernate.Cfg; using System.Collections.Generic; +using NHibernate.Cfg; namespace NHibernate.Cache { @@ -47,7 +47,7 @@ public static ICacheConcurrencyStrategy CreateCache( if (mutable && usage == ReadOnly) log.Warn("read-only cache configured for mutable: {0}", name); - + return ccs; } diff --git a/src/NHibernate/Cache/NonstrictReadWriteCache.cs b/src/NHibernate/Cache/NonstrictReadWriteCache.cs index 780464a74d8..ca0e29ffb18 100644 --- a/src/NHibernate/Cache/NonstrictReadWriteCache.cs +++ b/src/NHibernate/Cache/NonstrictReadWriteCache.cs @@ -1,4 +1,3 @@ -using System; using System.Collections; using System.Collections.Generic; using System.Linq; diff --git a/src/NHibernate/Cache/ReadWriteCache.cs b/src/NHibernate/Cache/ReadWriteCache.cs index ef3db827daf..881baafb248 100644 --- a/src/NHibernate/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Cache/ReadWriteCache.cs @@ -1,4 +1,3 @@ -using System; using System.Collections; using System.Collections.Generic; using System.Linq; From 473bf03c1067d40665dbcea2e08da64cf40a17f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Sat, 24 Nov 2018 15:24:43 +0100 Subject: [PATCH 09/10] Fix null set handling --- src/NHibernate/Cache/NonstrictReadWriteCache.cs | 2 +- src/NHibernate/Cache/ReadOnlyCache.cs | 2 +- src/NHibernate/Cache/ReadWriteCache.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NHibernate/Cache/NonstrictReadWriteCache.cs b/src/NHibernate/Cache/NonstrictReadWriteCache.cs index ca0e29ffb18..065d7189ac6 100644 --- a/src/NHibernate/Cache/NonstrictReadWriteCache.cs +++ b/src/NHibernate/Cache/NonstrictReadWriteCache.cs @@ -33,7 +33,7 @@ public ICache Cache #pragma warning restore 618 { get { return _cache; } - set { _cache = value.AsCacheBase(); } + set { _cache = value?.AsCacheBase(); } } // 6.0 TODO: make implicit and switch to auto-property diff --git a/src/NHibernate/Cache/ReadOnlyCache.cs b/src/NHibernate/Cache/ReadOnlyCache.cs index 477ac3f8625..0f71872282f 100644 --- a/src/NHibernate/Cache/ReadOnlyCache.cs +++ b/src/NHibernate/Cache/ReadOnlyCache.cs @@ -29,7 +29,7 @@ public ICache Cache #pragma warning restore 618 { get { return _cache; } - set { _cache = value.AsCacheBase(); } + set { _cache = value?.AsCacheBase(); } } // 6.0 TODO: make implicit and switch to auto-property diff --git a/src/NHibernate/Cache/ReadWriteCache.cs b/src/NHibernate/Cache/ReadWriteCache.cs index 881baafb248..1b0ca1f879f 100644 --- a/src/NHibernate/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Cache/ReadWriteCache.cs @@ -51,7 +51,7 @@ public ICache Cache #pragma warning restore 618 { get { return _cache; } - set { _cache = value.AsCacheBase(); } + set { _cache = value?.AsCacheBase(); } } // 6.0 TODO: make implicit and switch to auto-property From fcd00a9720ef4ec26e9c36bff46d07c38afae7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Tue, 11 Dec 2018 16:36:42 +0100 Subject: [PATCH 10/10] Upgrade obsolete comments --- src/NHibernate/Cache/CacheFactory.cs | 4 ++-- src/NHibernate/Cache/IQueryCacheFactory.cs | 2 +- src/NHibernate/Cache/StandardQueryCache.cs | 2 +- src/NHibernate/Cache/StandardQueryCacheFactory.cs | 2 +- src/NHibernate/Cache/UpdateTimestampsCache.cs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/NHibernate/Cache/CacheFactory.cs b/src/NHibernate/Cache/CacheFactory.cs index 6499f64b46f..b6c5df8549d 100644 --- a/src/NHibernate/Cache/CacheFactory.cs +++ b/src/NHibernate/Cache/CacheFactory.cs @@ -30,8 +30,8 @@ public static class CacheFactory /// Used to retrieve the global cache region prefix. /// Properties the cache provider can use to configure the cache. /// An to use for this object in the . - // Since v5.2 - [Obsolete("Please use overload with a CacheBase builder parameter.")] + // Since v5.3 + [Obsolete("Please use overload with a CacheBase parameter.")] public static ICacheConcurrencyStrategy CreateCache( string usage, string name, diff --git a/src/NHibernate/Cache/IQueryCacheFactory.cs b/src/NHibernate/Cache/IQueryCacheFactory.cs index 6a615f8103a..f9c828f22ae 100644 --- a/src/NHibernate/Cache/IQueryCacheFactory.cs +++ b/src/NHibernate/Cache/IQueryCacheFactory.cs @@ -10,7 +10,7 @@ namespace NHibernate.Cache /// public interface IQueryCacheFactory { - // Since v5.2 + // Since v5.3 [Obsolete("Please use extension overload with a CacheBase parameter.")] IQueryCache GetQueryCache( string regionName, diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index ae42e0fbbbc..ee361c1541c 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -22,7 +22,7 @@ public partial class StandardQueryCache : IQueryCache, IBatchableQueryCache private readonly UpdateTimestampsCache _updateTimestampsCache; private readonly CacheBase _cache; - // Since v5.2 + // Since v5.3 [Obsolete("Please use overload with a CacheBase parameter.")] public StandardQueryCache( Settings settings, diff --git a/src/NHibernate/Cache/StandardQueryCacheFactory.cs b/src/NHibernate/Cache/StandardQueryCacheFactory.cs index 8b3874aec8f..8f453468fef 100644 --- a/src/NHibernate/Cache/StandardQueryCacheFactory.cs +++ b/src/NHibernate/Cache/StandardQueryCacheFactory.cs @@ -10,7 +10,7 @@ namespace NHibernate.Cache /// public class StandardQueryCacheFactory : IQueryCacheFactory { - // Since v5.2 + // Since v5.3 [Obsolete("Please use overload with a CacheBase parameter.")] public IQueryCache GetQueryCache(string regionName, UpdateTimestampsCache updateTimestampsCache, diff --git a/src/NHibernate/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Cache/UpdateTimestampsCache.cs index b1ef75eb831..e98b30d5dc2 100644 --- a/src/NHibernate/Cache/UpdateTimestampsCache.cs +++ b/src/NHibernate/Cache/UpdateTimestampsCache.cs @@ -25,7 +25,7 @@ public virtual void Clear() _updateTimestamps.Clear(); } - // Since v5.2 + // Since v5.3 [Obsolete("Please use overload with a CacheBase parameter.")] public UpdateTimestampsCache(Settings settings, IDictionary props) : this( @@ -155,7 +155,7 @@ public virtual bool[] AreUpToDate(ISet[] spaces, long[] timestamps) return results; } - // Since v5.2 + // Since v5.3 [Obsolete("This method has no usages anymore")] public virtual void Destroy() {