diff --git a/src/AsyncGenerator.yml b/src/AsyncGenerator.yml index 47966a224ff..7c02ecf2900 100644 --- a/src/AsyncGenerator.yml +++ b/src/AsyncGenerator.yml @@ -267,8 +267,6 @@ methodRules: name: Lock - containingType: NHibernate.Cache.ICache name: Unlock - - containingType: NHibernate.Cache.IBatchableReadOnlyCache - - containingType: NHibernate.Cache.IBatchableCache name: Cache - filters: - containingNamespace: NHibernate diff --git a/src/NHibernate.Test/Async/CacheTest/CacheFixture.cs b/src/NHibernate.Test/Async/CacheTest/CacheFixture.cs index 449402f5cd1..ad8af003134 100644 --- a/src/NHibernate.Test/Async/CacheTest/CacheFixture.cs +++ b/src/NHibernate.Test/Async/CacheTest/CacheFixture.cs @@ -34,7 +34,7 @@ private CacheKey CreateCacheKey(string text) public async Task DoTestCacheAsync(ICacheProvider cacheProvider, CancellationToken cancellationToken = default(CancellationToken)) { - ICache cache = cacheProvider.BuildCache(typeof(String).FullName, new Dictionary()); + var cache = cacheProvider.BuildCache(typeof(String).FullName, new Dictionary()); long longBefore = Timestamper.Next(); @@ -141,7 +141,7 @@ private CacheKey CreateCacheKey(string text) Assert.AreEqual("baz", await (ccs.GetAsync(fooKey, longLongAfter, cancellationToken))); } - private async Task DoTestMinValueTimestampOnStrategyAsync(ICache cache, ICacheConcurrencyStrategy strategy, CancellationToken cancellationToken = default(CancellationToken)) + private async Task DoTestMinValueTimestampOnStrategyAsync(CacheBase cache, ICacheConcurrencyStrategy strategy, CancellationToken cancellationToken = default(CancellationToken)) { CacheKey key = CreateCacheKey("key"); strategy.Cache = cache; @@ -154,7 +154,7 @@ private CacheKey CreateCacheKey(string text) [Test] public async Task MinValueTimestampAsync() { - ICache cache = new HashtableCacheProvider().BuildCache("region", new Dictionary()); + var cache = new HashtableCacheProvider().BuildCache("region", new Dictionary()); ICacheConcurrencyStrategy strategy = new ReadWriteCache(); strategy.Cache = cache; @@ -163,4 +163,4 @@ public async Task MinValueTimestampAsync() await (DoTestMinValueTimestampOnStrategyAsync(cache, new ReadOnlyCache())); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/CacheTest/Caches/BatchableCache.cs b/src/NHibernate.Test/Async/CacheTest/Caches/BatchableCache.cs index 808997b9323..26bf53ad4e9 100644 --- a/src/NHibernate.Test/Async/CacheTest/Caches/BatchableCache.cs +++ b/src/NHibernate.Test/Async/CacheTest/Caches/BatchableCache.cs @@ -8,21 +8,36 @@ //------------------------------------------------------------------------------ -using System; using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using NHibernate.Cache; namespace NHibernate.Test.CacheTest.Caches { - public partial class BatchableCache : ICache, IBatchableCache + using System.Threading.Tasks; + using System.Threading; + public partial class BatchableCache : CacheBase { - public Task PutManyAsync(object[] keys, object[] values, CancellationToken cancellationToken) + public override Task GetManyAsync(object[] keys, CancellationToken cancellationToken) + { + try + { + GetMultipleCalls.Add(keys); + var result = new object[keys.Length]; + for (var i = 0; i < keys.Length; i++) + { + result[i] = _hashtable[keys[i]]; + } + return Task.FromResult(result); + } + catch (System.Exception ex) + { + return Task.FromException(ex); + } + } + + public override Task PutManyAsync(object[] keys, object[] values, CancellationToken cancellationToken) { try { @@ -33,74 +48,52 @@ public Task PutManyAsync(object[] keys, object[] values, CancellationToken cance } return Task.CompletedTask; } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } } - public Task LockManyAsync(object[] keys, CancellationToken cancellationToken) + public override Task LockManyAsync(object[] keys, CancellationToken cancellationToken) { try { LockMultipleCalls.Add(keys); return Task.FromResult(null); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } } - public Task UnlockManyAsync(object[] keys, object lockValue, CancellationToken cancellationToken) + public override Task UnlockManyAsync(object[] keys, object lockValue, CancellationToken cancellationToken) { try { UnlockMultipleCalls.Add(keys); return Task.CompletedTask; } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } } - #region ICache Members - - /// - public Task GetAsync(object key, CancellationToken cancellationToken) + public override Task GetAsync(object key, CancellationToken cancellationToken) { try { GetCalls.Add(key); return Task.FromResult(_hashtable[key]); } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } } - public Task GetManyAsync(object[] keys, CancellationToken cancellationToken) - { - try - { - GetMultipleCalls.Add(keys); - var result = new object[keys.Length]; - for (var i = 0; i < keys.Length; i++) - { - result[i] = _hashtable[keys[i]]; - } - return Task.FromResult(result); - } - catch (Exception ex) - { - return Task.FromException(ex); - } - } - - /// - public Task PutAsync(object key, object value, CancellationToken cancellationToken) + public override Task PutAsync(object key, object value, CancellationToken cancellationToken) { try { @@ -108,55 +101,48 @@ public Task PutAsync(object key, object value, CancellationToken cancellationTok _hashtable[key] = value; return Task.CompletedTask; } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } } - /// - public Task RemoveAsync(object key, CancellationToken cancellationToken) + public override Task RemoveAsync(object key, CancellationToken cancellationToken) { try { _hashtable.Remove(key); return Task.CompletedTask; } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } } - /// - /// A cancellation token that can be used to cancel the work - public Task ClearAsync(CancellationToken cancellationToken) + public override Task ClearAsync(CancellationToken cancellationToken) { try { _hashtable.Clear(); return Task.CompletedTask; } - catch (Exception ex) + catch (System.Exception ex) { return Task.FromException(ex); } } - /// - public Task LockAsync(object key, CancellationToken cancellationToken) + public override Task LockAsync(object key, CancellationToken cancellationToken) { - return Task.CompletedTask; - // local cache, so we use synchronization + // local cache, no need to actually lock. + return Task.FromResult(null); } - /// - public Task UnlockAsync(object key, CancellationToken cancellationToken) + public override Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken) { return Task.CompletedTask; - // local cache, so we use synchronization + // local cache, no need to actually lock. } - - #endregion } } diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH2898/BinaryFormatterCache.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH2898/BinaryFormatterCache.cs index cf96d323bea..5fbc3ca0ef7 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH2898/BinaryFormatterCache.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH2898/BinaryFormatterCache.cs @@ -12,16 +12,18 @@ using System.IO; using System.Runtime.Serialization.Formatters.Binary; using NHibernate.Cache; +#if !NETFX using NHibernate.Util; +#endif namespace NHibernate.Test.NHSpecificTest.NH2898 { using System.Threading.Tasks; using System.Threading; - public partial class BinaryFormatterCache : ICache + public partial class BinaryFormatterCache : CacheBase { - public Task GetAsync(object key, CancellationToken cancellationToken) + public override Task GetAsync(object key, CancellationToken cancellationToken) { try { @@ -46,7 +48,7 @@ public Task GetAsync(object key, CancellationToken cancellationToken) } } - public Task PutAsync(object key, object value, CancellationToken cancellationToken) + public override Task PutAsync(object key, object value, CancellationToken cancellationToken) { try { @@ -69,7 +71,7 @@ public Task PutAsync(object key, object value, CancellationToken cancellationTok } } - public Task RemoveAsync(object key, CancellationToken cancellationToken) + public override Task RemoveAsync(object key, CancellationToken cancellationToken) { try { @@ -82,7 +84,7 @@ public Task RemoveAsync(object key, CancellationToken cancellationToken) } } - public Task ClearAsync(CancellationToken cancellationToken) + public override Task ClearAsync(CancellationToken cancellationToken) { try { @@ -95,16 +97,16 @@ public Task ClearAsync(CancellationToken cancellationToken) } } - public Task LockAsync(object key, CancellationToken cancellationToken) + public override Task LockAsync(object key, CancellationToken cancellationToken) { - return Task.CompletedTask; // local cache, so we use synchronization + return Task.FromResult(null); } - public Task UnlockAsync(object key, CancellationToken cancellationToken) + public override Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken) { return Task.CompletedTask; // local cache, so we use synchronization } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs index fce703f4ac7..bbacc7c2ce2 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3564/FixtureByCode.cs @@ -24,10 +24,10 @@ namespace NHibernate.Test.NHSpecificTest.NH3564 { using System.Threading.Tasks; using System.Threading; - public partial class MyDummyCache : ICache + public partial class MyDummyCache : CacheBase { - public Task GetAsync(object key, CancellationToken cancellationToken) + public override Task GetAsync(object key, CancellationToken cancellationToken) { try { @@ -39,7 +39,7 @@ public Task GetAsync(object key, CancellationToken cancellationToken) } } - public Task PutAsync(object key, object value, CancellationToken cancellationToken) + public override Task PutAsync(object key, object value, CancellationToken cancellationToken) { try { @@ -52,7 +52,7 @@ public Task PutAsync(object key, object value, CancellationToken cancellationTok } } - public Task RemoveAsync(object key, CancellationToken cancellationToken) + public override Task RemoveAsync(object key, CancellationToken cancellationToken) { try { @@ -65,7 +65,7 @@ public Task RemoveAsync(object key, CancellationToken cancellationToken) } } - public Task ClearAsync(CancellationToken cancellationToken) + public override Task ClearAsync(CancellationToken cancellationToken) { try { @@ -78,13 +78,13 @@ public Task ClearAsync(CancellationToken cancellationToken) } } - public Task LockAsync(object key, CancellationToken cancellationToken) + public override Task LockAsync(object key, CancellationToken cancellationToken) { - return Task.CompletedTask; // local cache, so we use synchronization + return Task.FromResult(null); } - public Task UnlockAsync(object key, CancellationToken cancellationToken) + public override Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken) { return Task.CompletedTask; // local cache, so we use synchronization diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH720/Domain.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH720/Domain.cs index 4a24f1b5d94..cdf59cdf334 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH720/Domain.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH720/Domain.cs @@ -20,37 +20,37 @@ namespace NHibernate.Test.NHSpecificTest.NH720 using System.Threading.Tasks; using System.Threading; - public partial class FooCache : ICache + public partial class FooCache : CacheBase { - public Task GetAsync(object key, CancellationToken cancellationToken) + public override Task GetAsync(object key, CancellationToken cancellationToken) { return Task.FromResult(null); } - public Task PutAsync(object key, object value, CancellationToken cancellationToken) + public override Task PutAsync(object key, object value, CancellationToken cancellationToken) { return Task.CompletedTask; } - public Task RemoveAsync(object key, CancellationToken cancellationToken) + public override Task RemoveAsync(object key, CancellationToken cancellationToken) { return Task.CompletedTask; } - public Task ClearAsync(CancellationToken cancellationToken) + public override Task ClearAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } - public Task LockAsync(object key, CancellationToken cancellationToken) + public override Task LockAsync(object key, CancellationToken cancellationToken) { - return Task.CompletedTask; + return Task.FromResult(null); } - public Task UnlockAsync(object key, CancellationToken cancellationToken) + public override Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken) { return Task.CompletedTask; } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/BulkManipulation/NativeSQLBulkOperationsWithCache.cs b/src/NHibernate.Test/BulkManipulation/NativeSQLBulkOperationsWithCache.cs index 54b19b970aa..505ed26b7c1 100644 --- a/src/NHibernate.Test/BulkManipulation/NativeSQLBulkOperationsWithCache.cs +++ b/src/NHibernate.Test/BulkManipulation/NativeSQLBulkOperationsWithCache.cs @@ -61,14 +61,21 @@ public void SimpleNativeSQLInsert_DoesNotEvictEntireCacheWhenQuerySpacesAreAdded public class SubstituteCacheProvider : ICacheProvider { - private readonly ConcurrentDictionary> _caches = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> _caches = new ConcurrentDictionary>(); private Action _onClear; - public ICache BuildCache(string regionName, IDictionary properties) + // Since 5.2 + [Obsolete] + ICache ICacheProvider.BuildCache(string regionName, IDictionary properties) { - return _caches.GetOrAdd(regionName, x => new Lazy(() => + return BuildCache(regionName, properties); + } + + public CacheBase BuildCache(string regionName, IDictionary properties) + { + return _caches.GetOrAdd(regionName, x => new Lazy(() => { - var cache = Substitute.For(); + var cache = Substitute.For(); cache.RegionName.Returns(regionName); cache.When(c => c.Clear()).Do(c => _onClear?.Invoke(regionName)); return cache; @@ -88,14 +95,13 @@ public void Stop() { } - public ICache GetCache(string region) + public CacheBase GetCache(string region) { - Lazy cache; - _caches.TryGetValue(region, out cache); + _caches.TryGetValue(region, out var cache); return cache?.Value; } - public IEnumerable GetAllCaches() + public IEnumerable GetAllCaches() { return _caches.Values.Select(x => x.Value); } diff --git a/src/NHibernate.Test/CacheTest/CacheFixture.cs b/src/NHibernate.Test/CacheTest/CacheFixture.cs index d7ec22069cc..2c96ba341dd 100644 --- a/src/NHibernate.Test/CacheTest/CacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/CacheFixture.cs @@ -23,7 +23,7 @@ private CacheKey CreateCacheKey(string text) public void DoTestCache(ICacheProvider cacheProvider) { - ICache cache = cacheProvider.BuildCache(typeof(String).FullName, new Dictionary()); + var cache = cacheProvider.BuildCache(typeof(String).FullName, new Dictionary()); long longBefore = Timestamper.Next(); @@ -130,7 +130,7 @@ public void DoTestCache(ICacheProvider cacheProvider) Assert.AreEqual("baz", ccs.Get(fooKey, longLongAfter)); } - private void DoTestMinValueTimestampOnStrategy(ICache cache, ICacheConcurrencyStrategy strategy) + private void DoTestMinValueTimestampOnStrategy(CacheBase cache, ICacheConcurrencyStrategy strategy) { CacheKey key = CreateCacheKey("key"); strategy.Cache = cache; @@ -143,7 +143,7 @@ private void DoTestMinValueTimestampOnStrategy(ICache cache, ICacheConcurrencySt [Test] public void MinValueTimestamp() { - ICache cache = new HashtableCacheProvider().BuildCache("region", new Dictionary()); + var cache = new HashtableCacheProvider().BuildCache("region", new Dictionary()); ICacheConcurrencyStrategy strategy = new ReadWriteCache(); strategy.Cache = cache; @@ -152,4 +152,4 @@ public void MinValueTimestamp() DoTestMinValueTimestampOnStrategy(cache, new ReadOnlyCache()); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/CacheTest/Caches/BatchableCache.cs b/src/NHibernate.Test/CacheTest/Caches/BatchableCache.cs index e19985f1422..2d12940e4d7 100644 --- a/src/NHibernate.Test/CacheTest/Caches/BatchableCache.cs +++ b/src/NHibernate.Test/CacheTest/Caches/BatchableCache.cs @@ -1,16 +1,13 @@ -using System; -using System.Collections; +using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using NHibernate.Cache; namespace NHibernate.Test.CacheTest.Caches { - public partial class BatchableCache : ICache, IBatchableCache + public partial class BatchableCache : CacheBase { + public override bool PreferMultipleGet => true; + private readonly IDictionary _hashtable = new Hashtable(); public List GetMultipleCalls { get; } = new List(); @@ -25,7 +22,23 @@ public partial class BatchableCache : ICache, IBatchableCache public List PutCalls { get; } = new List(); - public void PutMany(object[] keys, object[] values) + public BatchableCache(string regionName) + { + RegionName = regionName; + } + + public override object[] GetMany(object[] keys) + { + GetMultipleCalls.Add(keys); + var result = new object[keys.Length]; + for (var i = 0; i < keys.Length; i++) + { + result[i] = _hashtable[keys[i]]; + } + return result; + } + + public override void PutMany(object[] keys, object[] values) { PutMultipleCalls.Add(keys); for (int i = 0; i < keys.Length; i++) @@ -34,57 +47,35 @@ public void PutMany(object[] keys, object[] values) } } - public object LockMany(object[] keys) + public override object LockMany(object[] keys) { LockMultipleCalls.Add(keys); return null; } - public void UnlockMany(object[] keys, object lockValue) + public override void UnlockMany(object[] keys, object lockValue) { UnlockMultipleCalls.Add(keys); } - #region ICache Members - - public BatchableCache(string regionName) - { - RegionName = regionName; - } - - /// - public object Get(object key) + public override object Get(object key) { GetCalls.Add(key); return _hashtable[key]; } - public object[] GetMany(object[] keys) - { - GetMultipleCalls.Add(keys); - var result = new object[keys.Length]; - for (var i = 0; i < keys.Length; i++) - { - result[i] = _hashtable[keys[i]]; - } - return result; - } - - /// - public void Put(object key, object value) + public override void Put(object key, object value) { PutCalls.Add(key); _hashtable[key] = value; } - /// - public void Remove(object key) + public override void Remove(object key) { _hashtable.Remove(key); } - /// - public void Clear() + public override void Clear() { _hashtable.Clear(); } @@ -99,34 +90,28 @@ public void ClearStatistics() LockMultipleCalls.Clear(); } - /// - public void Destroy() + public override void Destroy() { } - /// - public void Lock(object key) + public override object Lock(object key) { - // local cache, so we use synchronization + // local cache, no need to actually lock. + return null; } - /// - public void Unlock(object key) + public override void Unlock(object key, object lockValue) { - // local cache, so we use synchronization + // local cache, no need to actually lock. } - /// - public long NextTimestamp() + public override long NextTimestamp() { return Timestamper.Next(); } - /// - public int Timeout => Timestamper.OneMs * 60000; - - public string RegionName { get; } + public override int Timeout => Timestamper.OneMs * 60000; - #endregion + public override string RegionName { get; } } } diff --git a/src/NHibernate.Test/CacheTest/Caches/BatchableCacheProvider.cs b/src/NHibernate.Test/CacheTest/Caches/BatchableCacheProvider.cs index aef5bd5a7c6..acbc80d22af 100644 --- a/src/NHibernate.Test/CacheTest/Caches/BatchableCacheProvider.cs +++ b/src/NHibernate.Test/CacheTest/Caches/BatchableCacheProvider.cs @@ -11,7 +11,14 @@ public class BatchableCacheProvider : ICacheProvider { #region ICacheProvider Members - public ICache BuildCache(string regionName, IDictionary properties) + // Since 5.2 + [Obsolete] + ICache ICacheProvider.BuildCache(string regionName, IDictionary properties) + { + return BuildCache(regionName, properties); + } + + public CacheBase BuildCache(string regionName, IDictionary properties) { return new BatchableCache(regionName); } diff --git a/src/NHibernate.Test/CacheTest/GetQueryCacheFixture.cs b/src/NHibernate.Test/CacheTest/GetQueryCacheFixture.cs index 8e5c1e4c938..11fdcacd374 100644 --- a/src/NHibernate.Test/CacheTest/GetQueryCacheFixture.cs +++ b/src/NHibernate.Test/CacheTest/GetQueryCacheFixture.cs @@ -104,7 +104,14 @@ public LockedCache(string regionName) : base(regionName) public class LockedCacheProvider : ICacheProvider { - public ICache BuildCache(string regionName, IDictionary properties) + // Since 5.2 + [Obsolete] + ICache ICacheProvider.BuildCache(string regionName, IDictionary properties) + { + return BuildCache(regionName, properties); + } + + public CacheBase BuildCache(string regionName, IDictionary properties) { return new LockedCache(regionName); } diff --git a/src/NHibernate.Test/DebugSessionFactory.cs b/src/NHibernate.Test/DebugSessionFactory.cs index 99331bef6d9..152105d473a 100644 --- a/src/NHibernate.Test/DebugSessionFactory.cs +++ b/src/NHibernate.Test/DebugSessionFactory.cs @@ -292,7 +292,9 @@ ISession ISessionFactory.GetCurrentSession() SQLFunctionRegistry ISessionFactoryImplementor.SQLFunctionRegistry => ActualFactory.SQLFunctionRegistry; +#pragma warning disable 618 IDictionary ISessionFactoryImplementor.GetAllSecondLevelCacheRegions() +#pragma warning restore 618 { return ActualFactory.GetAllSecondLevelCacheRegions(); } @@ -354,7 +356,9 @@ IIdentifierGenerator ISessionFactoryImplementor.GetIdentifierGenerator(string ro return ActualFactory.GetIdentifierGenerator(rootEntityName); } +#pragma warning disable 618 ICache ISessionFactoryImplementor.GetSecondLevelCacheRegion(string regionName) +#pragma warning restore 618 { return ActualFactory.GetSecondLevelCacheRegion(regionName); } diff --git a/src/NHibernate.Test/NHSpecificTest/NH2898/BinaryFormatterCache.cs b/src/NHibernate.Test/NHSpecificTest/NH2898/BinaryFormatterCache.cs index de767846180..42f7b26bd09 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2898/BinaryFormatterCache.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH2898/BinaryFormatterCache.cs @@ -2,11 +2,13 @@ using System.IO; using System.Runtime.Serialization.Formatters.Binary; using NHibernate.Cache; +#if !NETFX using NHibernate.Util; +#endif namespace NHibernate.Test.NHSpecificTest.NH2898 { - public partial class BinaryFormatterCache : ICache + public partial class BinaryFormatterCache : CacheBase { private readonly IDictionary _hashtable = new Hashtable(); private readonly string _regionName; @@ -16,7 +18,9 @@ public BinaryFormatterCache(string regionName) _regionName = regionName; } - public object Get(object key) + public override bool PreferMultipleGet => false; + + public override object Get(object key) { var entry = _hashtable[key] as byte[]; if (entry == null) @@ -34,7 +38,7 @@ public object Get(object key) } } - public void Put(object key, object value) + public override void Put(object key, object value) { var fmt = new BinaryFormatter { @@ -49,43 +53,44 @@ public void Put(object key, object value) } } - public void Remove(object key) + public override void Remove(object key) { _hashtable.Remove(key); } - public void Clear() + public override void Clear() { _hashtable.Clear(); } - public void Destroy() + public override void Destroy() { } - public void Lock(object key) + public override object Lock(object key) { // local cache, so we use synchronization + return null; } - public void Unlock(object key) + public override void Unlock(object key, object lockValue) { // local cache, so we use synchronization } - public long NextTimestamp() + public override long NextTimestamp() { return Timestamper.Next(); } - public int Timeout + public override int Timeout { get { return Timestamper.OneMs*60000; } } - public string RegionName + public override string RegionName { get { return _regionName; } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH2898/BinaryFormatterCacheProvider.cs b/src/NHibernate.Test/NHSpecificTest/NH2898/BinaryFormatterCacheProvider.cs index 6c9f8357124..c02d70498bf 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH2898/BinaryFormatterCacheProvider.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH2898/BinaryFormatterCacheProvider.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NHibernate.Cache; @@ -7,7 +8,14 @@ public class BinaryFormatterCacheProvider : ICacheProvider { #region ICacheProvider Members - public ICache BuildCache(string regionName, IDictionary properties) + // Since 5.2 + [Obsolete] + ICache ICacheProvider.BuildCache(string regionName, IDictionary properties) + { + return BuildCache(regionName, properties); + } + + public CacheBase BuildCache(string regionName, IDictionary properties) { return new BinaryFormatterCache(regionName); } @@ -27,4 +35,4 @@ public void Stop() #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/NH3564/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/NH3564/FixtureByCode.cs index a264e855341..31eb142be35 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH3564/FixtureByCode.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH3564/FixtureByCode.cs @@ -12,7 +12,7 @@ namespace NHibernate.Test.NHSpecificTest.NH3564 { - public partial class MyDummyCache : ICache + public partial class MyDummyCache : CacheBase { private IDictionary hashtable = new Hashtable(); private readonly string regionName; @@ -22,51 +22,54 @@ public MyDummyCache(string regionName) this.regionName = regionName; } - public object Get(object key) + public override bool PreferMultipleGet => false; + + public override object Get(object key) { return hashtable[KeyAsString(key)]; } - public void Put(object key, object value) + public override void Put(object key, object value) { hashtable[KeyAsString(key)] = value; } - public void Remove(object key) + public override void Remove(object key) { hashtable.Remove(KeyAsString(key)); } - public void Clear() + public override void Clear() { hashtable.Clear(); } - public void Destroy() + public override void Destroy() { } - public void Lock(object key) + public override object Lock(object key) { // local cache, so we use synchronization + return null; } - public void Unlock(object key) + public override void Unlock(object key, object lockValue) { // local cache, so we use synchronization } - public long NextTimestamp() + public override long NextTimestamp() { return Timestamper.Next(); } - public int Timeout + public override int Timeout { get { return Timestamper.OneMs*60000; } } - public string RegionName + public override string RegionName { get { return regionName; } } @@ -80,7 +83,14 @@ private string KeyAsString(object key) public class MyDummyCacheProvider : ICacheProvider { - public ICache BuildCache(string regionName, IDictionary properties) + // Since 5.2 + [Obsolete] + ICache ICacheProvider.BuildCache(string regionName, IDictionary properties) + { + return BuildCache(regionName, properties); + } + + public CacheBase BuildCache(string regionName, IDictionary properties) { return new MyDummyCache(regionName); } diff --git a/src/NHibernate.Test/NHSpecificTest/NH720/Domain.cs b/src/NHibernate.Test/NHSpecificTest/NH720/Domain.cs index e69fbb64333..d7cbf756bb3 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH720/Domain.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH720/Domain.cs @@ -10,19 +10,19 @@ namespace NHibernate.Test.NHSpecificTest.NH720 public class FooCacheProvider : ICacheProvider { private static readonly ILog log; - private static Hashtable caches; + private static readonly Dictionary caches; static FooCacheProvider() { log = LogManager.GetLogger(typeof(FooCacheProvider)); - caches = new Hashtable(); + caches = new Dictionary(); } - public static ICache BuildCacheStatic(string regionName, IDictionary properties) + public static CacheBase BuildCacheStatic(string regionName, IDictionary properties) { - if (regionName != null && caches[regionName] != null) + if (regionName != null && caches.TryGetValue(regionName, out var cache)) { - return caches[regionName] as ICache; + return cache; } if (regionName == null) @@ -46,12 +46,19 @@ public static ICache BuildCacheStatic(string regionName, IDictionary properties) + // Since 5.2 + [Obsolete] + ICache ICacheProvider.BuildCache(string regionName, IDictionary properties) + { + return BuildCache(regionName, properties); + } + + public CacheBase BuildCache(string regionName, IDictionary properties) { return BuildCacheStatic(regionName, properties); } @@ -85,7 +92,7 @@ public void Stop() } } - public partial class FooCache : ICache + public partial class FooCache : CacheBase { private static readonly ILog log = LogManager.GetLogger(typeof(FooCache)); private string _region; @@ -99,7 +106,9 @@ public FooCache(string region, IDictionary properties) Configure(properties); } - public string RegionName + public override bool PreferMultipleGet => false; + + public override string RegionName { get { return _region; } } @@ -152,42 +161,43 @@ private void Configure(IDictionary props) } } - public object Get(object key) + public override object Get(object key) { return null; } - public void Put(object key, object value) + public override void Put(object key, object value) { } - public void Remove(object key) + public override void Remove(object key) { } - public void Clear() + public override void Clear() { } - public void Destroy() + public override void Destroy() { Clear(); } - public void Lock(object key) + public override object Lock(object key) { + return null; } - public void Unlock(object key) + public override void Unlock(object key, object lockValue) { } - public long NextTimestamp() + public override long NextTimestamp() { return Timestamper.Next(); } - public int Timeout + public override int Timeout { get { return Timestamper.OneMs * 60000; } // 60 seconds } @@ -262,4 +272,4 @@ public string Foo set { foo = value; } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Cache/CacheBase.cs b/src/NHibernate/Async/Cache/CacheBase.cs new file mode 100644 index 00000000000..24f78e7de7f --- /dev/null +++ b/src/NHibernate/Async/Cache/CacheBase.cs @@ -0,0 +1,161 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System; +using System.Collections; +using System.Threading; +using System.Threading.Tasks; +using NHibernate.Cache.Entry; +using NHibernate.Type; + +namespace NHibernate.Cache +{ + public abstract partial class CacheBase : + // 6.0 TODO: remove ICache +#pragma warning disable 618 + ICache +#pragma warning restore 618 + { + + #region Batch operations default implementation + + /// + /// Get multiple items from the cache. + /// + /// The keys to be retrieved from the cache. + /// A cancellation token that can be used to cancel the work + /// The cached items, matching each key of respectively. For each missed key, + /// it will contain a . + public virtual Task GetManyAsync(object[] keys, CancellationToken cancellationToken) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return InternalGetManyAsync(); + async Task InternalGetManyAsync() + { + var result = new object[keys.Length]; + for (var i = 0; i < keys.Length; i++) + { + result[i] = await (GetAsync(keys[i], cancellationToken)).ConfigureAwait(false); + } + + return result; + } + } + + /// + /// Add multiple items to the cache. + /// + /// The keys of the items. + /// The items. + /// A cancellation token that can be used to cancel the work + public virtual Task PutManyAsync(object[] keys, object[] values, CancellationToken cancellationToken) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + if (values == null) + throw new ArgumentNullException(nameof(values)); + if (keys.Length != values.Length) + throw new ArgumentException( + $"{nameof(keys)} and {nameof(values)} must have the same length. Found {keys.Length} and " + + $"{values.Length} respectively"); + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return InternalPutManyAsync(); + async Task InternalPutManyAsync() + { + + for (var i = 0; i < keys.Length; i++) + { + await (PutAsync(keys[i], values[i], cancellationToken)).ConfigureAwait(false); + } + } + } + + /// + /// Lock the items from being concurrently changed. + /// + /// The keys of the items. + /// A cancellation token that can be used to cancel the work + /// A lock object to use for unlocking the items. Can be . + /// The implementation is allowed to do nothing for non-clustered cache. + public virtual Task LockManyAsync(object[] keys, CancellationToken cancellationToken) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return InternalLockManyAsync(); + async Task InternalLockManyAsync() + { + // When delegating to the single operation lock, agreggate lock values into one. + var lockValues = new object[keys.Length]; + for (var i = 0; i < keys.Length; i++) + { + lockValues[i] = await (LockAsync(keys[i], cancellationToken)).ConfigureAwait(false); + } + + return lockValues; + } + } + + /// + /// Unlock the items that were previously locked. + /// + /// The keys of the items. + /// The lock object to use for unlocking the items, as received from . + /// A cancellation token that can be used to cancel the work + /// The implementation should do nothing if own implementation does nothing. + public virtual Task UnlockManyAsync(object[] keys, object lockValue, CancellationToken cancellationToken) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + if (!(lockValue is object[] lockValues) || lockValues.Length != keys.Length) + throw new ArgumentException( + $"When {nameof(UnlockManyAsync)} is not overriden, {nameof(lockValue)} must be an array of the lock " + + $"values resulting of sequentially single-locking each key.", + nameof(lockValue)); + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return InternalUnlockManyAsync(); + async Task InternalUnlockManyAsync() + { + for (var i = 0; i < keys.Length; i++) + { + await (UnlockAsync(keys[i], lockValues[i], cancellationToken)).ConfigureAwait(false); + } + } + } + + #endregion + #region Obsolete ICache implementation + + Task ICache.UnlockAsync(object key, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return UnlockAsync(key, null, cancellationToken); + } + + #endregion + } +} diff --git a/src/NHibernate/Async/Cache/FakeCache.cs b/src/NHibernate/Async/Cache/FakeCache.cs index 4725b4aeff8..860ffc175b2 100644 --- a/src/NHibernate/Async/Cache/FakeCache.cs +++ b/src/NHibernate/Async/Cache/FakeCache.cs @@ -12,10 +12,10 @@ namespace NHibernate.Cache { using System.Threading.Tasks; using System.Threading; - public partial class FakeCache : ICache + public partial class FakeCache : CacheBase { - public Task GetAsync(object key, CancellationToken cancellationToken) + public override Task GetAsync(object key, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -31,7 +31,7 @@ public Task GetAsync(object key, CancellationToken cancellationToken) } } - public Task PutAsync(object key, object value, CancellationToken cancellationToken) + public override Task PutAsync(object key, object value, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -48,7 +48,7 @@ public Task PutAsync(object key, object value, CancellationToken cancellationTok } } - public Task RemoveAsync(object key, CancellationToken cancellationToken) + public override Task RemoveAsync(object key, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -65,7 +65,7 @@ public Task RemoveAsync(object key, CancellationToken cancellationToken) } } - public Task ClearAsync(CancellationToken cancellationToken) + public override Task ClearAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -82,7 +82,7 @@ public Task ClearAsync(CancellationToken cancellationToken) } } - public Task LockAsync(object key, CancellationToken cancellationToken) + public override Task LockAsync(object key, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -90,8 +90,7 @@ public Task LockAsync(object key, CancellationToken cancellationToken) } try { - Lock(key); - return Task.CompletedTask; + return Task.FromResult(Lock(key)); } catch (System.Exception ex) { @@ -99,7 +98,7 @@ public Task LockAsync(object key, CancellationToken cancellationToken) } } - public Task UnlockAsync(object key, CancellationToken cancellationToken) + public override Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -107,7 +106,7 @@ public Task UnlockAsync(object key, CancellationToken cancellationToken) } try { - Unlock(key); + Unlock(key, lockValue); return Task.CompletedTask; } catch (System.Exception ex) @@ -116,4 +115,4 @@ public Task UnlockAsync(object key, CancellationToken cancellationToken) } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Cache/HashtableCache.cs b/src/NHibernate/Async/Cache/HashtableCache.cs index 97dbd81d308..801465e110f 100644 --- a/src/NHibernate/Async/Cache/HashtableCache.cs +++ b/src/NHibernate/Async/Cache/HashtableCache.cs @@ -14,13 +14,11 @@ namespace NHibernate.Cache { using System.Threading.Tasks; using System.Threading; - public partial class HashtableCache : ICache + public partial class HashtableCache : CacheBase { - #region ICache Members - - /// - public Task GetAsync(object key, CancellationToken cancellationToken) + /// + public override Task GetAsync(object key, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -36,8 +34,8 @@ public Task GetAsync(object key, CancellationToken cancellationToken) } } - /// - public Task PutAsync(object key, object value, CancellationToken cancellationToken) + /// + public override Task PutAsync(object key, object value, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -54,8 +52,8 @@ public Task PutAsync(object key, object value, CancellationToken cancellationTok } } - /// - public Task RemoveAsync(object key, CancellationToken cancellationToken) + /// + public override Task RemoveAsync(object key, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -72,9 +70,8 @@ public Task RemoveAsync(object key, CancellationToken cancellationToken) } } - /// - /// A cancellation token that can be used to cancel the work - public Task ClearAsync(CancellationToken cancellationToken) + /// + public override Task ClearAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -91,8 +88,8 @@ public Task ClearAsync(CancellationToken cancellationToken) } } - /// - public Task LockAsync(object key, CancellationToken cancellationToken) + /// + public override Task LockAsync(object key, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -100,8 +97,7 @@ public Task LockAsync(object key, CancellationToken cancellationToken) } try { - Lock(key); - return Task.CompletedTask; + return Task.FromResult(Lock(key)); } catch (System.Exception ex) { @@ -109,8 +105,8 @@ public Task LockAsync(object key, CancellationToken cancellationToken) } } - /// - public Task UnlockAsync(object key, CancellationToken cancellationToken) + /// + public override Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -118,7 +114,7 @@ public Task UnlockAsync(object key, CancellationToken cancellationToken) } try { - Unlock(key); + Unlock(key, lockValue); return Task.CompletedTask; } catch (System.Exception ex) @@ -126,7 +122,5 @@ public Task UnlockAsync(object key, CancellationToken cancellationToken) return Task.FromException(ex); } } - - #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Cache/IBatchableCache.cs b/src/NHibernate/Async/Cache/IBatchableCache.cs deleted file mode 100644 index f52892a2569..00000000000 --- a/src/NHibernate/Async/Cache/IBatchableCache.cs +++ /dev/null @@ -1,45 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by AsyncGenerator. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace NHibernate.Cache -{ - using System.Threading.Tasks; - using System.Threading; - public partial interface IBatchableCache : IBatchableReadOnlyCache - { - /// - /// Add multiple objects to the cache. - /// - /// The keys to cache. - /// The objects to cache. - /// A cancellation token that can be used to cancel the work - Task PutManyAsync(object[] keys, object[] values, CancellationToken cancellationToken); - - /// - /// Lock the objects from being changed by another thread. - /// - /// The keys to lock. - /// A cancellation token that can be used to cancel the work - /// The value that was used to lock the keys. - Task LockManyAsync(object[] keys, CancellationToken cancellationToken); - - /// - /// Unlock the objects that were previously locked. - /// - /// The keys to unlock. - /// The value that was used to lock the keys. - /// A cancellation token that can be used to cancel the work - Task UnlockManyAsync(object[] keys, object lockValue, CancellationToken cancellationToken); - } -} diff --git a/src/NHibernate/Async/Cache/IBatchableCacheConcurrencyStrategy.cs b/src/NHibernate/Async/Cache/IBatchableCacheConcurrencyStrategy.cs index 78a38e73d22..a4fa6b7ae21 100644 --- a/src/NHibernate/Async/Cache/IBatchableCacheConcurrencyStrategy.cs +++ b/src/NHibernate/Async/Cache/IBatchableCacheConcurrencyStrategy.cs @@ -20,26 +20,27 @@ namespace NHibernate.Cache public partial interface IBatchableCacheConcurrencyStrategy : ICacheConcurrencyStrategy { /// - /// Attempt to retrieve multiple objects from the Cache + /// Attempt to retrieve multiple items from the cache. /// - /// The keys (id) of the objects to get out of the Cache. - /// A timestamp prior to the transaction start time + /// The keys of the items. + /// A timestamp prior to the transaction start time. /// A cancellation token that can be used to cancel the work - /// An array of cached objects or + /// The cached items, matching each key of respectively. For each missed key, + /// it will contain a . /// Task GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken); /// - /// Attempt to cache objects, after loading them from the database. + /// Attempt to cache items, after loading them from the database. /// - /// The keys (id) of the objects to put in the Cache. - /// The objects to put in the cache. + /// The keys of the items. + /// The items. /// A timestamp prior to the transaction start time. - /// The version numbers of the objects we are putting. - /// The comparers to be used to compare version numbers + /// The version numbers of the items. + /// The comparers to be used to compare version numbers. /// Indicates that the cache should avoid a put if the item is already cached. /// A cancellation token that can be used to cancel the work - /// if the objects were successfully cached. + /// An array of boolean indicating if each item was successfully cached. /// Task PutManyAsync(CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, bool[] minimalPuts, CancellationToken cancellationToken); diff --git a/src/NHibernate/Async/Cache/IBatchableReadOnlyCache.cs b/src/NHibernate/Async/Cache/IBatchableReadOnlyCache.cs deleted file mode 100644 index 750c5df8280..00000000000 --- a/src/NHibernate/Async/Cache/IBatchableReadOnlyCache.cs +++ /dev/null @@ -1,29 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by AsyncGenerator. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace NHibernate.Cache -{ - using System.Threading.Tasks; - using System.Threading; - public partial interface IBatchableReadOnlyCache - { - /// - /// Get multiple objects from the cache. - /// - /// The keys to be retrieved from the cache. - /// A cancellation token that can be used to cancel the work - /// - Task GetManyAsync(object[] keys, CancellationToken cancellationToken); - } -} diff --git a/src/NHibernate/Async/Cache/ICache.cs b/src/NHibernate/Async/Cache/ICache.cs index c6278674da8..d2fb44f7252 100644 --- a/src/NHibernate/Async/Cache/ICache.cs +++ b/src/NHibernate/Async/Cache/ICache.cs @@ -8,6 +8,8 @@ //------------------------------------------------------------------------------ +using System; + namespace NHibernate.Cache { using System.Threading.Tasks; @@ -61,4 +63,4 @@ public partial interface ICache /// Task UnlockAsync(object key, CancellationToken cancellationToken); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Cache/ICacheConcurrencyStrategy.cs b/src/NHibernate/Async/Cache/ICacheConcurrencyStrategy.cs index 5474e15a68b..6bb04013c1e 100644 --- a/src/NHibernate/Async/Cache/ICacheConcurrencyStrategy.cs +++ b/src/NHibernate/Async/Cache/ICacheConcurrencyStrategy.cs @@ -12,6 +12,7 @@ using System.Collections; using NHibernate.Cache.Access; using NHibernate.Cache.Entry; +using NHibernate.Util; namespace NHibernate.Cache { @@ -121,6 +122,7 @@ public partial interface ICacheConcurrencyStrategy /// A cancellation token that can be used to cancel the work /// Task ClearAsync(CancellationToken cancellationToken); +#pragma warning restore 618 } internal static partial class CacheConcurrencyStrategyExtensions @@ -136,15 +138,22 @@ internal static partial class CacheConcurrencyStrategyExtensions /// public static Task GetManyAsync(this ICacheConcurrencyStrategy cache, CacheKey[] keys, long timestamp, CancellationToken cancellationToken) { - if (!(cache is IBatchableCacheConcurrencyStrategy batchableCache)) - { - throw new InvalidOperationException($"Cache concurrency strategy {cache.GetType()} does not support batching"); - } if (cancellationToken.IsCancellationRequested) { return Task.FromCanceled(cancellationToken); } - return batchableCache.GetManyAsync(keys, timestamp, cancellationToken); + try + { + // PreferMultipleGet yields false if !IBatchableCacheConcurrencyStrategy, no GetMany call should be done + // in such case. + return ReflectHelper + .CastOrThrow(cache, "batching") + .GetManyAsync(keys, timestamp, cancellationToken); + } + catch (Exception ex) + { + return Task.FromException(ex); + } } /// @@ -160,18 +169,22 @@ public static Task GetManyAsync(this ICacheConcurrencyStrategy cache, /// A cancellation token that can be used to cancel the work /// if the objects were successfully cached. /// - public static Task PutManyAsync(this ICacheConcurrencyStrategy cache, CacheKey[] keys, object[] values, long timestamp, + public static async Task PutManyAsync(this ICacheConcurrencyStrategy cache, CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, bool[] minimalPuts, CancellationToken cancellationToken) { - if (!(cache is IBatchableCacheConcurrencyStrategy batchableCache)) + cancellationToken.ThrowIfCancellationRequested(); + if (cache is IBatchableCacheConcurrencyStrategy batchableCache) { - throw new InvalidOperationException($"Cache concurrency strategy {cache.GetType()} does not support batching"); + return await (batchableCache.PutManyAsync(keys, values, timestamp, versions, versionComparers, minimalPuts, cancellationToken)).ConfigureAwait(false); } - if (cancellationToken.IsCancellationRequested) + + var result = new bool[keys.Length]; + for (var i = 0; i < keys.Length; i++) { - return Task.FromCanceled(cancellationToken); + result[i] = await (cache.PutAsync(keys[i], values[i], timestamp, versions[i], versionComparers[i], minimalPuts[i], cancellationToken)).ConfigureAwait(false); } - return batchableCache.PutManyAsync(keys, values, timestamp, versions, versionComparers, minimalPuts, cancellationToken); + + return result; } } } diff --git a/src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs b/src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs index f8a21aa2d88..5c3e51ec371 100644 --- a/src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs +++ b/src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs @@ -32,7 +32,7 @@ public async Task GetAsync(CacheKey key, long txTimestamp, CancellationT log.Debug("Cache lookup: {0}", key); } - object result = await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + object result = await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (result != null) { log.Debug("Cache hit"); @@ -44,108 +44,87 @@ public async Task GetAsync(CacheKey key, long txTimestamp, CancellationT return result; } - public Task GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken) + public async Task GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken) { - if (_batchableReadOnlyCache == null) + cancellationToken.ThrowIfCancellationRequested(); + if (log.IsDebugEnabled()) { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching get operation"); + log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); } - if (cancellationToken.IsCancellationRequested) + var results = await (_batchableCache.GetManyAsync(keys.Select(o => (object) o).ToArray(), cancellationToken)).ConfigureAwait(false); + if (!log.IsDebugEnabled()) { - return Task.FromCanceled(cancellationToken); + return results; } - return InternalGetManyAsync(); - async Task InternalGetManyAsync() + for (var i = 0; i < keys.Length; i++) { - if (log.IsDebugEnabled()) - { - log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); - } - var results = await (_batchableReadOnlyCache.GetManyAsync(keys.Select(o => (object) o).ToArray(), cancellationToken)).ConfigureAwait(false); - if (!log.IsDebugEnabled()) - { - return results; - } - for (var i = 0; i < keys.Length; i++) - { - log.Debug(results[i] != null ? $"Cache hit: {keys[i]}" : $"Cache miss: {keys[i]}"); - } - return results; + log.Debug(results[i] != null ? $"Cache hit: {keys[i]}" : $"Cache miss: {keys[i]}"); } + return results; } /// /// Add multiple items to the cache /// - public Task PutManyAsync(CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, - bool[] minimalPuts, CancellationToken cancellationToken) + public async Task PutManyAsync( + CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, + bool[] minimalPuts, CancellationToken cancellationToken) { - if (_batchableCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching operations"); - } - if (cancellationToken.IsCancellationRequested) + cancellationToken.ThrowIfCancellationRequested(); + var result = new bool[keys.Length]; + if (timestamp == long.MinValue) { - return Task.FromCanceled(cancellationToken); + // MinValue means cache is disabled + return result; } - return InternalPutManyAsync(); - async Task InternalPutManyAsync() - { - var result = new bool[keys.Length]; - if (timestamp == long.MinValue) - { - // MinValue means cache is disabled - return result; - } - var checkKeys = new List(); - var checkKeyIndexes = new List(); - for (var i = 0; i < minimalPuts.Length; i++) + var checkKeys = new List(); + var checkKeyIndexes = new List(); + for (var i = 0; i < minimalPuts.Length; i++) + { + if (minimalPuts[i]) { - if (minimalPuts[i]) - { - checkKeys.Add(keys[i]); - checkKeyIndexes.Add(i); - } + checkKeys.Add(keys[i]); + checkKeyIndexes.Add(i); } - var skipKeyIndexes = new HashSet(); - if (checkKeys.Any()) + } + var skipKeyIndexes = new HashSet(); + if (checkKeys.Any()) + { + var objects = await (_batchableCache.GetManyAsync(checkKeys.ToArray(), cancellationToken)).ConfigureAwait(false); + for (var i = 0; i < objects.Length; i++) { - var objects = await (_batchableCache.GetManyAsync(checkKeys.ToArray(), cancellationToken)).ConfigureAwait(false); - for (var i = 0; i < objects.Length; i++) + if (objects[i] != null) { - if (objects[i] != null) + if (log.IsDebugEnabled()) { - if (log.IsDebugEnabled()) - { - log.Debug("item already cached: {0}", checkKeys[i]); - } - skipKeyIndexes.Add(checkKeyIndexes[i]); + log.Debug("item already cached: {0}", checkKeys[i]); } + skipKeyIndexes.Add(checkKeyIndexes[i]); } } + } - if (skipKeyIndexes.Count == keys.Length) - { - return result; - } + if (skipKeyIndexes.Count == keys.Length) + { + return result; + } - var putKeys = new object[keys.Length - skipKeyIndexes.Count]; - var putValues = new object[putKeys.Length]; - var j = 0; - for (var i = 0; i < keys.Length; i++) + var putKeys = new object[keys.Length - skipKeyIndexes.Count]; + var putValues = new object[putKeys.Length]; + var j = 0; + for (var i = 0; i < keys.Length; i++) + { + if (skipKeyIndexes.Contains(i)) { - if (skipKeyIndexes.Contains(i)) - { - continue; - } - putKeys[j] = keys[i]; - putValues[j++] = values[i]; - result[i] = true; + continue; } - await (_batchableCache.PutManyAsync(putKeys, putValues, cancellationToken)).ConfigureAwait(false); - return result; + putKeys[j] = keys[i]; + putValues[j++] = values[i]; + result[i] = true; } + await (_batchableCache.PutManyAsync(putKeys, putValues, cancellationToken)).ConfigureAwait(false); + return result; } /// @@ -161,7 +140,7 @@ public async Task PutAsync(CacheKey key, object value, long txTimestamp, o return false; } - if (minimalPut && await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false) != null) + if (minimalPut && await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false) != null) { if (log.IsDebugEnabled()) { @@ -173,7 +152,7 @@ public async Task PutAsync(CacheKey key, object value, long txTimestamp, o { log.Debug("Caching: {0}", key); } - await (cache.PutAsync(key, value, cancellationToken)).ConfigureAwait(false); + await (Cache.PutAsync(key, value, cancellationToken)).ConfigureAwait(false); return true; } @@ -208,7 +187,7 @@ public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken) { log.Debug("Removing: {0}", key); } - return cache.RemoveAsync(key, cancellationToken); + return Cache.RemoveAsync(key, cancellationToken); } catch (Exception ex) { @@ -228,7 +207,7 @@ public Task ClearAsync(CancellationToken cancellationToken) { log.Debug("Clearing"); } - return cache.ClearAsync(cancellationToken); + return Cache.ClearAsync(cancellationToken); } catch (Exception ex) { @@ -251,7 +230,7 @@ public Task EvictAsync(CacheKey key, CancellationToken cancellationToken) { log.Debug("Invalidating: {0}", key); } - return cache.RemoveAsync(key, cancellationToken); + return Cache.RemoveAsync(key, cancellationToken); } catch (Exception ex) { @@ -285,7 +264,7 @@ public Task ReleaseAsync(CacheKey key, ISoftLock @lock, CancellationToken cancel log.Debug("Invalidating (again): {0}", key); } - return cache.RemoveAsync(key, cancellationToken); + return Cache.RemoveAsync(key, cancellationToken); } catch (Exception ex) { diff --git a/src/NHibernate/Async/Cache/ObsoleteCacheWrapper.cs b/src/NHibernate/Async/Cache/ObsoleteCacheWrapper.cs new file mode 100644 index 00000000000..a0700858c32 --- /dev/null +++ b/src/NHibernate/Async/Cache/ObsoleteCacheWrapper.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +namespace NHibernate.Cache +{ + using System.Threading.Tasks; + using System.Threading; + internal partial class ObsoleteCacheWrapper : CacheBase + { + + public override Task GetAsync(object key, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return _cache.GetAsync(key, cancellationToken); + } + + public override Task PutAsync(object key, object value, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return _cache.PutAsync(key, value, cancellationToken); + } + + public override Task RemoveAsync(object key, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return _cache.RemoveAsync(key, cancellationToken); + } + + public override Task ClearAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return _cache.ClearAsync(cancellationToken); + } + + public override async Task LockAsync(object key, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + await (_cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); + return null; + } + + public override Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + return _cache.UnlockAsync(key, cancellationToken); + } + } +} diff --git a/src/NHibernate/Async/Cache/ReadOnlyCache.cs b/src/NHibernate/Async/Cache/ReadOnlyCache.cs index 14530333b71..a838890b661 100644 --- a/src/NHibernate/Async/Cache/ReadOnlyCache.cs +++ b/src/NHibernate/Async/Cache/ReadOnlyCache.cs @@ -24,7 +24,7 @@ public partial class ReadOnlyCache : IBatchableCacheConcurrencyStrategy public async Task GetAsync(CacheKey key, long timestamp, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - object result = await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + object result = await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (result != null && log.IsDebugEnabled()) { log.Debug("Cache hit: {0}", key); @@ -32,34 +32,23 @@ public async Task GetAsync(CacheKey key, long timestamp, CancellationTok return result; } - public Task GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken) + public async Task GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken) { - if (_batchableReadOnlyCache == null) + cancellationToken.ThrowIfCancellationRequested(); + if (log.IsDebugEnabled()) { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching get operation"); + log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); } - if (cancellationToken.IsCancellationRequested) + var results = await (_batchableCache.GetManyAsync(keys.Select(o => (object) o).ToArray(), cancellationToken)).ConfigureAwait(false); + if (!log.IsDebugEnabled()) { - return Task.FromCanceled(cancellationToken); + return results; } - return InternalGetManyAsync(); - async Task InternalGetManyAsync() + for (var i = 0; i < keys.Length; i++) { - if (log.IsDebugEnabled()) - { - log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); - } - var results = await (_batchableReadOnlyCache.GetManyAsync(keys.Select(o => (object) o).ToArray(), cancellationToken)).ConfigureAwait(false); - if (!log.IsDebugEnabled()) - { - return results; - } - for (var i = 0; i < keys.Length; i++) - { - log.Debug(results[i] != null ? $"Cache hit: {keys[i]}" : $"Cache miss: {keys[i]}"); - } - return results; + log.Debug(results[i] != null ? $"Cache hit: {keys[i]}" : $"Cache miss: {keys[i]}"); } + return results; } /// @@ -81,75 +70,65 @@ public Task LockAsync(CacheKey key, object version, CancellationToken } } - public Task PutManyAsync(CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, - bool[] minimalPuts, CancellationToken cancellationToken) + public async Task PutManyAsync( + CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, + bool[] minimalPuts, CancellationToken cancellationToken) { - if (_batchableCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching operations"); - } - if (cancellationToken.IsCancellationRequested) + cancellationToken.ThrowIfCancellationRequested(); + var result = new bool[keys.Length]; + if (timestamp == long.MinValue) { - return Task.FromCanceled(cancellationToken); + // MinValue means cache is disabled + return result; } - return InternalPutManyAsync(); - async Task InternalPutManyAsync() + + var checkKeys = new List(); + var checkKeyIndexes = new List(); + for (var i = 0; i < minimalPuts.Length; i++) { - var result = new bool[keys.Length]; - if (timestamp == long.MinValue) + if (minimalPuts[i]) { - // MinValue means cache is disabled - return result; + checkKeys.Add(keys[i]); + checkKeyIndexes.Add(i); } - - var checkKeys = new List(); - var checkKeyIndexes = new List(); - for (var i = 0; i < minimalPuts.Length; i++) - { - if (minimalPuts[i]) - { - checkKeys.Add(keys[i]); - checkKeyIndexes.Add(i); - } - } - var skipKeyIndexes = new HashSet(); - if (checkKeys.Any()) + } + var skipKeyIndexes = new HashSet(); + if (checkKeys.Any()) + { + var objects = await (_batchableCache.GetManyAsync(checkKeys.Select(o => (object) o).ToArray(), cancellationToken)).ConfigureAwait(false); + for (var i = 0; i < objects.Length; i++) { - var objects = await (_batchableCache.GetManyAsync(checkKeys.Select(o => (object) o).ToArray(), cancellationToken)).ConfigureAwait(false); - for (var i = 0; i < objects.Length; i++) + if (objects[i] != null) { - if (objects[i] != null) + if (log.IsDebugEnabled()) { - if (log.IsDebugEnabled()) - { - log.Debug("item already cached: {0}", checkKeys[i]); - } - skipKeyIndexes.Add(checkKeyIndexes[i]); + log.Debug("item already cached: {0}", checkKeys[i]); } + skipKeyIndexes.Add(checkKeyIndexes[i]); } } + } - if (skipKeyIndexes.Count == keys.Length) - { - return result; - } + if (skipKeyIndexes.Count == keys.Length) + { + return result; + } - var putKeys = new object[keys.Length - skipKeyIndexes.Count]; - var putValues = new object[putKeys.Length]; - var j = 0; - for (var i = 0; i < keys.Length; i++) + var putKeys = new object[keys.Length - skipKeyIndexes.Count]; + var putValues = new object[putKeys.Length]; + var j = 0; + for (var i = 0; i < keys.Length; i++) + { + if (skipKeyIndexes.Contains(i)) { - if (skipKeyIndexes.Contains(i)) - { - continue; - } - putKeys[j] = keys[i]; - putValues[j++] = values[i]; - result[i] = true; + continue; } - await (_batchableCache.PutManyAsync(putKeys, putValues, cancellationToken)).ConfigureAwait(false); - return result; + putKeys[j] = keys[i]; + putValues[j++] = values[i]; + result[i] = true; } + await (_batchableCache.PutManyAsync(putKeys, putValues, cancellationToken)).ConfigureAwait(false); + return result; } public async Task PutAsync(CacheKey key, object value, long timestamp, object version, IComparer versionComparator, @@ -162,7 +141,7 @@ public async Task PutAsync(CacheKey key, object value, long timestamp, obj return false; } - if (minimalPut && await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false) != null) + if (minimalPut && await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false) != null) { if (log.IsDebugEnabled()) { @@ -174,7 +153,7 @@ public async Task PutAsync(CacheKey key, object value, long timestamp, obj { log.Debug("Caching: {0}", key); } - await (cache.PutAsync(key, value, cancellationToken)).ConfigureAwait(false); + await (Cache.PutAsync(key, value, cancellationToken)).ConfigureAwait(false); return true; } @@ -204,7 +183,7 @@ public Task ClearAsync(CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - return cache.ClearAsync(cancellationToken); + return Cache.ClearAsync(cancellationToken); } public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken) @@ -213,7 +192,7 @@ public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - return cache.RemoveAsync(key, cancellationToken); + return Cache.RemoveAsync(key, cancellationToken); } /// diff --git a/src/NHibernate/Async/Cache/ReadWriteCache.cs b/src/NHibernate/Async/Cache/ReadWriteCache.cs index 4c83d96b122..5a2f20356eb 100644 --- a/src/NHibernate/Async/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Async/Cache/ReadWriteCache.cs @@ -54,7 +54,7 @@ public async Task GetAsync(CacheKey key, long txTimestamp, CancellationT { cache.Lock( key );*/ - ILockable lockable = (ILockable) await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + ILockable lockable = (ILockable) await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); bool gettable = lockable != null && lockable.IsGettable(txTimestamp); @@ -90,51 +90,40 @@ public async Task GetAsync(CacheKey key, long txTimestamp, CancellationT } } - public Task GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken) + public async Task GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken) { - if (_batchableReadOnlyCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching get operation"); - } - if (cancellationToken.IsCancellationRequested) + cancellationToken.ThrowIfCancellationRequested(); + if (log.IsDebugEnabled()) { - return Task.FromCanceled(cancellationToken); + log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); } - return InternalGetManyAsync(); - async Task InternalGetManyAsync() + var result = new object[keys.Length]; + using (await _lockObjectAsync.LockAsync()) { - if (log.IsDebugEnabled()) - { - log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); - } - var result = new object[keys.Length]; - using (await _lockObjectAsync.LockAsync()) + var lockables = await (_cacheBase.GetManyAsync(keys.Select(o => (object) o).ToArray(), cancellationToken)).ConfigureAwait(false); + for (var i = 0; i < lockables.Length; i++) { - var lockables = await (_batchableReadOnlyCache.GetManyAsync(keys.Select(o => (object) o).ToArray(), cancellationToken)).ConfigureAwait(false); - for (var i = 0; i < lockables.Length; i++) - { - var lockable = (ILockable) lockables[i]; - var gettable = lockable != null && lockable.IsGettable(timestamp); - - if (gettable) - { - if (log.IsDebugEnabled()) - { - log.Debug("Cache hit: {0}", keys[i]); - } - result[i] = ((CachedItem) lockable).Value; - } + var lockable = (ILockable) lockables[i]; + var gettable = lockable != null && lockable.IsGettable(timestamp); + if (gettable) + { if (log.IsDebugEnabled()) { - log.Debug(lockable == null ? "Cache miss: {0}" : "Cached item was locked: {0}", keys[i]); + log.Debug("Cache hit: {0}", keys[i]); } + result[i] = ((CachedItem) lockable).Value; + } - result[i] = null; + if (log.IsDebugEnabled()) + { + log.Debug(lockable == null ? "Cache miss: {0}" : "Cached item was locked: {0}", keys[i]); } + + result[i] = null; } - return result; } + return result; } /// @@ -154,21 +143,20 @@ public async Task LockAsync(CacheKey key, object version, Cancellatio log.Debug("Invalidating: {0}", key); } + var lockValue = await (_cacheBase.LockAsync(key, cancellationToken)).ConfigureAwait(false); try { - await (cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); - - ILockable lockable = (ILockable) await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); - long timeout = cache.NextTimestamp() + cache.Timeout; + ILockable lockable = (ILockable) await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + long timeout = Cache.NextTimestamp() + Cache.Timeout; CacheLock @lock = lockable == null ? new CacheLock(timeout, NextLockId(), version) : lockable.Lock(timeout, NextLockId()); - await (cache.PutAsync(key, @lock, cancellationToken)).ConfigureAwait(false); + await (Cache.PutAsync(key, @lock, cancellationToken)).ConfigureAwait(false); return @lock; } finally { - await (cache.UnlockAsync(key, cancellationToken)).ConfigureAwait(false); + await (_cacheBase.UnlockAsync(key, lockValue, cancellationToken)).ConfigureAwait(false); } } } @@ -180,91 +168,74 @@ public async Task LockAsync(CacheKey key, object version, Cancellatio /// database is operating in repeatable read isolation mode.) /// /// Whether the items were actually put into the cache - public Task PutManyAsync(CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, - bool[] minimalPuts, CancellationToken cancellationToken) + public async Task PutManyAsync( + CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, + bool[] minimalPuts, CancellationToken cancellationToken) { - if (_batchableCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching operations"); - } - if (cancellationToken.IsCancellationRequested) + cancellationToken.ThrowIfCancellationRequested(); + var result = new bool[keys.Length]; + if (timestamp == long.MinValue) { - return Task.FromCanceled(cancellationToken); + // MinValue means cache is disabled + return result; } - return InternalPutManyAsync(); - async Task InternalPutManyAsync() - { - var result = new bool[keys.Length]; - if (timestamp == long.MinValue) + using (await _lockObjectAsync.LockAsync()) + { + if (log.IsDebugEnabled()) { - // MinValue means cache is disabled - return result; + log.Debug("Caching: {0}", string.Join(",", keys.AsEnumerable())); } - - using (await _lockObjectAsync.LockAsync()) + var keysArr = keys.Cast().ToArray(); + var lockValue = await (_cacheBase.LockManyAsync(keysArr, cancellationToken)).ConfigureAwait(false);; + try { - if (log.IsDebugEnabled()) - { - log.Debug("Caching: {0}", string.Join(",", keys.AsEnumerable())); - } - var keysArr = keys.Cast().ToArray(); - var lockAquired = false; - object lockValue = null; - try + var putBatch = new Dictionary(); + var lockables = await (_cacheBase.GetManyAsync(keysArr, cancellationToken)).ConfigureAwait(false); + for (var i = 0; i < keys.Length; i++) { - lockValue = await (_batchableCache.LockManyAsync(keysArr, cancellationToken)).ConfigureAwait(false); - lockAquired = true; - var putBatch = new Dictionary(); - var lockables = await (_batchableCache.GetManyAsync(keysArr, cancellationToken)).ConfigureAwait(false); - for (var i = 0; i < keys.Length; i++) - { - var key = keys[i]; - var version = versions[i]; - var lockable = (ILockable) lockables[i]; - bool puttable = lockable == null || + var key = keys[i]; + var version = versions[i]; + var lockable = (ILockable) lockables[i]; + bool puttable = lockable == null || lockable.IsPuttable(timestamp, version, versionComparers[i]); - if (puttable) + if (puttable) + { + putBatch.Add(key, new CachedItem(values[i], cache.NextTimestamp(), version)); + if (log.IsDebugEnabled()) { - putBatch.Add(key, new CachedItem(values[i], cache.NextTimestamp(), version)); - if (log.IsDebugEnabled()) - { - log.Debug("Cached: {0}", key); - } - result[i] = true; + log.Debug("Cached: {0}", key); } - else + result[i] = true; + } + else + { + if (log.IsDebugEnabled()) { - if (log.IsDebugEnabled()) + if (lockable.IsLock) { - if (lockable.IsLock) - { - log.Debug("Item was locked: {0}", key); - } - else - { - log.Debug("Item was already cached: {0}", key); - } + log.Debug("Item was locked: {0}", key); + } + else + { + log.Debug("Item was already cached: {0}", key); } - result[i] = false; } - } - - if (putBatch.Count > 0) - { - await (_batchableCache.PutManyAsync(putBatch.Keys.ToArray(), putBatch.Values.ToArray(), cancellationToken)).ConfigureAwait(false); + result[i] = false; } } - finally + + if (putBatch.Count > 0) { - if (lockAquired) - { - await (_batchableCache.UnlockManyAsync(keysArr, lockValue, cancellationToken)).ConfigureAwait(false); - } + await (_cacheBase.PutManyAsync(putBatch.Keys.ToArray(), putBatch.Values.ToArray(), cancellationToken)).ConfigureAwait(false); } } - return result; + finally + { + await (_cacheBase.UnlockManyAsync(keysArr, lockValue, cancellationToken)).ConfigureAwait(false); + } } + return result; } /// @@ -291,18 +262,17 @@ public async Task PutAsync(CacheKey key, object value, long txTimestamp, o log.Debug("Caching: {0}", key); } + var lockValue = await (_cacheBase.LockAsync(key, cancellationToken)).ConfigureAwait(false); try { - await (cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); - - ILockable lockable = (ILockable) await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + ILockable lockable = (ILockable) await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); bool puttable = lockable == null || lockable.IsPuttable(txTimestamp, version, versionComparator); if (puttable) { - await (cache.PutAsync(key, new CachedItem(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); + await (Cache.PutAsync(key, new CachedItem(value, Cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); if (log.IsDebugEnabled()) { log.Debug("Cached: {0}", key); @@ -327,7 +297,7 @@ public async Task PutAsync(CacheKey key, object value, long txTimestamp, o } finally { - await (cache.UnlockAsync(key, cancellationToken)).ConfigureAwait(false); + await (_cacheBase.UnlockAsync(key, lockValue, cancellationToken)).ConfigureAwait(false); } } } @@ -344,8 +314,8 @@ private Task DecrementLockAsync(object key, CacheLock @lock, CancellationToken c try { //decrement the lock - @lock.Unlock(cache.NextTimestamp()); - return cache.PutAsync(key, @lock, cancellationToken); + @lock.Unlock(Cache.NextTimestamp()); + return Cache.PutAsync(key, @lock, cancellationToken); } catch (Exception ex) { @@ -363,11 +333,10 @@ public async Task ReleaseAsync(CacheKey key, ISoftLock clientLock, CancellationT log.Debug("Releasing: {0}", key); } + var lockValue = await (_cacheBase.LockAsync(key, cancellationToken)).ConfigureAwait(false); try { - await (cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); - - ILockable lockable = (ILockable) await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + ILockable lockable = (ILockable) await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (IsUnlockable(clientLock, lockable)) { await (DecrementLockAsync(key, (CacheLock) lockable, cancellationToken)).ConfigureAwait(false); @@ -379,7 +348,7 @@ public async Task ReleaseAsync(CacheKey key, ISoftLock clientLock, CancellationT } finally { - await (cache.UnlockAsync(key, cancellationToken)).ConfigureAwait(false); + await (_cacheBase.UnlockAsync(key, lockValue, cancellationToken)).ConfigureAwait(false); } } } @@ -393,11 +362,11 @@ internal Task HandleLockExpiryAsync(object key, CancellationToken cancellationTo try { log.Warn("An item was expired by the cache while it was locked (increase your cache timeout): {0}", key); - long ts = cache.NextTimestamp() + cache.Timeout; + long ts = Cache.NextTimestamp() + Cache.Timeout; // create new lock that times out immediately CacheLock @lock = new CacheLock(ts, NextLockId(), null); @lock.Unlock(ts); - return cache.PutAsync(key, @lock, cancellationToken); + return Cache.PutAsync(key, @lock, cancellationToken); } catch (Exception ex) { @@ -411,7 +380,7 @@ public Task ClearAsync(CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - return cache.ClearAsync(cancellationToken); + return Cache.ClearAsync(cancellationToken); } public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken) @@ -420,7 +389,7 @@ public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - return cache.RemoveAsync(key, cancellationToken); + return Cache.RemoveAsync(key, cancellationToken); } /// @@ -437,11 +406,10 @@ public async Task AfterUpdateAsync(CacheKey key, object value, object vers log.Debug("Updating: {0}", key); } + var lockValue = await (_cacheBase.LockAsync(key, cancellationToken)).ConfigureAwait(false); try { - await (cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); - - ILockable lockable = (ILockable) await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + ILockable lockable = (ILockable) await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (IsUnlockable(clientLock, lockable)) { CacheLock @lock = (CacheLock) lockable; @@ -454,7 +422,7 @@ public async Task AfterUpdateAsync(CacheKey key, object value, object vers else { //recache the updated state - await (cache.PutAsync(key, new CachedItem(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); + await (Cache.PutAsync(key, new CachedItem(value, Cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); if (log.IsDebugEnabled()) { log.Debug("Updated: {0}", key); @@ -470,7 +438,7 @@ public async Task AfterUpdateAsync(CacheKey key, object value, object vers } finally { - await (cache.UnlockAsync(key, cancellationToken)).ConfigureAwait(false); + await (_cacheBase.UnlockAsync(key, lockValue, cancellationToken)).ConfigureAwait(false); } } } @@ -485,14 +453,14 @@ public async Task AfterInsertAsync(CacheKey key, object value, object vers log.Debug("Inserting: {0}", key); } + var lockValue = await (_cacheBase.LockAsync(key, cancellationToken)).ConfigureAwait(false); try { - await (cache.LockAsync(key, cancellationToken)).ConfigureAwait(false); - - ILockable lockable = (ILockable) await (cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + + ILockable lockable = (ILockable) await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (lockable == null) { - await (cache.PutAsync(key, new CachedItem(value, cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); + await (Cache.PutAsync(key, new CachedItem(value, Cache.NextTimestamp(), version), cancellationToken)).ConfigureAwait(false); if (log.IsDebugEnabled()) { log.Debug("Inserted: {0}", key); @@ -506,7 +474,7 @@ public async Task AfterInsertAsync(CacheKey key, object value, object vers } finally { - await (cache.UnlockAsync(key, cancellationToken)).ConfigureAwait(false); + await (_cacheBase.UnlockAsync(key, lockValue, cancellationToken)).ConfigureAwait(false); } } } diff --git a/src/NHibernate/Async/Cache/StandardQueryCache.cs b/src/NHibernate/Async/Cache/StandardQueryCache.cs index a85a0916311..09d54586410 100644 --- a/src/NHibernate/Async/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Async/Cache/StandardQueryCache.cs @@ -32,7 +32,7 @@ public Task ClearAsync(CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - return _queryCache.ClearAsync(cancellationToken); + return Cache.ClearAsync(cancellationToken); } public async Task PutAsync(QueryKey key, ICacheAssembler[] returnTypes, IList result, bool isNaturalKeyLookup, ISessionImplementor session, CancellationToken cancellationToken) @@ -59,7 +59,7 @@ public async Task PutAsync(QueryKey key, ICacheAssembler[] returnTypes, IL } } - await (_queryCache.PutAsync(key, cacheable, cancellationToken)).ConfigureAwait(false); + await (Cache.PutAsync(key, cacheable, cancellationToken)).ConfigureAwait(false); return true; } @@ -70,7 +70,7 @@ public async Task GetAsync(QueryKey key, ICacheAssembler[] returnTypes, b if (Log.IsDebugEnabled()) Log.Debug("checking cached query results in region: '{0}'; {1}", _regionName, key); - var cacheable = (IList)await (_queryCache.GetAsync(key, cancellationToken)).ConfigureAwait(false); + var cacheable = (IList)await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false); if (cacheable == null) { Log.Debug("query results were not found in cache: {0}", key); @@ -132,7 +132,7 @@ public async Task GetAsync(QueryKey key, ICacheAssembler[] returnTypes, b // the UnresolvableObjectException could occur while resolving // associations, leaving the PC in an inconsistent state Log.Debug(ex, "could not reassemble cached result set"); - await (_queryCache.RemoveAsync(key, cancellationToken)).ConfigureAwait(false); + await (Cache.RemoveAsync(key, cancellationToken)).ConfigureAwait(false); return null; } diff --git a/src/NHibernate/Async/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Async/Cache/UpdateTimestampsCache.cs index 843160a1824..ba978e1aea9 100644 --- a/src/NHibernate/Async/Cache/UpdateTimestampsCache.cs +++ b/src/NHibernate/Async/Cache/UpdateTimestampsCache.cs @@ -114,33 +114,19 @@ public virtual async Task IsUpToDateAsync(ISet spaces, long timest cancellationToken.ThrowIfCancellationRequested(); using (await _isUpToDate.LockAsync()) { - if (_batchUpdateTimestamps != null) + var keys = new object[spaces.Count]; + var index = 0; + foreach (var space in spaces) { - var keys = new object[spaces.Count]; - var index = 0; - foreach (var space in spaces) - { - keys[index++] = space; - } - var lastUpdates = await (_batchUpdateTimestamps.GetManyAsync(keys, cancellationToken)).ConfigureAwait(false); - foreach (var lastUpdate in lastUpdates) - { - if (IsOutdated(lastUpdate, timestamp)) - { - return false; - } - } - return true; + keys[index++] = space; } - - foreach (string space in spaces) + var lastUpdates = await (_batchUpdateTimestamps.GetManyAsync(keys, cancellationToken)).ConfigureAwait(false); + foreach (var lastUpdate in lastUpdates) { - object lastUpdate = await (updateTimestamps.GetAsync(space, cancellationToken)).ConfigureAwait(false); if (IsOutdated(lastUpdate, timestamp)) { return false; } - } return true; } diff --git a/src/NHibernate/Async/Engine/BatchFetchQueue.cs b/src/NHibernate/Async/Engine/BatchFetchQueue.cs index 66ff2292419..de53cf605a3 100644 --- a/src/NHibernate/Async/Engine/BatchFetchQueue.cs +++ b/src/NHibernate/Async/Engine/BatchFetchQueue.cs @@ -9,14 +9,12 @@ using System; -using System.Collections; using NHibernate.Cache; using NHibernate.Collection; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.Util; using System.Collections.Generic; -using System.Linq; using Iesi.Collections.Generic; namespace NHibernate.Engine @@ -66,7 +64,7 @@ internal async Task GetCollectionBatchAsync(ICollectionPersister colle // List of collection entries that haven't been checked for their existance in the cache. Besides the collection entry, // the index where the entry was found is also stored in order to correctly order the returning keys. var collectionKeys = new List, int>>(batchSize); - var batchableCache = collectionPersister.Cache?.Cache as IBatchableReadOnlyCache; + var batchableCache = collectionPersister.Cache?.Cache as CacheBase; if (!batchLoadableCollections.TryGetValue(collectionPersister.Role, out var map)) { @@ -258,7 +256,8 @@ internal async Task GetEntityBatchAsync(IEntityPersister persister, ob // List of entity keys that haven't been checked for their existance in the cache. Besides the entity key, // the index where the key was found is also stored in order to correctly order the returning keys. var entityKeys = new List>(batchSize); - var batchableCache = persister.Cache?.Cache as IBatchableReadOnlyCache; + // If there is a cache, obsolete or not, batchableCache will not be null. + var batchableCache = persister.Cache?.GetCacheBase(); if (!batchLoadableEntityKeys.TryGetValue(persister.EntityName, out var set)) { @@ -327,12 +326,12 @@ async Task CheckCacheAndProcessResultAsync() return false; } - async Task ProcessKeyAsync(EntityKey key, bool ignoreCache = false) + Task ProcessKeyAsync(EntityKey key, bool ignoreCache = false) { //TODO: this needn't exclude subclasses... if (checkForEnd && (index >= idIndex.Value + batchSize || index == set.Count)) { - return true; + return Task.FromResult(true); } if (persister.IdentifierType.IsEqual(id, key.Identifier)) { @@ -343,13 +342,13 @@ async Task ProcessKeyAsync(EntityKey key, bool ignoreCache = false) if (!idIndex.HasValue || index < idIndex.Value) { entityKeys.Add(new KeyValuePair(key, index)); - return false; + return Task.FromResult(false); } - if (!checkCache || !await (IsCachedAsync(key, persister, cancellationToken)).ConfigureAwait(false)) - { - ids[i++] = key.Identifier; - } + // No need to check "!checkCache || !IsCached(key, persister)": "batchableCache == null" + // already means there is no cache, so IsCached can only yield false. (This method is now + // removed.) + ids[i++] = key.Identifier; } else if (ignoreCache) { @@ -362,9 +361,9 @@ async Task ProcessKeyAsync(EntityKey key, bool ignoreCache = false) // that are after the demanded key. if (!idIndex.HasValue || index < idIndex.Value + batchSize) { - return false; + return Task.FromResult(false); } - return await (CheckCacheAndProcessResultAsync()).ConfigureAwait(false); + return CheckCacheAndProcessResultAsync(); } if (i == batchSize) { @@ -372,24 +371,13 @@ async Task ProcessKeyAsync(EntityKey key, bool ignoreCache = false) if (idIndex.HasValue) { checkForEnd = true; - return index >= idIndex.Value + batchSize || index == set.Count; + return Task.FromResult(index >= idIndex.Value + batchSize || index == set.Count); } } - return false; + return Task.FromResult(false); } } - private async Task IsCachedAsync(EntityKey entityKey, IEntityPersister persister, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - if (persister.HasCache && context.Session.CacheMode.HasFlag(CacheMode.Get)) - { - CacheKey key = context.Session.GenerateCacheKey(entityKey.Identifier, persister.IdentifierType, entityKey.EntityName); - return await (persister.Cache.Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false) != null; - } - return false; - } - private async Task IsCachedAsync(object collectionKey, ICollectionPersister persister, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -412,7 +400,7 @@ private async Task IsCachedAsync(object collectionKey, ICollectionPersiste /// A cancellation token that can be used to cancel the work /// An array of booleans that contains the result for each key. private async Task AreCachedAsync(List> entityKeys, int[] keyIndexes, IEntityPersister persister, - IBatchableReadOnlyCache batchableCache, bool checkCache, CancellationToken cancellationToken) + CacheBase batchableCache, bool checkCache, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var result = new bool[keyIndexes.Length]; @@ -450,7 +438,7 @@ private async Task AreCachedAsync(List> ent /// A cancellation token that can be used to cancel the work /// An array of booleans that contains the result for each key. private async Task AreCachedAsync(List, int>> collectionKeys, - int[] keyIndexes, ICollectionPersister persister, IBatchableReadOnlyCache batchableCache, + int[] keyIndexes, ICollectionPersister persister, CacheBase batchableCache, bool checkCache, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs index 1b73bc3322f..f6cbf3fd8d0 100644 --- a/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Async/Engine/Loading/CollectionLoadContext.cs @@ -229,7 +229,7 @@ private async Task AddCollectionToCacheAsync(LoadingCollectionEntry lce, ICollec CollectionCacheEntry entry = new CollectionCacheEntry(lce.Collection, persister); CacheKey cacheKey = session.GenerateCacheKey(lce.Key, persister.KeyType, persister.Role); - if (persister.GetBatchSize() > 1 && persister.Cache.IsBatchingPutSupported()) + if (persister.GetBatchSize() > 1) { cacheBatchingHandler( new CachePutData( diff --git a/src/NHibernate/Async/Engine/TwoPhaseLoad.cs b/src/NHibernate/Async/Engine/TwoPhaseLoad.cs index dda0ea12e0e..e7f7762d015 100644 --- a/src/NHibernate/Async/Engine/TwoPhaseLoad.cs +++ b/src/NHibernate/Async/Engine/TwoPhaseLoad.cs @@ -113,7 +113,7 @@ internal static async Task InitializeEntityAsync(object entity, bool readOnly, I new CacheEntry(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity); CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - if (cacheBatchingHandler != null && persister.IsBatchLoadable && persister.Cache.IsBatchingPutSupported()) + if (cacheBatchingHandler != null && persister.IsBatchLoadable) { cacheBatchingHandler( persister, diff --git a/src/NHibernate/Async/Event/Default/DefaultInitializeCollectionEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultInitializeCollectionEventListener.cs index 4e71de9ca1d..fb5daca044c 100644 --- a/src/NHibernate/Async/Event/Default/DefaultInitializeCollectionEventListener.cs +++ b/src/NHibernate/Async/Event/Default/DefaultInitializeCollectionEventListener.cs @@ -91,7 +91,7 @@ private async Task InitializeCollectionFromCacheAsync(object id, ICollecti } var batchSize = persister.GetBatchSize(); - if (batchSize > 1 && persister.Cache.IsBatchingGetSupported()) + if (batchSize > 1 && persister.Cache.PreferMultipleGet()) { var collectionEntries = new CollectionEntry[batchSize]; // The first item in the array is the item that we want to load diff --git a/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs index 24d2752e3cd..45f0295e6e4 100644 --- a/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs +++ b/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs @@ -412,7 +412,7 @@ protected virtual async Task LoadFromSecondLevelCacheAsync(LoadEvent @ev } ISessionFactoryImplementor factory = source.Factory; var batchSize = persister.GetBatchSize(); - if (batchSize > 1 && persister.Cache.IsBatchingGetSupported()) + if (batchSize > 1 && persister.Cache.PreferMultipleGet()) { // The first item in the array is the item that we want to load var entityBatch = diff --git a/src/NHibernate/Cache/CacheBase.cs b/src/NHibernate/Cache/CacheBase.cs new file mode 100644 index 00000000000..81a11c0bc66 --- /dev/null +++ b/src/NHibernate/Cache/CacheBase.cs @@ -0,0 +1,386 @@ +using System; +using System.Collections; +using System.Threading; +using System.Threading.Tasks; +using NHibernate.Cache.Entry; +using NHibernate.Type; + +namespace NHibernate.Cache +{ + /// + /// Base class for letting implementors define a caching algorithm. + /// + /// + /// + /// + /// All implementations must be threadsafe. + /// + /// + /// The key is the identifier of the object that is being cached. The key is in most cases + /// a . + /// + /// + /// The value can be a , a , + /// a , an or + /// implementation, all containing simple values or array of + /// simple values. It can also be directly a simple value or an array of simple values. + /// And it can be a containing any of the previous types, or + /// a . + /// + /// + /// All those types are binary serializable. + /// + /// + /// This base class provides minimal async method implementations delegating their work to their + /// synchronous counterparts. Override them for supplying actual async operations. + /// + /// + /// Similarly, this base class provides minimal multiple get/put/lock/unlock implementations + /// delegating their work to their single operation counterparts. Override them if your cache + /// implementation supports multiple operations. + /// + /// + public abstract partial class CacheBase : + // 6.0 TODO: remove ICache +#pragma warning disable 618 + ICache +#pragma warning restore 618 + { + /// + /// A reasonable "lock timeout". + /// + public abstract int Timeout { get; } + + /// + /// The name of the cache region. + /// + public abstract string RegionName { get; } + + /// + /// Should batched get operations be preferred other single get calls? + /// + /// + /// + /// This property should yield if delegates + /// its implementation to . + /// + /// + /// When , NHibernate will attempt to get other non initialized proxies or + /// collections from the cache instead of only getting the proxy or collection which initialization + /// is asked for. If this cache implementation does not benefit from batching together get operations, + /// this may result in a performance loss. + /// + /// + /// When , NHibernate will still call when it has many + /// gets to perform. Its default implementation is adequate for this case. + /// + /// + public abstract bool PreferMultipleGet { get; } + + #region Basic abstract operations + + /// + /// Get the item from the cache. + /// + /// The item key. + /// The cached item. + public abstract object Get(object key); + + /// + /// Put the item into the cache. + /// + /// The item key. + /// The item. + public abstract void Put(object key, object value); + + /// + /// Remove an item from the cache. + /// + /// The item key. + public abstract void Remove(object key); + + /// + /// Clear the cache. + /// + public abstract void Clear(); + + /// + /// Clean up. + /// + public abstract void Destroy(); + + /// + /// Lock the item from being concurrently changed. + /// + /// The item key. + /// A lock object to use for unlocking the item. Can be . + /// The implementation is allowed to do nothing for non-clustered cache. + public abstract object Lock(object key); + + /// + /// Unlock an item which was previously locked. + /// + /// The item key. + /// The lock object to use for unlocking the item, as received from . + /// The implementation should do nothing if own implementation does nothing. + public abstract void Unlock(object key, object lockValue); + + /// + /// Generate a timestamp. + /// + /// A timestamp. + public abstract long NextTimestamp(); + + #endregion + + #region Batch operations default implementation + + /// + /// Get multiple items from the cache. + /// + /// The keys to be retrieved from the cache. + /// The cached items, matching each key of respectively. For each missed key, + /// it will contain a . + public virtual object[] GetMany(object[] keys) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + var result = new object[keys.Length]; + for (var i = 0; i < keys.Length; i++) + { + result[i] = Get(keys[i]); + } + + return result; + } + + /// + /// Add multiple items to the cache. + /// + /// The keys of the items. + /// The items. + public virtual void PutMany(object[] keys, object[] values) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + if (values == null) + throw new ArgumentNullException(nameof(values)); + if (keys.Length != values.Length) + throw new ArgumentException( + $"{nameof(keys)} and {nameof(values)} must have the same length. Found {keys.Length} and " + + $"{values.Length} respectively"); + + for (var i = 0; i < keys.Length; i++) + { + Put(keys[i], values[i]); + } + } + + /// + /// Lock the items from being concurrently changed. + /// + /// The keys of the items. + /// A lock object to use for unlocking the items. Can be . + /// The implementation is allowed to do nothing for non-clustered cache. + public virtual object LockMany(object[] keys) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + // When delegating to the single operation lock, agreggate lock values into one. + var lockValues = new object[keys.Length]; + for (var i = 0; i < keys.Length; i++) + { + lockValues[i] = Lock(keys[i]); + } + + return lockValues; + } + + /// + /// Unlock the items that were previously locked. + /// + /// The keys of the items. + /// The lock object to use for unlocking the items, as received from . + /// The implementation should do nothing if own implementation does nothing. + public virtual void UnlockMany(object[] keys, object lockValue) + { + if (keys == null) + throw new ArgumentNullException(nameof(keys)); + if (!(lockValue is object[] lockValues) || lockValues.Length != keys.Length) + throw new ArgumentException( + $"When {nameof(UnlockMany)} is not overriden, {nameof(lockValue)} must be an array of the lock " + + $"values resulting of sequentially single-locking each key.", + nameof(lockValue)); + for (var i = 0; i < keys.Length; i++) + { + Unlock(keys[i], lockValues[i]); + } + } + + #endregion + + #region Async basic operations default implementations + + /// + /// Get the item from the cache. + /// + /// The item key. + /// A cancellation token that can be used to cancel the work. + /// The cached item. + public virtual Task GetAsync(object key, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + return Task.FromResult(Get(key)); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + /// + /// Put the item into the cache. + /// + /// The item key. + /// The item. + /// A cancellation token that can be used to cancel the work. + public virtual Task PutAsync(object key, object value, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + Put(key, value); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + /// + /// Remove an item from the cache. + /// + /// The item key. + /// A cancellation token that can be used to cancel the work. + public virtual Task RemoveAsync(object key, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + Remove(key); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + /// + /// Clear the cache. + /// + /// A cancellation token that can be used to cancel the work. + public virtual Task ClearAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + Clear(); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + /// + /// If this is a clustered cache, lock the item. + /// + /// The item key. + /// A cancellation token that can be used to cancel the work. + /// A lock object to use for unlocking the key. Can be . + public virtual Task LockAsync(object key, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + return Task.FromResult(Lock(key)); + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + /// + /// If this is a clustered cache, unlock the item. + /// + /// The item key. + /// The lock object to use for unlocking the key, as received from . + /// A cancellation token that can be used to cancel the work. + public virtual Task UnlockAsync(object key, object lockValue, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + try + { + Unlock(key, lockValue); + return Task.CompletedTask; + } + catch (Exception ex) + { + return Task.FromException(ex); + } + } + + #endregion + + #region Obsolete ICache implementation + + void ICache.Lock(object key) + { + Lock(key); + } + + void ICache.Unlock(object key) + { + Unlock(key, null); + } + + // Async Generator does not generate an explicit missing async counterpart when a method + // differing only by its return type already exists. + Task ICache.LockAsync(object key, CancellationToken cancellationToken) + { + return LockAsync(key, cancellationToken); + } + + #endregion + } +} diff --git a/src/NHibernate/Cache/CacheFactory.cs b/src/NHibernate/Cache/CacheFactory.cs index f79db5383a7..8a7d3b08641 100644 --- a/src/NHibernate/Cache/CacheFactory.cs +++ b/src/NHibernate/Cache/CacheFactory.cs @@ -67,16 +67,14 @@ public static ICacheConcurrencyStrategy CreateCache(string usage, string name, b "cache usage attribute should be read-write, read-only, nonstrict-read-write, or transactional"); } - ICache impl; try { - impl = settings.CacheProvider.BuildCache(name, properties); + ccs.Cache = settings.CacheProvider.BuildCache(name, properties); } catch (CacheException e) { throw new HibernateException("Could not instantiate cache implementation", e); } - ccs.Cache = impl; return ccs; } diff --git a/src/NHibernate/Cache/FakeCache.cs b/src/NHibernate/Cache/FakeCache.cs index 8c6bc90091b..f8b3bc99032 100644 --- a/src/NHibernate/Cache/FakeCache.cs +++ b/src/NHibernate/Cache/FakeCache.cs @@ -3,48 +3,51 @@ namespace NHibernate.Cache /// /// Used by /// - public partial class FakeCache : ICache + public partial class FakeCache : CacheBase { public FakeCache(string regionName) { RegionName = regionName; } - public object Get(object key) + public override bool PreferMultipleGet => false; + + public override object Get(object key) { return null; } - public void Put(object key, object value) + public override void Put(object key, object value) { } - public void Remove(object key) + public override void Remove(object key) { } - public void Clear() + public override void Clear() { } - public void Destroy() + public override void Destroy() { } - public void Lock(object key) + public override object Lock(object key) { + return null; } - public void Unlock(object key) + public override void Unlock(object key, object lockValue) { } - public long NextTimestamp() + public override long NextTimestamp() { return Timestamper.Next(); } - public int Timeout { get; private set; } - public string RegionName { get; private set; } + public override int Timeout => 0; + public override string RegionName { get; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/HashtableCache.cs b/src/NHibernate/Cache/HashtableCache.cs index db181df87c1..97587a5d0c4 100644 --- a/src/NHibernate/Cache/HashtableCache.cs +++ b/src/NHibernate/Cache/HashtableCache.cs @@ -5,77 +5,78 @@ namespace NHibernate.Cache /// /// A simple -based cache /// - public partial class HashtableCache : ICache + public partial class HashtableCache : CacheBase { private IDictionary hashtable = new Hashtable(); private readonly string regionName; - #region ICache Members - public HashtableCache(string regionName) { this.regionName = regionName; } - /// - public object Get(object key) + /// + public override object Get(object key) { return hashtable[key]; } - /// - public void Put(object key, object value) + /// + public override void Put(object key, object value) { hashtable[key] = value; } - /// - public void Remove(object key) + /// + public override void Remove(object key) { hashtable.Remove(key); } - /// - public void Clear() + /// + public override void Clear() { hashtable.Clear(); } - /// - public void Destroy() + /// + public override void Destroy() { } - /// - public void Lock(object key) + /// + public override object Lock(object key) { // local cache, so we use synchronization + return null; } - /// - public void Unlock(object key) + /// + public override void Unlock(object key, object lockValue) { // local cache, so we use synchronization } - /// - public long NextTimestamp() + /// + public override long NextTimestamp() { return Timestamper.Next(); } - /// - public int Timeout + /// + public override int Timeout { get { return Timestamper.OneMs * 60000; // ie. 60 seconds } } - public string RegionName + /// + public override string RegionName { get { return regionName; } } - #endregion + /// + public override bool PreferMultipleGet => false; } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/HashtableCacheProvider.cs b/src/NHibernate/Cache/HashtableCacheProvider.cs index cb277ae3fa1..af63c50dd04 100644 --- a/src/NHibernate/Cache/HashtableCacheProvider.cs +++ b/src/NHibernate/Cache/HashtableCacheProvider.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace NHibernate.Cache @@ -10,7 +11,14 @@ public class HashtableCacheProvider : ICacheProvider { #region ICacheProvider Members - public ICache BuildCache(string regionName, IDictionary properties) + // Since 5.2 + [Obsolete] + ICache ICacheProvider.BuildCache(string regionName, IDictionary properties) + { + return BuildCache(regionName, properties); + } + + public CacheBase BuildCache(string regionName, IDictionary properties) { return new HashtableCache(regionName); } @@ -30,4 +38,4 @@ public void Stop() #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/IBatchableCache.cs b/src/NHibernate/Cache/IBatchableCache.cs deleted file mode 100644 index b85dc83c9cf..00000000000 --- a/src/NHibernate/Cache/IBatchableCache.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NHibernate.Cache -{ - /// - /// Defines methods for retrieving and adding multiple objects from/to the cache at once. - /// The implementor should use this interface along with when the - /// cache supports a multiple get and put operation. - /// - /// - /// - /// - /// All implementations must be threadsafe. - /// - /// - public partial interface IBatchableCache : IBatchableReadOnlyCache - { - /// - /// Add multiple objects to the cache. - /// - /// The keys to cache. - /// The objects to cache. - void PutMany(object[] keys, object[] values); - - /// - /// Lock the objects from being changed by another thread. - /// - /// The keys to lock. - /// The value that was used to lock the keys. - object LockMany(object[] keys); - - /// - /// Unlock the objects that were previously locked. - /// - /// The keys to unlock. - /// The value that was used to lock the keys. - void UnlockMany(object[] keys, object lockValue); - } -} diff --git a/src/NHibernate/Cache/IBatchableCacheConcurrencyStrategy.cs b/src/NHibernate/Cache/IBatchableCacheConcurrencyStrategy.cs index 56f15f6a9a8..82eb1f8da8c 100644 --- a/src/NHibernate/Cache/IBatchableCacheConcurrencyStrategy.cs +++ b/src/NHibernate/Cache/IBatchableCacheConcurrencyStrategy.cs @@ -19,29 +19,34 @@ namespace NHibernate.Cache /// for a collection. /// /// + // 6.0 TODO: merge into ICacheConcurrencyStrategy public partial interface IBatchableCacheConcurrencyStrategy : ICacheConcurrencyStrategy { /// - /// Attempt to retrieve multiple objects from the Cache + /// Attempt to retrieve multiple items from the cache. /// - /// The keys (id) of the objects to get out of the Cache. - /// A timestamp prior to the transaction start time - /// An array of cached objects or + /// The keys of the items. + /// A timestamp prior to the transaction start time. + /// The cached items, matching each key of respectively. For each missed key, + /// it will contain a . /// object[] GetMany(CacheKey[] keys, long timestamp); /// - /// Attempt to cache objects, after loading them from the database. + /// Attempt to cache items, after loading them from the database. /// - /// The keys (id) of the objects to put in the Cache. - /// The objects to put in the cache. + /// The keys of the items. + /// The items. /// A timestamp prior to the transaction start time. - /// The version numbers of the objects we are putting. - /// The comparers to be used to compare version numbers + /// The version numbers of the items. + /// The comparers to be used to compare version numbers. /// Indicates that the cache should avoid a put if the item is already cached. - /// if the objects were successfully cached. + /// An array of boolean indicating if each item was successfully cached. /// bool[] PutMany(CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, bool[] minimalPuts); + + // 6.0 TODO: remove for using ICacheConcurrencyStrategy.Cache re-typed CacheBase instead + CacheBase CacheBase { get; } } } diff --git a/src/NHibernate/Cache/IBatchableReadOnlyCache.cs b/src/NHibernate/Cache/IBatchableReadOnlyCache.cs deleted file mode 100644 index 7bdbaa07bce..00000000000 --- a/src/NHibernate/Cache/IBatchableReadOnlyCache.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NHibernate.Cache -{ - /// - /// Defines a method for retrieving multiple objects from the cache at once. The implementor - /// should use this interface along with when the cache supports - /// a multiple get operation. - /// - /// - /// - /// - /// All implementations must be threadsafe. - /// - /// - public partial interface IBatchableReadOnlyCache - { - /// - /// Get multiple objects from the cache. - /// - /// The keys to be retrieved from the cache. - /// - object[] GetMany(object[] keys); - } -} diff --git a/src/NHibernate/Cache/ICache.cs b/src/NHibernate/Cache/ICache.cs index d4dbaedbd56..39d3ffa2472 100644 --- a/src/NHibernate/Cache/ICache.cs +++ b/src/NHibernate/Cache/ICache.cs @@ -1,3 +1,5 @@ +using System; + namespace NHibernate.Cache { /// @@ -13,6 +15,8 @@ namespace NHibernate.Cache /// value is a . /// /// + // Since 5.2 + [Obsolete("Derive from CacheBase instead. NHibernate members using this type will switch to CacheBase in a future version.")] public partial interface ICache { /// @@ -78,4 +82,4 @@ public partial interface ICache /// string RegionName { get; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs b/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs index d22862d128a..d9925c1b49a 100644 --- a/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs +++ b/src/NHibernate/Cache/ICacheConcurrencyStrategy.cs @@ -2,6 +2,7 @@ using System.Collections; using NHibernate.Cache.Access; using NHibernate.Cache.Entry; +using NHibernate.Util; namespace NHibernate.Cache { @@ -134,13 +135,17 @@ public partial interface ICacheConcurrencyStrategy /// string RegionName { get; } + // 6.0 TODO: type as CacheBase instead +#pragma warning disable 618 /// /// Gets or sets the for this strategy to use. /// /// The for this strategy to use. ICache Cache { get; set; } +#pragma warning restore 618 } + // 6.0 TODO: remove internal static partial class CacheConcurrencyStrategyExtensions { /// @@ -153,11 +158,11 @@ internal static partial class CacheConcurrencyStrategyExtensions /// public static object[] GetMany(this ICacheConcurrencyStrategy cache, CacheKey[] keys, long timestamp) { - if (!(cache is IBatchableCacheConcurrencyStrategy batchableCache)) - { - throw new InvalidOperationException($"Cache concurrency strategy {cache.GetType()} does not support batching"); - } - return batchableCache.GetMany(keys, timestamp); + // PreferMultipleGet yields false if !IBatchableCacheConcurrencyStrategy, no GetMany call should be done + // in such case. + return ReflectHelper + .CastOrThrow(cache, "batching") + .GetMany(keys, timestamp); } /// @@ -175,23 +180,37 @@ public static object[] GetMany(this ICacheConcurrencyStrategy cache, CacheKey[] public static bool[] PutMany(this ICacheConcurrencyStrategy cache, CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, bool[] minimalPuts) { - if (!(cache is IBatchableCacheConcurrencyStrategy batchableCache)) + if (cache is IBatchableCacheConcurrencyStrategy batchableCache) + { + return batchableCache.PutMany(keys, values, timestamp, versions, versionComparers, minimalPuts); + } + + var result = new bool[keys.Length]; + for (var i = 0; i < keys.Length; i++) { - throw new InvalidOperationException($"Cache concurrency strategy {cache.GetType()} does not support batching"); + result[i] = cache.Put(keys[i], values[i], timestamp, versions[i], versionComparers[i], minimalPuts[i]); } - return batchableCache.PutMany(keys, values, timestamp, versions, versionComparers, minimalPuts); + + return result; } - public static bool IsBatchingGetSupported(this ICacheConcurrencyStrategy cache) + // 6.0 TODO: remove + internal static bool PreferMultipleGet(this ICacheConcurrencyStrategy cache) { - // ReSharper disable once SuspiciousTypeConversion.Global - return cache.Cache is IBatchableReadOnlyCache && cache is IBatchableCacheConcurrencyStrategy; + if (cache is IBatchableCacheConcurrencyStrategy batchableCache) + return batchableCache.CacheBase.PreferMultipleGet; + return false; } - public static bool IsBatchingPutSupported(this ICacheConcurrencyStrategy cache) + // 6.0 TODO: remove + internal static CacheBase GetCacheBase(this ICacheConcurrencyStrategy cache) { - // ReSharper disable once SuspiciousTypeConversion.Global - return cache.Cache is IBatchableCache && cache is IBatchableCacheConcurrencyStrategy; + if (cache is IBatchableCacheConcurrencyStrategy batchableCache) + return batchableCache.CacheBase; + var concreteCache = cache.Cache; + if (concreteCache == null) + return null; + return concreteCache as CacheBase ?? new ObsoleteCacheWrapper(concreteCache); } } } diff --git a/src/NHibernate/Cache/ICacheProvider.cs b/src/NHibernate/Cache/ICacheProvider.cs index 5fe7e7efb2b..49e2054aeba 100644 --- a/src/NHibernate/Cache/ICacheProvider.cs +++ b/src/NHibernate/Cache/ICacheProvider.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace NHibernate.Cache @@ -8,12 +9,15 @@ namespace NHibernate.Cache public interface ICacheProvider { /// - /// Configure the cache + /// Build a cache. /// - /// the name of the cache region - /// configuration settings - /// + /// The name of the cache region. + /// Configuration settings. + /// A cache. + // 6.0 TODO: return a CacheBase instead +#pragma warning disable 618 ICache BuildCache(string regionName, IDictionary properties); +#pragma warning restore 618 /// /// generate a timestamp @@ -34,4 +38,4 @@ public interface ICacheProvider /// void Stop(); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/IQueryCache.cs b/src/NHibernate/Cache/IQueryCache.cs index 77874f4c2e2..acce37a7fda 100644 --- a/src/NHibernate/Cache/IQueryCache.cs +++ b/src/NHibernate/Cache/IQueryCache.cs @@ -13,7 +13,10 @@ namespace NHibernate.Cache /// public partial interface IQueryCache { + // 6.0 TODO: type as CacheBase instead +#pragma warning disable 618 ICache Cache { get; } +#pragma warning restore 618 string RegionName { get; } void Clear(); diff --git a/src/NHibernate/Cache/NoCacheProvider.cs b/src/NHibernate/Cache/NoCacheProvider.cs index 357b30eb27a..9c927369028 100644 --- a/src/NHibernate/Cache/NoCacheProvider.cs +++ b/src/NHibernate/Cache/NoCacheProvider.cs @@ -13,13 +13,20 @@ public class NoCacheProvider : ICacheProvider public const string WarnMessage = "Second-level cache is enabled in a class, but no cache provider was selected. Fake cache used."; + // Since 5.2 + [Obsolete] + ICache ICacheProvider.BuildCache(string regionName, IDictionary properties) + { + return BuildCache(regionName, properties); + } + /// /// Configure the cache /// /// the name of the cache region /// configuration settings /// - public ICache BuildCache(string regionName, IDictionary properties) + public CacheBase BuildCache(string regionName, IDictionary properties) { // NH different behavior because NH-1093 log.Warn(WarnMessage); @@ -63,4 +70,4 @@ public void Stop() // get { return false; } //} } -} \ No newline at end of file +} diff --git a/src/NHibernate/Cache/NonstrictReadWriteCache.cs b/src/NHibernate/Cache/NonstrictReadWriteCache.cs index ff7f6ce7ece..1e4fa78cfa2 100644 --- a/src/NHibernate/Cache/NonstrictReadWriteCache.cs +++ b/src/NHibernate/Cache/NonstrictReadWriteCache.cs @@ -16,9 +16,10 @@ namespace NHibernate.Cache /// public partial class NonstrictReadWriteCache : IBatchableCacheConcurrencyStrategy { +#pragma warning disable 618 private ICache cache; - private IBatchableReadOnlyCache _batchableReadOnlyCache; - private IBatchableCache _batchableCache; +#pragma warning restore 618 + private CacheBase _batchableCache; private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(NonstrictReadWriteCache)); @@ -27,21 +28,25 @@ public partial class NonstrictReadWriteCache : IBatchableCacheConcurrencyStrateg /// public string RegionName { - get { return cache.RegionName; } + get { return Cache.RegionName; } } + // 6.0 TODO: type as CacheBase instead and switch to auto-property +#pragma warning disable 618 public ICache Cache +#pragma warning restore 618 { get { return cache; } set { cache = value; - // ReSharper disable once SuspiciousTypeConversion.Global - _batchableReadOnlyCache = value as IBatchableReadOnlyCache; - _batchableCache = value as IBatchableCache; + _batchableCache = value as CacheBase ?? new ObsoleteCacheWrapper(value); } } + // 6.0 TODO: remove + public CacheBase CacheBase => _batchableCache; + /// /// Get the most recent version, if available. /// @@ -52,7 +57,7 @@ public object Get(CacheKey key, long txTimestamp) log.Debug("Cache lookup: {0}", key); } - object result = cache.Get(key); + object result = Cache.Get(key); if (result != null) { log.Debug("Cache hit"); @@ -66,15 +71,11 @@ public object Get(CacheKey key, long txTimestamp) public object[] GetMany(CacheKey[] keys, long timestamp) { - if (_batchableReadOnlyCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching get operation"); - } if (log.IsDebugEnabled()) { log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); } - var results = _batchableReadOnlyCache.GetMany(keys.Select(o => (object) o).ToArray()); + var results = _batchableCache.GetMany(keys.Select(o => (object) o).ToArray()); if (!log.IsDebugEnabled()) { return results; @@ -89,13 +90,10 @@ public object[] GetMany(CacheKey[] keys, long timestamp) /// /// Add multiple items to the cache /// - public bool[] PutMany(CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, - bool[] minimalPuts) + public bool[] PutMany( + CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, + bool[] minimalPuts) { - if (_batchableCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching operations"); - } var result = new bool[keys.Length]; if (timestamp == long.MinValue) { @@ -103,7 +101,7 @@ public bool[] PutMany(CacheKey[] keys, object[] values, long timestamp, object[] return result; } - var checkKeys = new List(); + var checkKeys = new List(); var checkKeyIndexes = new List(); for (var i = 0; i < minimalPuts.Length; i++) { @@ -164,7 +162,7 @@ public bool Put(CacheKey key, object value, long txTimestamp, object version, IC return false; } - if (minimalPut && cache.Get(key) != null) + if (minimalPut && Cache.Get(key) != null) { if (log.IsDebugEnabled()) { @@ -176,7 +174,7 @@ public bool Put(CacheKey key, object value, long txTimestamp, object version, IC { log.Debug("Caching: {0}", key); } - cache.Put(key, value); + Cache.Put(key, value); return true; } @@ -194,7 +192,7 @@ public void Remove(CacheKey key) { log.Debug("Removing: {0}", key); } - cache.Remove(key); + Cache.Remove(key); } public void Clear() @@ -203,14 +201,14 @@ public void Clear() { log.Debug("Clearing"); } - cache.Clear(); + Cache.Clear(); } public void Destroy() { try { - cache.Destroy(); + Cache.Destroy(); } catch (Exception e) { @@ -227,7 +225,7 @@ public void Evict(CacheKey key) { log.Debug("Invalidating: {0}", key); } - cache.Remove(key); + Cache.Remove(key); } /// @@ -257,7 +255,7 @@ public void Release(CacheKey key, ISoftLock @lock) log.Debug("Invalidating (again): {0}", key); } - cache.Remove(key); + Cache.Remove(key); } /// diff --git a/src/NHibernate/Cache/ObsoleteCacheWrapper.cs b/src/NHibernate/Cache/ObsoleteCacheWrapper.cs new file mode 100644 index 00000000000..febd5a47da0 --- /dev/null +++ b/src/NHibernate/Cache/ObsoleteCacheWrapper.cs @@ -0,0 +1,64 @@ +namespace NHibernate.Cache +{ + // 6.0 TODO: remove this class + internal partial class ObsoleteCacheWrapper : CacheBase + { +#pragma warning disable 618 + private ICache _cache; +#pragma warning restore 618 + +#pragma warning disable 618 + internal ObsoleteCacheWrapper(ICache cache) +#pragma warning restore 618 + { + _cache = cache; + } + + public override long NextTimestamp() + { + return _cache.NextTimestamp(); + } + + public override int Timeout => _cache.Timeout; + + public override string RegionName => _cache.RegionName; + + public override bool PreferMultipleGet => false; + + public override object Get(object key) + { + return _cache.Get(key); + } + + public override void Put(object key, object value) + { + _cache.Put(key, value); + } + + public override void Remove(object key) + { + _cache.Remove(key); + } + + public override void Clear() + { + _cache.Clear(); + } + + public override void Destroy() + { + _cache.Destroy(); + } + + public override object Lock(object key) + { + _cache.Lock(key); + return null; + } + + public override void Unlock(object key, object lockValue) + { + _cache.Unlock(key); + } + } +} diff --git a/src/NHibernate/Cache/ReadOnlyCache.cs b/src/NHibernate/Cache/ReadOnlyCache.cs index e5df5e3eead..23806ef394e 100644 --- a/src/NHibernate/Cache/ReadOnlyCache.cs +++ b/src/NHibernate/Cache/ReadOnlyCache.cs @@ -11,9 +11,10 @@ namespace NHibernate.Cache /// public partial class ReadOnlyCache : IBatchableCacheConcurrencyStrategy { +#pragma warning disable 618 private ICache cache; - private IBatchableReadOnlyCache _batchableReadOnlyCache; - private IBatchableCache _batchableCache; +#pragma warning restore 618 + private CacheBase _batchableCache; private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(ReadOnlyCache)); /// @@ -21,24 +22,28 @@ public partial class ReadOnlyCache : IBatchableCacheConcurrencyStrategy /// public string RegionName { - get { return cache.RegionName; } + get { return Cache.RegionName; } } + // 6.0 TODO: type as CacheBase instead and switch to autoproperty +#pragma warning disable 618 public ICache Cache +#pragma warning restore 618 { get { return cache; } set { cache = value; - // ReSharper disable once SuspiciousTypeConversion.Global - _batchableReadOnlyCache = value as IBatchableReadOnlyCache; - _batchableCache = value as IBatchableCache; + _batchableCache = value as CacheBase ?? new ObsoleteCacheWrapper(value); } } + // 6.0 TODO: remove + public CacheBase CacheBase => _batchableCache; + public object Get(CacheKey key, long timestamp) { - object result = cache.Get(key); + object result = Cache.Get(key); if (result != null && log.IsDebugEnabled()) { log.Debug("Cache hit: {0}", key); @@ -48,15 +53,11 @@ public object Get(CacheKey key, long timestamp) public object[] GetMany(CacheKey[] keys, long timestamp) { - if (_batchableReadOnlyCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching get operation"); - } if (log.IsDebugEnabled()) { log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); } - var results = _batchableReadOnlyCache.GetMany(keys.Select(o => (object) o).ToArray()); + var results = _batchableCache.GetMany(keys.Select(o => (object) o).ToArray()); if (!log.IsDebugEnabled()) { return results; @@ -77,13 +78,10 @@ public ISoftLock Lock(CacheKey key, object version) throw new InvalidOperationException("ReadOnlyCache: Can't write to a readonly object " + key.EntityOrRoleName); } - public bool[] PutMany(CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, - bool[] minimalPuts) + public bool[] PutMany( + CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, + bool[] minimalPuts) { - if (_batchableCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching operations"); - } var result = new bool[keys.Length]; if (timestamp == long.MinValue) { @@ -149,7 +147,7 @@ public bool Put(CacheKey key, object value, long timestamp, object version, ICom return false; } - if (minimalPut && cache.Get(key) != null) + if (minimalPut && Cache.Get(key) != null) { if (log.IsDebugEnabled()) { @@ -161,7 +159,7 @@ public bool Put(CacheKey key, object value, long timestamp, object version, ICom { log.Debug("Caching: {0}", key); } - cache.Put(key, value); + Cache.Put(key, value); return true; } @@ -175,19 +173,19 @@ public void Release(CacheKey key, ISoftLock @lock) public void Clear() { - cache.Clear(); + Cache.Clear(); } public void Remove(CacheKey key) { - cache.Remove(key); + Cache.Remove(key); } public void Destroy() { try { - cache.Destroy(); + Cache.Destroy(); } catch (Exception e) { diff --git a/src/NHibernate/Cache/ReadWriteCache.cs b/src/NHibernate/Cache/ReadWriteCache.cs index fa66ed9bd16..9a841fc2e80 100644 --- a/src/NHibernate/Cache/ReadWriteCache.cs +++ b/src/NHibernate/Cache/ReadWriteCache.cs @@ -35,35 +35,36 @@ public interface ILockable private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(ReadWriteCache)); private readonly object _lockObject = new object(); +#pragma warning disable 618 private ICache cache; - private IBatchableReadOnlyCache _batchableReadOnlyCache; - private IBatchableCache _batchableCache; +#pragma warning restore 618 + private CacheBase _cacheBase; private int _nextLockId; - public ReadWriteCache() - { - } - /// /// Gets the cache region name. /// public string RegionName { - get { return cache.RegionName; } + get { return Cache.RegionName; } } + // 6.0 TODO: type as CacheBase instead and switch to autoproperty +#pragma warning disable 618 public ICache Cache +#pragma warning restore 618 { get { return cache; } set { cache = value; - // ReSharper disable once SuspiciousTypeConversion.Global - _batchableReadOnlyCache = value as IBatchableReadOnlyCache; - _batchableCache = value as IBatchableCache; + _cacheBase = value as CacheBase ?? new ObsoleteCacheWrapper(value); } } + // 6.0 TODO: remove + public CacheBase CacheBase => _cacheBase; + /// /// Generate an id for a new lock. Uniqueness per cache instance is very /// desirable but not absolutely critical. Must be called from one of the @@ -110,7 +111,7 @@ public object Get(CacheKey key, long txTimestamp) { cache.Lock( key );*/ - ILockable lockable = (ILockable) cache.Get(key); + ILockable lockable = (ILockable) Cache.Get(key); bool gettable = lockable != null && lockable.IsGettable(txTimestamp); @@ -148,10 +149,6 @@ public object Get(CacheKey key, long txTimestamp) public object[] GetMany(CacheKey[] keys, long timestamp) { - if (_batchableReadOnlyCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching get operation"); - } if (log.IsDebugEnabled()) { log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable())); @@ -159,7 +156,7 @@ public object[] GetMany(CacheKey[] keys, long timestamp) var result = new object[keys.Length]; lock (_lockObject) { - var lockables = _batchableReadOnlyCache.GetMany(keys.Select(o => (object) o).ToArray()); + var lockables = _cacheBase.GetMany(keys.Select(o => (object) o).ToArray()); for (var i = 0; i < lockables.Length; i++) { var lockable = (ILockable) lockables[i]; @@ -201,21 +198,20 @@ public ISoftLock Lock(CacheKey key, object version) log.Debug("Invalidating: {0}", key); } + var lockValue = _cacheBase.Lock(key); try { - cache.Lock(key); - - ILockable lockable = (ILockable) cache.Get(key); - long timeout = cache.NextTimestamp() + cache.Timeout; + ILockable lockable = (ILockable) Cache.Get(key); + long timeout = Cache.NextTimestamp() + Cache.Timeout; CacheLock @lock = lockable == null ? new CacheLock(timeout, NextLockId(), version) : lockable.Lock(timeout, NextLockId()); - cache.Put(key, @lock); + Cache.Put(key, @lock); return @lock; } finally { - cache.Unlock(key); + _cacheBase.Unlock(key, lockValue); } } } @@ -227,14 +223,10 @@ public ISoftLock Lock(CacheKey key, object version) /// database is operating in repeatable read isolation mode.) /// /// Whether the items were actually put into the cache - public bool[] PutMany(CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, - bool[] minimalPuts) + public bool[] PutMany( + CacheKey[] keys, object[] values, long timestamp, object[] versions, IComparer[] versionComparers, + bool[] minimalPuts) { - if (_batchableCache == null) - { - throw new InvalidOperationException($"Cache {cache.GetType()} does not support batching operations"); - } - var result = new bool[keys.Length]; if (timestamp == long.MinValue) { @@ -249,14 +241,11 @@ public bool[] PutMany(CacheKey[] keys, object[] values, long timestamp, object[] log.Debug("Caching: {0}", string.Join(",", keys.AsEnumerable())); } var keysArr = keys.Cast().ToArray(); - var lockAquired = false; - object lockValue = null; + var lockValue = _cacheBase.LockMany(keysArr);; try { - lockValue = _batchableCache.LockMany(keysArr); - lockAquired = true; var putBatch = new Dictionary(); - var lockables = _batchableCache.GetMany(keysArr); + var lockables = _cacheBase.GetMany(keysArr); for (var i = 0; i < keys.Length; i++) { var key = keys[i]; @@ -292,15 +281,12 @@ public bool[] PutMany(CacheKey[] keys, object[] values, long timestamp, object[] if (putBatch.Count > 0) { - _batchableCache.PutMany(putBatch.Keys.ToArray(), putBatch.Values.ToArray()); + _cacheBase.PutMany(putBatch.Keys.ToArray(), putBatch.Values.ToArray()); } } finally { - if (lockAquired) - { - _batchableCache.UnlockMany(keysArr, lockValue); - } + _cacheBase.UnlockMany(keysArr, lockValue); } } return result; @@ -329,18 +315,17 @@ public bool Put(CacheKey key, object value, long txTimestamp, object version, IC log.Debug("Caching: {0}", key); } + var lockValue = _cacheBase.Lock(key); try { - cache.Lock(key); - - ILockable lockable = (ILockable) cache.Get(key); + ILockable lockable = (ILockable) Cache.Get(key); bool puttable = lockable == null || lockable.IsPuttable(txTimestamp, version, versionComparator); if (puttable) { - cache.Put(key, new CachedItem(value, cache.NextTimestamp(), version)); + Cache.Put(key, new CachedItem(value, Cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Cached: {0}", key); @@ -365,7 +350,7 @@ public bool Put(CacheKey key, object value, long txTimestamp, object version, IC } finally { - cache.Unlock(key); + _cacheBase.Unlock(key, lockValue); } } } @@ -376,8 +361,8 @@ public bool Put(CacheKey key, object value, long txTimestamp, object version, IC private void DecrementLock(object key, CacheLock @lock) { //decrement the lock - @lock.Unlock(cache.NextTimestamp()); - cache.Put(key, @lock); + @lock.Unlock(Cache.NextTimestamp()); + Cache.Put(key, @lock); } public void Release(CacheKey key, ISoftLock clientLock) @@ -389,11 +374,10 @@ public void Release(CacheKey key, ISoftLock clientLock) log.Debug("Releasing: {0}", key); } + var lockValue = _cacheBase.Lock(key); try { - cache.Lock(key); - - ILockable lockable = (ILockable) cache.Get(key); + ILockable lockable = (ILockable) Cache.Get(key); if (IsUnlockable(clientLock, lockable)) { DecrementLock(key, (CacheLock) lockable); @@ -405,7 +389,7 @@ public void Release(CacheKey key, ISoftLock clientLock) } finally { - cache.Unlock(key); + _cacheBase.Unlock(key, lockValue); } } } @@ -413,28 +397,28 @@ public void Release(CacheKey key, ISoftLock clientLock) internal void HandleLockExpiry(object key) { log.Warn("An item was expired by the cache while it was locked (increase your cache timeout): {0}", key); - long ts = cache.NextTimestamp() + cache.Timeout; + long ts = Cache.NextTimestamp() + Cache.Timeout; // create new lock that times out immediately CacheLock @lock = new CacheLock(ts, NextLockId(), null); @lock.Unlock(ts); - cache.Put(key, @lock); + Cache.Put(key, @lock); } public void Clear() { - cache.Clear(); + Cache.Clear(); } public void Remove(CacheKey key) { - cache.Remove(key); + Cache.Remove(key); } public void Destroy() { try { - cache.Destroy(); + Cache.Destroy(); } catch (Exception e) { @@ -455,11 +439,10 @@ public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock cl log.Debug("Updating: {0}", key); } + var lockValue = _cacheBase.Lock(key); try { - cache.Lock(key); - - ILockable lockable = (ILockable) cache.Get(key); + ILockable lockable = (ILockable) Cache.Get(key); if (IsUnlockable(clientLock, lockable)) { CacheLock @lock = (CacheLock) lockable; @@ -472,7 +455,7 @@ public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock cl else { //recache the updated state - cache.Put(key, new CachedItem(value, cache.NextTimestamp(), version)); + Cache.Put(key, new CachedItem(value, Cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Updated: {0}", key); @@ -488,7 +471,7 @@ public bool AfterUpdate(CacheKey key, object value, object version, ISoftLock cl } finally { - cache.Unlock(key); + _cacheBase.Unlock(key, lockValue); } } } @@ -502,14 +485,14 @@ public bool AfterInsert(CacheKey key, object value, object version) log.Debug("Inserting: {0}", key); } + var lockValue = _cacheBase.Lock(key); try { - cache.Lock(key); - - ILockable lockable = (ILockable) cache.Get(key); + + ILockable lockable = (ILockable) Cache.Get(key); if (lockable == null) { - cache.Put(key, new CachedItem(value, cache.NextTimestamp(), version)); + Cache.Put(key, new CachedItem(value, Cache.NextTimestamp(), version)); if (log.IsDebugEnabled()) { log.Debug("Inserted: {0}", key); @@ -523,7 +506,7 @@ public bool AfterInsert(CacheKey key, object value, object version) } finally { - cache.Unlock(key); + _cacheBase.Unlock(key, lockValue); } } } diff --git a/src/NHibernate/Cache/StandardQueryCache.cs b/src/NHibernate/Cache/StandardQueryCache.cs index a1643d0f998..1d54e5962a3 100644 --- a/src/NHibernate/Cache/StandardQueryCache.cs +++ b/src/NHibernate/Cache/StandardQueryCache.cs @@ -18,7 +18,6 @@ namespace NHibernate.Cache public partial class StandardQueryCache : IQueryCache { private static readonly INHibernateLogger Log = NHibernateLogger.For(typeof (StandardQueryCache)); - private readonly ICache _queryCache; private readonly string _regionName; private readonly UpdateTimestampsCache _updateTimestampsCache; @@ -33,17 +32,17 @@ public StandardQueryCache(Settings settings, IDictionary props, Log.Info("starting query cache at region: {0}", regionName); - _queryCache = settings.CacheProvider.BuildCache(regionName, props); + Cache = settings.CacheProvider.BuildCache(regionName, props); _updateTimestampsCache = updateTimestampsCache; _regionName = regionName; } #region IQueryCache Members - public ICache Cache - { - get { return _queryCache; } - } + // 6.0 TODO: type as CacheBase instead +#pragma warning disable 618 + public ICache Cache { get; } +#pragma warning restore 618 public string RegionName { @@ -52,7 +51,7 @@ public string RegionName public void Clear() { - _queryCache.Clear(); + Cache.Clear(); } public bool Put(QueryKey key, ICacheAssembler[] returnTypes, IList result, bool isNaturalKeyLookup, ISessionImplementor session) @@ -78,7 +77,7 @@ public bool Put(QueryKey key, ICacheAssembler[] returnTypes, IList result, bool } } - _queryCache.Put(key, cacheable); + Cache.Put(key, cacheable); return true; } @@ -88,7 +87,7 @@ public IList Get(QueryKey key, ICacheAssembler[] returnTypes, bool isNaturalKeyL if (Log.IsDebugEnabled()) Log.Debug("checking cached query results in region: '{0}'; {1}", _regionName, key); - var cacheable = (IList)_queryCache.Get(key); + var cacheable = (IList)Cache.Get(key); if (cacheable == null) { Log.Debug("query results were not found in cache: {0}", key); @@ -146,7 +145,7 @@ public IList Get(QueryKey key, ICacheAssembler[] returnTypes, bool isNaturalKeyL // the UnresolvableObjectException could occur while resolving // associations, leaving the PC in an inconsistent state Log.Debug(ex, "could not reassemble cached result set"); - _queryCache.Remove(key); + Cache.Remove(key); return null; } @@ -203,7 +202,7 @@ public void Destroy() { try { - _queryCache.Destroy(); + Cache.Destroy(); } catch (Exception e) { diff --git a/src/NHibernate/Cache/UpdateTimestampsCache.cs b/src/NHibernate/Cache/UpdateTimestampsCache.cs index 8641bbdab1b..7e9901e3bbf 100644 --- a/src/NHibernate/Cache/UpdateTimestampsCache.cs +++ b/src/NHibernate/Cache/UpdateTimestampsCache.cs @@ -17,8 +17,11 @@ namespace NHibernate.Cache public partial class UpdateTimestampsCache { private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(UpdateTimestampsCache)); - private ICache updateTimestamps; - private readonly IBatchableReadOnlyCache _batchUpdateTimestamps; + // 6.0 TODO: type as CacheBase instead +#pragma warning disable 618 + private readonly ICache updateTimestamps; +#pragma warning restore 618 + private readonly CacheBase _batchUpdateTimestamps; private readonly string regionName = typeof(UpdateTimestampsCache).Name; @@ -34,7 +37,7 @@ public UpdateTimestampsCache(Settings settings, IDictionary prop log.Info("starting update timestamps cache at region: {0}", regionName); updateTimestamps = settings.CacheProvider.BuildCache(regionName, props); // ReSharper disable once SuspiciousTypeConversion.Global - _batchUpdateTimestamps = updateTimestamps as IBatchableReadOnlyCache; + _batchUpdateTimestamps = updateTimestamps as CacheBase ?? new ObsoleteCacheWrapper(updateTimestamps); } //Since v5.1 @@ -82,33 +85,19 @@ public virtual void Invalidate(IReadOnlyCollection spaces) [MethodImpl(MethodImplOptions.Synchronized)] public virtual bool IsUpToDate(ISet spaces, long timestamp /* H2.1 has Long here */) { - if (_batchUpdateTimestamps != null) + var keys = new object[spaces.Count]; + var index = 0; + foreach (var space in spaces) { - var keys = new object[spaces.Count]; - var index = 0; - foreach (var space in spaces) - { - keys[index++] = space; - } - var lastUpdates = _batchUpdateTimestamps.GetMany(keys); - foreach (var lastUpdate in lastUpdates) - { - if (IsOutdated(lastUpdate, timestamp)) - { - return false; - } - } - return true; + keys[index++] = space; } - - foreach (string space in spaces) + var lastUpdates = _batchUpdateTimestamps.GetMany(keys); + foreach (var lastUpdate in lastUpdates) { - object lastUpdate = updateTimestamps.Get(space); if (IsOutdated(lastUpdate, timestamp)) { return false; } - } return true; } diff --git a/src/NHibernate/Engine/BatchFetchQueue.cs b/src/NHibernate/Engine/BatchFetchQueue.cs index 1c02286959b..57c03be65fd 100644 --- a/src/NHibernate/Engine/BatchFetchQueue.cs +++ b/src/NHibernate/Engine/BatchFetchQueue.cs @@ -1,12 +1,10 @@ using System; -using System.Collections; using NHibernate.Cache; using NHibernate.Collection; using NHibernate.Persister.Collection; using NHibernate.Persister.Entity; using NHibernate.Util; using System.Collections.Generic; -using System.Linq; using Iesi.Collections.Generic; namespace NHibernate.Engine @@ -237,7 +235,7 @@ internal object[] GetCollectionBatch(ICollectionPersister collectionPersister, o // List of collection entries that haven't been checked for their existance in the cache. Besides the collection entry, // the index where the entry was found is also stored in order to correctly order the returning keys. var collectionKeys = new List, int>>(batchSize); - var batchableCache = collectionPersister.Cache?.Cache as IBatchableReadOnlyCache; + var batchableCache = collectionPersister.Cache?.Cache as CacheBase; if (!batchLoadableCollections.TryGetValue(collectionPersister.Role, out var map)) { @@ -420,7 +418,8 @@ internal object[] GetEntityBatch(IEntityPersister persister, object id, int batc // List of entity keys that haven't been checked for their existance in the cache. Besides the entity key, // the index where the key was found is also stored in order to correctly order the returning keys. var entityKeys = new List>(batchSize); - var batchableCache = persister.Cache?.Cache as IBatchableReadOnlyCache; + // If there is a cache, obsolete or not, batchableCache will not be null. + var batchableCache = persister.Cache?.GetCacheBase(); if (!batchLoadableEntityKeys.TryGetValue(persister.EntityName, out var set)) { @@ -506,10 +505,10 @@ bool ProcessKey(EntityKey key, bool ignoreCache = false) return false; } - if (!checkCache || !IsCached(key, persister)) - { - ids[i++] = key.Identifier; - } + // No need to check "!checkCache || !IsCached(key, persister)": "batchableCache == null" + // already means there is no cache, so IsCached can only yield false. (This method is now + // removed.) + ids[i++] = key.Identifier; } else if (ignoreCache) { @@ -539,16 +538,6 @@ bool ProcessKey(EntityKey key, bool ignoreCache = false) } } - private bool IsCached(EntityKey entityKey, IEntityPersister persister) - { - if (persister.HasCache && context.Session.CacheMode.HasFlag(CacheMode.Get)) - { - CacheKey key = context.Session.GenerateCacheKey(entityKey.Identifier, persister.IdentifierType, entityKey.EntityName); - return persister.Cache.Cache.Get(key) != null; - } - return false; - } - private bool IsCached(object collectionKey, ICollectionPersister persister) { if (persister.HasCache && context.Session.CacheMode.HasFlag(CacheMode.Get)) @@ -569,7 +558,7 @@ private bool IsCached(object collectionKey, ICollectionPersister persister) /// Whether to check the cache or just return for all keys. /// An array of booleans that contains the result for each key. private bool[] AreCached(List> entityKeys, int[] keyIndexes, IEntityPersister persister, - IBatchableReadOnlyCache batchableCache, bool checkCache) + CacheBase batchableCache, bool checkCache) { var result = new bool[keyIndexes.Length]; if (!checkCache || !persister.HasCache || !context.Session.CacheMode.HasFlag(CacheMode.Get)) @@ -605,7 +594,7 @@ private bool[] AreCached(List> entityKeys, int[] ke /// Whether to check the cache or just return for all keys. /// An array of booleans that contains the result for each key. private bool[] AreCached(List, int>> collectionKeys, - int[] keyIndexes, ICollectionPersister persister, IBatchableReadOnlyCache batchableCache, + int[] keyIndexes, ICollectionPersister persister, CacheBase batchableCache, bool checkCache) { var result = new bool[keyIndexes.Length]; diff --git a/src/NHibernate/Engine/ISessionFactoryImplementor.cs b/src/NHibernate/Engine/ISessionFactoryImplementor.cs index 0f3d9972ecd..23d9f88429a 100644 --- a/src/NHibernate/Engine/ISessionFactoryImplementor.cs +++ b/src/NHibernate/Engine/ISessionFactoryImplementor.cs @@ -51,7 +51,10 @@ public interface ISessionFactoryImplementor : IMapping, ISessionFactory SQLFunctionRegistry SQLFunctionRegistry { get; } + // 6.0 TODO: type as CacheBase instead +#pragma warning disable 618 IDictionary GetAllSecondLevelCacheRegions(); +#pragma warning restore 618 /// /// Get the persister for the named entity @@ -126,7 +129,10 @@ public interface ISessionFactoryImplementor : IMapping, ISessionFactory IIdentifierGenerator GetIdentifierGenerator(string rootEntityName); /// Get a named second-level cache region + // 6.0 TODO: return CacheBase instead +#pragma warning disable 618 ICache GetSecondLevelCacheRegion(string regionName); +#pragma warning restore 618 // Obsolete since v5 /// diff --git a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs index 89f154af1bd..9f360ad563c 100644 --- a/src/NHibernate/Engine/Loading/CollectionLoadContext.cs +++ b/src/NHibernate/Engine/Loading/CollectionLoadContext.cs @@ -335,7 +335,7 @@ private void AddCollectionToCache(LoadingCollectionEntry lce, ICollectionPersist CollectionCacheEntry entry = new CollectionCacheEntry(lce.Collection, persister); CacheKey cacheKey = session.GenerateCacheKey(lce.Key, persister.KeyType, persister.Role); - if (persister.GetBatchSize() > 1 && persister.Cache.IsBatchingPutSupported()) + if (persister.GetBatchSize() > 1) { cacheBatchingHandler( new CachePutData( diff --git a/src/NHibernate/Engine/TwoPhaseLoad.cs b/src/NHibernate/Engine/TwoPhaseLoad.cs index c2df76ce4dc..7906d21212a 100644 --- a/src/NHibernate/Engine/TwoPhaseLoad.cs +++ b/src/NHibernate/Engine/TwoPhaseLoad.cs @@ -121,7 +121,7 @@ internal static void InitializeEntity(object entity, bool readOnly, ISessionImpl new CacheEntry(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity); CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName); - if (cacheBatchingHandler != null && persister.IsBatchLoadable && persister.Cache.IsBatchingPutSupported()) + if (cacheBatchingHandler != null && persister.IsBatchLoadable) { cacheBatchingHandler( persister, diff --git a/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs b/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs index f1bdf215f8b..e62a0319b24 100644 --- a/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultInitializeCollectionEventListener.cs @@ -79,7 +79,7 @@ private bool InitializeCollectionFromCache(object id, ICollectionPersister persi } var batchSize = persister.GetBatchSize(); - if (batchSize > 1 && persister.Cache.IsBatchingGetSupported()) + if (batchSize > 1 && persister.Cache.PreferMultipleGet()) { var collectionEntries = new CollectionEntry[batchSize]; // The first item in the array is the item that we want to load diff --git a/src/NHibernate/Event/Default/DefaultLoadEventListener.cs b/src/NHibernate/Event/Default/DefaultLoadEventListener.cs index df2a49b4a57..4e1f8e3e073 100644 --- a/src/NHibernate/Event/Default/DefaultLoadEventListener.cs +++ b/src/NHibernate/Event/Default/DefaultLoadEventListener.cs @@ -423,7 +423,7 @@ protected virtual object LoadFromSecondLevelCache(LoadEvent @event, IEntityPersi } ISessionFactoryImplementor factory = source.Factory; var batchSize = persister.GetBatchSize(); - if (batchSize > 1 && persister.Cache.IsBatchingGetSupported()) + if (batchSize > 1 && persister.Cache.PreferMultipleGet()) { // The first item in the array is the item that we want to load var entityBatch = diff --git a/src/NHibernate/Impl/SessionFactoryImpl.cs b/src/NHibernate/Impl/SessionFactoryImpl.cs index bb453cf6c90..8ef05d2e6b8 100644 --- a/src/NHibernate/Impl/SessionFactoryImpl.cs +++ b/src/NHibernate/Impl/SessionFactoryImpl.cs @@ -95,8 +95,11 @@ 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 allCacheRegions = new ConcurrentDictionary(); +#pragma warning restore 618 [NonSerialized] private readonly IDictionary classMetadata; @@ -1033,16 +1036,21 @@ public UpdateTimestampsCache UpdateTimestampsCache get { return updateTimestampsCache; } } + // 6.0 TODO: type as CacheBase instead +#pragma warning disable 618 public IDictionary GetAllSecondLevelCacheRegions() +#pragma warning restore 618 { // ToArray creates a moment in time snapshot return allCacheRegions.ToArray().ToDictionary(kv => kv.Key, kv => kv.Value); } + // 6.0 TODO: return CacheBase instead +#pragma warning disable 618 public ICache GetSecondLevelCacheRegion(string regionName) +#pragma warning restore 618 { - ICache result; - allCacheRegions.TryGetValue(regionName, out result); + allCacheRegions.TryGetValue(regionName, out var result); return result; } diff --git a/src/NHibernate/Stat/SecondLevelCacheStatistics.cs b/src/NHibernate/Stat/SecondLevelCacheStatistics.cs index 6c872cbca30..c29ed3b71c1 100644 --- a/src/NHibernate/Stat/SecondLevelCacheStatistics.cs +++ b/src/NHibernate/Stat/SecondLevelCacheStatistics.cs @@ -10,12 +10,18 @@ namespace NHibernate.Stat public class SecondLevelCacheStatistics : CategorizedStatistics { [NonSerialized] + // 6.0 TODO: type as CacheBase instead +#pragma warning disable 618 private readonly ICache cache; +#pragma warning restore 618 internal long hitCount; internal long missCount; internal long putCount; + // 6.0 TODO: get as CacheBase instead +#pragma warning disable 618 public SecondLevelCacheStatistics(ICache cache) : base(cache.RegionName) +#pragma warning restore 618 { this.cache = cache; } diff --git a/src/NHibernate/Stat/StatisticsImpl.cs b/src/NHibernate/Stat/StatisticsImpl.cs index 9e26f905013..2a17e3eb1eb 100644 --- a/src/NHibernate/Stat/StatisticsImpl.cs +++ b/src/NHibernate/Stat/StatisticsImpl.cs @@ -396,7 +396,7 @@ public SecondLevelCacheStatistics GetSecondLevelCacheStatistics(string regionNam { if (sessionFactory == null) return null; - ICache cache = sessionFactory.GetSecondLevelCacheRegion(regionName); + var cache = sessionFactory.GetSecondLevelCacheRegion(regionName); if (cache == null) return null; slcs = new SecondLevelCacheStatistics(cache);