Skip to content

Avoid NRE in cache strategies #2554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: 5.3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/NHibernate/Async/Cache/NonstrictReadWriteCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//------------------------------------------------------------------------------


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -26,6 +27,7 @@ public partial class NonstrictReadWriteCache : IBatchableCacheConcurrencyStrateg
public async Task<object> GetAsync(CacheKey key, long txTimestamp, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
if (log.IsDebugEnabled())
{
log.Debug("Cache lookup: {0}", key);
Expand All @@ -43,6 +45,7 @@ public async Task<object> GetAsync(CacheKey key, long txTimestamp, CancellationT
public async Task<object[]> GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
if (log.IsDebugEnabled())
{
log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable()));
Expand Down Expand Up @@ -73,6 +76,8 @@ public async Task<bool[]> PutManyAsync(
return result;
}

CheckCache();

var checkKeys = new List<object>();
var checkKeyIndexes = new List<int>();
for (var i = 0; i < minimalPuts.Length; i++)
Expand Down Expand Up @@ -135,6 +140,8 @@ public async Task<bool> PutAsync(CacheKey key, object value, long txTimestamp, o
return false;
}

CheckCache();

if (minimalPut && await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false) != null)
{
if (log.IsDebugEnabled())
Expand Down Expand Up @@ -164,7 +171,7 @@ public Task<ISoftLock> LockAsync(CacheKey key, object version, CancellationToken
{
return Task.FromResult<ISoftLock>(Lock(key, version));
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<ISoftLock>(ex);
}
Expand All @@ -178,13 +185,14 @@ public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken)
}
try
{
CheckCache();
if (log.IsDebugEnabled())
{
log.Debug("Removing: {0}", key);
}
return Cache.RemoveAsync(key, cancellationToken);
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
Expand All @@ -198,13 +206,14 @@ public Task ClearAsync(CancellationToken cancellationToken)
}
try
{
CheckCache();
if (log.IsDebugEnabled())
{
log.Debug("Clearing");
}
return Cache.ClearAsync(cancellationToken);
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
Expand All @@ -221,13 +230,14 @@ public Task EvictAsync(CacheKey key, CancellationToken cancellationToken)
}
try
{
CheckCache();
if (log.IsDebugEnabled())
{
log.Debug("Invalidating: {0}", key);
}
return Cache.RemoveAsync(key, cancellationToken);
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
Expand All @@ -254,14 +264,15 @@ public Task ReleaseAsync(CacheKey key, ISoftLock @lock, CancellationToken cancel
}
try
{
CheckCache();
if (log.IsDebugEnabled())
{
log.Debug("Invalidating (again): {0}", key);
}

return Cache.RemoveAsync(key, cancellationToken);
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
Expand Down Expand Up @@ -290,7 +301,7 @@ public Task<bool> AfterInsertAsync(CacheKey key, object value, object version, C
{
return Task.FromResult<bool>(AfterInsert(key, value, version));
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<bool>(ex);
}
Expand Down
26 changes: 24 additions & 2 deletions src/NHibernate/Async/Cache/ReadOnlyCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public partial class ReadOnlyCache : IBatchableCacheConcurrencyStrategy
public async Task<object> GetAsync(CacheKey key, long timestamp, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
var result = await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false);
if (log.IsDebugEnabled())
{
Expand All @@ -36,6 +37,7 @@ public async Task<object> GetAsync(CacheKey key, long timestamp, CancellationTok
public async Task<object[]> GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
if (log.IsDebugEnabled())
{
log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable()));
Expand Down Expand Up @@ -82,6 +84,8 @@ public async Task<bool[]> PutManyAsync(
return result;
}

CheckCache();

var checkKeys = new List<CacheKey>();
var checkKeyIndexes = new List<int>();
for (var i = 0; i < minimalPuts.Length; i++)
Expand Down Expand Up @@ -141,6 +145,8 @@ public async Task<bool> PutAsync(CacheKey key, object value, long timestamp, obj
return false;
}

CheckCache();

if (minimalPut && await (Cache.GetAsync(key, cancellationToken)).ConfigureAwait(false) != null)
{
if (log.IsDebugEnabled())
Expand Down Expand Up @@ -183,7 +189,15 @@ public Task ClearAsync(CancellationToken cancellationToken)
{
return Task.FromCanceled<object>(cancellationToken);
}
return Cache.ClearAsync(cancellationToken);
try
{
CheckCache();
return Cache.ClearAsync(cancellationToken);
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}

public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken)
Expand All @@ -192,7 +206,15 @@ public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken)
{
return Task.FromCanceled<object>(cancellationToken);
}
return Cache.RemoveAsync(key, cancellationToken);
try
{
CheckCache();
return Cache.RemoveAsync(key, cancellationToken);
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}

/// <summary>
Expand Down
45 changes: 39 additions & 6 deletions src/NHibernate/Async/Cache/ReadWriteCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
//------------------------------------------------------------------------------


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -40,6 +41,8 @@ public partial class ReadWriteCache : IBatchableCacheConcurrencyStrategy
/// </remarks>
public async Task<object> GetAsync(CacheKey key, long txTimestamp, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
cancellationToken.ThrowIfCancellationRequested();
using (await (_asyncReaderWriterLock.ReadLockAsync()).ConfigureAwait(false))
{
Expand All @@ -65,6 +68,7 @@ public async Task<object> GetAsync(CacheKey key, long txTimestamp, CancellationT
public async Task<object[]> GetManyAsync(CacheKey[] keys, long timestamp, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
if (log.IsDebugEnabled())
{
log.Debug("Cache lookup: {0}", string.Join(",", keys.AsEnumerable()));
Expand Down Expand Up @@ -92,6 +96,8 @@ public async Task<object[]> GetManyAsync(CacheKey[] keys, long timestamp, Cancel
/// </summary>
public async Task<ISoftLock> LockAsync(CacheKey key, object version, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
cancellationToken.ThrowIfCancellationRequested();
using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
{
Expand Down Expand Up @@ -136,6 +142,8 @@ public async Task<bool[]> PutManyAsync(
// MinValue means cache is disabled
return result;
}

CheckCache();
cancellationToken.ThrowIfCancellationRequested();

using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
Expand Down Expand Up @@ -207,6 +215,8 @@ public async Task<bool> PutAsync(CacheKey key, object value, long txTimestamp, o
// MinValue means cache is disabled
return false;
}

CheckCache();
cancellationToken.ThrowIfCancellationRequested();

using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
Expand Down Expand Up @@ -264,14 +274,16 @@ private Task DecrementLockAsync(object key, CacheLock @lock, CancellationToken c
@lock.Unlock(Cache.NextTimestamp());
return Cache.PutAsync(key, @lock, cancellationToken);
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}

public async Task ReleaseAsync(CacheKey key, ISoftLock clientLock, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
cancellationToken.ThrowIfCancellationRequested();
using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
{
Expand Down Expand Up @@ -308,14 +320,15 @@ internal Task HandleLockExpiryAsync(object key, CancellationToken cancellationTo
}
try
{
CheckCache();
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;
// create new lock that times out immediately
CacheLock @lock = CacheLock.Create(ts, NextLockId(), null);
@lock.Unlock(ts);
return Cache.PutAsync(key, @lock, cancellationToken);
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
Expand All @@ -327,7 +340,15 @@ public Task ClearAsync(CancellationToken cancellationToken)
{
return Task.FromCanceled<object>(cancellationToken);
}
return Cache.ClearAsync(cancellationToken);
try
{
CheckCache();
return Cache.ClearAsync(cancellationToken);
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}

public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken)
Expand All @@ -336,7 +357,15 @@ public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken)
{
return Task.FromCanceled<object>(cancellationToken);
}
return Cache.RemoveAsync(key, cancellationToken);
try
{
CheckCache();
return Cache.RemoveAsync(key, cancellationToken);
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}

/// <summary>
Expand All @@ -345,6 +374,8 @@ public Task RemoveAsync(CacheKey key, CancellationToken cancellationToken)
/// </summary>
public async Task<bool> AfterUpdateAsync(CacheKey key, object value, object version, ISoftLock clientLock, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
cancellationToken.ThrowIfCancellationRequested();
using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
{
Expand Down Expand Up @@ -392,6 +423,8 @@ public async Task<bool> AfterUpdateAsync(CacheKey key, object value, object vers

public async Task<bool> AfterInsertAsync(CacheKey key, object value, object version, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
CheckCache();
cancellationToken.ThrowIfCancellationRequested();
using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
{
Expand Down Expand Up @@ -436,7 +469,7 @@ public Task EvictAsync(CacheKey key, CancellationToken cancellationToken)
Evict(key);
return Task.CompletedTask;
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
Expand All @@ -452,7 +485,7 @@ public Task<bool> UpdateAsync(CacheKey key, object value, object currentVersion,
{
return Task.FromResult<bool>(Update(key, value, currentVersion, previousVersion));
}
catch (System.Exception ex)
catch (Exception ex)
{
return Task.FromException<bool>(ex);
}
Expand Down
2 changes: 1 addition & 1 deletion src/NHibernate/Cache/CacheFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static ICacheConcurrencyStrategy CreateCache(
public static ICacheConcurrencyStrategy CreateCache(string usage, CacheBase cache)
{
if (log.IsDebugEnabled())
log.Debug("cache for: {0} usage strategy: {1}", cache.RegionName, usage);
log.Debug("cache for: {0} usage strategy: {1}", cache?.RegionName, usage);

ICacheConcurrencyStrategy ccs;
switch (usage)
Expand Down
Loading