Skip to content

Commit

Permalink
Remove locks from UpdateTimestampsCache to improve performance (#2742)
Browse files Browse the repository at this point in the history
See #2735
  • Loading branch information
hazzik committed Apr 30, 2021
1 parent d2599c6 commit 07ecb9f
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 80 deletions.
82 changes: 42 additions & 40 deletions src/NHibernate/Async/Cache/UpdateTimestampsCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;

using NHibernate.Cfg;
using NHibernate.Util;

Expand Down Expand Up @@ -51,20 +49,26 @@ public Task PreInvalidateAsync(object[] spaces, CancellationToken cancellationTo
}
}

public virtual async Task PreInvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
public virtual Task PreInvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (spaces.Count == 0)
return;
cancellationToken.ThrowIfCancellationRequested();

using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object>(cancellationToken);
}
try
{
if (spaces.Count == 0)
return Task.CompletedTask;

//TODO: to handle concurrent writes correctly, this should return a Lock to the client
var ts = _updateTimestamps.NextTimestamp() + _updateTimestamps.Timeout;
await (SetSpacesTimestampAsync(spaces, ts, cancellationToken)).ConfigureAwait(false);
return SetSpacesTimestampAsync(spaces, ts, cancellationToken);
//TODO: return new Lock(ts);
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}

//Since v5.1
Expand All @@ -86,21 +90,27 @@ public Task InvalidateAsync(object[] spaces, CancellationToken cancellationToken
}
}

public virtual async Task InvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
public virtual Task InvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (spaces.Count == 0)
return;
cancellationToken.ThrowIfCancellationRequested();

using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<object>(cancellationToken);
}
try
{
if (spaces.Count == 0)
return Task.CompletedTask;

//TODO: to handle concurrent writes correctly, the client should pass in a Lock
long ts = _updateTimestamps.NextTimestamp();
//TODO: if lock.getTimestamp().equals(ts)
if (log.IsDebugEnabled())
log.Debug("Invalidating spaces [{0}]", StringHelper.CollectionToString(spaces));
await (SetSpacesTimestampAsync(spaces, ts, cancellationToken)).ConfigureAwait(false);
return SetSpacesTimestampAsync(spaces, ts, cancellationToken);
}
catch (Exception ex)
{
return Task.FromException<object>(ex);
}
}

Expand All @@ -127,13 +137,9 @@ public virtual async Task<bool> IsUpToDateAsync(ISet<string> spaces, long timest
cancellationToken.ThrowIfCancellationRequested();
if (spaces.Count == 0)
return true;
cancellationToken.ThrowIfCancellationRequested();

using (await (_asyncReaderWriterLock.ReadLockAsync()).ConfigureAwait(false))
{
var lastUpdates = await (_updateTimestamps.GetManyAsync(spaces.ToArray<object>(), cancellationToken)).ConfigureAwait(false);
return lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp));
}
var lastUpdates = await (_updateTimestamps.GetManyAsync(spaces.ToArray<object>(), cancellationToken)).ConfigureAwait(false);
return lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp));
}

public virtual async Task<bool[]> AreUpToDateAsync(ISet<string>[] spaces, long[] timestamps, CancellationToken cancellationToken)
Expand All @@ -152,25 +158,21 @@ public virtual async Task<bool[]> AreUpToDateAsync(ISet<string>[] spaces, long[]
return ArrayHelper.Fill(true, spaces.Length);

var keys = allSpaces.ToArray<object>();
cancellationToken.ThrowIfCancellationRequested();

using (await (_asyncReaderWriterLock.ReadLockAsync()).ConfigureAwait(false))
{
var index = 0;
var lastUpdatesBySpace =
(await (_updateTimestamps
.GetManyAsync(keys, cancellationToken)).ConfigureAwait(false))
.ToDictionary(u => keys[index++], u => u as long?);

var results = new bool[spaces.Length];
for (var i = 0; i < spaces.Length; i++)
{
var timestamp = timestamps[i];
results[i] = spaces[i].All(space => !IsOutdated(lastUpdatesBySpace[space], timestamp));
}
var index = 0;
var lastUpdatesBySpace =
(await (_updateTimestamps
.GetManyAsync(keys, cancellationToken)).ConfigureAwait(false))
.ToDictionary(u => keys[index++], u => u as long?);

return results;
var results = new bool[spaces.Length];
for (var i = 0; i < spaces.Length; i++)
{
var timestamp = timestamps[i];
results[i] = spaces[i].All(space => !IsOutdated(lastUpdatesBySpace[space], timestamp));
}

return results;
}
}
}
64 changes: 24 additions & 40 deletions src/NHibernate/Cache/UpdateTimestampsCache.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;

using NHibernate.Cfg;
using NHibernate.Util;

Expand All @@ -19,7 +17,6 @@ public partial class UpdateTimestampsCache
{
private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(UpdateTimestampsCache));
private readonly CacheBase _updateTimestamps;
private readonly AsyncReaderWriterLock _asyncReaderWriterLock = new AsyncReaderWriterLock();

public virtual void Clear()
{
Expand Down Expand Up @@ -60,13 +57,10 @@ public virtual void PreInvalidate(IReadOnlyCollection<string> spaces)
if (spaces.Count == 0)
return;

using (_asyncReaderWriterLock.WriteLock())
{
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
var ts = _updateTimestamps.NextTimestamp() + _updateTimestamps.Timeout;
SetSpacesTimestamp(spaces, ts);
//TODO: return new Lock(ts);
}
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
var ts = _updateTimestamps.NextTimestamp() + _updateTimestamps.Timeout;
SetSpacesTimestamp(spaces, ts);
//TODO: return new Lock(ts);
}

//Since v5.1
Expand All @@ -82,15 +76,12 @@ public virtual void Invalidate(IReadOnlyCollection<string> spaces)
if (spaces.Count == 0)
return;

using (_asyncReaderWriterLock.WriteLock())
{
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
long ts = _updateTimestamps.NextTimestamp();
//TODO: if lock.getTimestamp().equals(ts)
if (log.IsDebugEnabled())
log.Debug("Invalidating spaces [{0}]", StringHelper.CollectionToString(spaces));
SetSpacesTimestamp(spaces, ts);
}
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
long ts = _updateTimestamps.NextTimestamp();
//TODO: if lock.getTimestamp().equals(ts)
if (log.IsDebugEnabled())
log.Debug("Invalidating spaces [{0}]", StringHelper.CollectionToString(spaces));
SetSpacesTimestamp(spaces, ts);
}

private void SetSpacesTimestamp(IReadOnlyCollection<string> spaces, long ts)
Expand All @@ -105,11 +96,8 @@ public virtual bool IsUpToDate(ISet<string> spaces, long timestamp /* H2.1 has L
if (spaces.Count == 0)
return true;

using (_asyncReaderWriterLock.ReadLock())
{
var lastUpdates = _updateTimestamps.GetMany(spaces.ToArray<object>());
return lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp));
}
var lastUpdates = _updateTimestamps.GetMany(spaces.ToArray<object>());
return lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp));
}

public virtual bool[] AreUpToDate(ISet<string>[] spaces, long[] timestamps)
Expand All @@ -128,23 +116,20 @@ public virtual bool[] AreUpToDate(ISet<string>[] spaces, long[] timestamps)

var keys = allSpaces.ToArray<object>();

using (_asyncReaderWriterLock.ReadLock())
{
var index = 0;
var lastUpdatesBySpace =
_updateTimestamps
.GetMany(keys)
.ToDictionary(u => keys[index++], u => u as long?);

var results = new bool[spaces.Length];
for (var i = 0; i < spaces.Length; i++)
{
var timestamp = timestamps[i];
results[i] = spaces[i].All(space => !IsOutdated(lastUpdatesBySpace[space], timestamp));
}
var index = 0;
var lastUpdatesBySpace =
_updateTimestamps
.GetMany(keys)
.ToDictionary(u => keys[index++], u => u as long?);

return results;
var results = new bool[spaces.Length];
for (var i = 0; i < spaces.Length; i++)
{
var timestamp = timestamps[i];
results[i] = spaces[i].All(space => !IsOutdated(lastUpdatesBySpace[space], timestamp));
}

return results;
}

// Since v5.3
Expand All @@ -153,7 +138,6 @@ public virtual void Destroy()
{
// The cache is externally provided and may be shared. Destroying the cache is
// not the responsibility of this class.
_asyncReaderWriterLock.Dispose();
}

private static bool IsOutdated(long? lastUpdate, long timestamp)
Expand Down

0 comments on commit 07ecb9f

Please sign in to comment.