Skip to content

Commit 07ecb9f

Browse files
authored
Remove locks from UpdateTimestampsCache to improve performance (#2742)
See #2735
1 parent d2599c6 commit 07ecb9f

File tree

2 files changed

+66
-80
lines changed

2 files changed

+66
-80
lines changed

src/NHibernate/Async/Cache/UpdateTimestampsCache.cs

+42-40
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
using System;
1212
using System.Collections.Generic;
1313
using System.Linq;
14-
using System.Runtime.CompilerServices;
15-
1614
using NHibernate.Cfg;
1715
using NHibernate.Util;
1816

@@ -51,20 +49,26 @@ public Task PreInvalidateAsync(object[] spaces, CancellationToken cancellationTo
5149
}
5250
}
5351

54-
public virtual async Task PreInvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
52+
public virtual Task PreInvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
5553
{
56-
cancellationToken.ThrowIfCancellationRequested();
57-
if (spaces.Count == 0)
58-
return;
59-
cancellationToken.ThrowIfCancellationRequested();
60-
61-
using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
54+
if (cancellationToken.IsCancellationRequested)
55+
{
56+
return Task.FromCanceled<object>(cancellationToken);
57+
}
58+
try
6259
{
60+
if (spaces.Count == 0)
61+
return Task.CompletedTask;
62+
6363
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
6464
var ts = _updateTimestamps.NextTimestamp() + _updateTimestamps.Timeout;
65-
await (SetSpacesTimestampAsync(spaces, ts, cancellationToken)).ConfigureAwait(false);
65+
return SetSpacesTimestampAsync(spaces, ts, cancellationToken);
6666
//TODO: return new Lock(ts);
6767
}
68+
catch (Exception ex)
69+
{
70+
return Task.FromException<object>(ex);
71+
}
6872
}
6973

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

89-
public virtual async Task InvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
93+
public virtual Task InvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
9094
{
91-
cancellationToken.ThrowIfCancellationRequested();
92-
if (spaces.Count == 0)
93-
return;
94-
cancellationToken.ThrowIfCancellationRequested();
95-
96-
using (await (_asyncReaderWriterLock.WriteLockAsync()).ConfigureAwait(false))
95+
if (cancellationToken.IsCancellationRequested)
96+
{
97+
return Task.FromCanceled<object>(cancellationToken);
98+
}
99+
try
97100
{
101+
if (spaces.Count == 0)
102+
return Task.CompletedTask;
103+
98104
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
99105
long ts = _updateTimestamps.NextTimestamp();
100106
//TODO: if lock.getTimestamp().equals(ts)
101107
if (log.IsDebugEnabled())
102108
log.Debug("Invalidating spaces [{0}]", StringHelper.CollectionToString(spaces));
103-
await (SetSpacesTimestampAsync(spaces, ts, cancellationToken)).ConfigureAwait(false);
109+
return SetSpacesTimestampAsync(spaces, ts, cancellationToken);
110+
}
111+
catch (Exception ex)
112+
{
113+
return Task.FromException<object>(ex);
104114
}
105115
}
106116

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

132-
using (await (_asyncReaderWriterLock.ReadLockAsync()).ConfigureAwait(false))
133-
{
134-
var lastUpdates = await (_updateTimestamps.GetManyAsync(spaces.ToArray<object>(), cancellationToken)).ConfigureAwait(false);
135-
return lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp));
136-
}
141+
var lastUpdates = await (_updateTimestamps.GetManyAsync(spaces.ToArray<object>(), cancellationToken)).ConfigureAwait(false);
142+
return lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp));
137143
}
138144

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

154160
var keys = allSpaces.ToArray<object>();
155-
cancellationToken.ThrowIfCancellationRequested();
156161

157-
using (await (_asyncReaderWriterLock.ReadLockAsync()).ConfigureAwait(false))
158-
{
159-
var index = 0;
160-
var lastUpdatesBySpace =
161-
(await (_updateTimestamps
162-
.GetManyAsync(keys, cancellationToken)).ConfigureAwait(false))
163-
.ToDictionary(u => keys[index++], u => u as long?);
164-
165-
var results = new bool[spaces.Length];
166-
for (var i = 0; i < spaces.Length; i++)
167-
{
168-
var timestamp = timestamps[i];
169-
results[i] = spaces[i].All(space => !IsOutdated(lastUpdatesBySpace[space], timestamp));
170-
}
162+
var index = 0;
163+
var lastUpdatesBySpace =
164+
(await (_updateTimestamps
165+
.GetManyAsync(keys, cancellationToken)).ConfigureAwait(false))
166+
.ToDictionary(u => keys[index++], u => u as long?);
171167

172-
return results;
168+
var results = new bool[spaces.Length];
169+
for (var i = 0; i < spaces.Length; i++)
170+
{
171+
var timestamp = timestamps[i];
172+
results[i] = spaces[i].All(space => !IsOutdated(lastUpdatesBySpace[space], timestamp));
173173
}
174+
175+
return results;
174176
}
175177
}
176178
}

src/NHibernate/Cache/UpdateTimestampsCache.cs

+24-40
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Runtime.CompilerServices;
5-
64
using NHibernate.Cfg;
75
using NHibernate.Util;
86

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

2421
public virtual void Clear()
2522
{
@@ -60,13 +57,10 @@ public virtual void PreInvalidate(IReadOnlyCollection<string> spaces)
6057
if (spaces.Count == 0)
6158
return;
6259

63-
using (_asyncReaderWriterLock.WriteLock())
64-
{
65-
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
66-
var ts = _updateTimestamps.NextTimestamp() + _updateTimestamps.Timeout;
67-
SetSpacesTimestamp(spaces, ts);
68-
//TODO: return new Lock(ts);
69-
}
60+
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
61+
var ts = _updateTimestamps.NextTimestamp() + _updateTimestamps.Timeout;
62+
SetSpacesTimestamp(spaces, ts);
63+
//TODO: return new Lock(ts);
7064
}
7165

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

85-
using (_asyncReaderWriterLock.WriteLock())
86-
{
87-
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
88-
long ts = _updateTimestamps.NextTimestamp();
89-
//TODO: if lock.getTimestamp().equals(ts)
90-
if (log.IsDebugEnabled())
91-
log.Debug("Invalidating spaces [{0}]", StringHelper.CollectionToString(spaces));
92-
SetSpacesTimestamp(spaces, ts);
93-
}
79+
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
80+
long ts = _updateTimestamps.NextTimestamp();
81+
//TODO: if lock.getTimestamp().equals(ts)
82+
if (log.IsDebugEnabled())
83+
log.Debug("Invalidating spaces [{0}]", StringHelper.CollectionToString(spaces));
84+
SetSpacesTimestamp(spaces, ts);
9485
}
9586

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

108-
using (_asyncReaderWriterLock.ReadLock())
109-
{
110-
var lastUpdates = _updateTimestamps.GetMany(spaces.ToArray<object>());
111-
return lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp));
112-
}
99+
var lastUpdates = _updateTimestamps.GetMany(spaces.ToArray<object>());
100+
return lastUpdates.All(lastUpdate => !IsOutdated(lastUpdate as long?, timestamp));
113101
}
114102

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

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

131-
using (_asyncReaderWriterLock.ReadLock())
132-
{
133-
var index = 0;
134-
var lastUpdatesBySpace =
135-
_updateTimestamps
136-
.GetMany(keys)
137-
.ToDictionary(u => keys[index++], u => u as long?);
138-
139-
var results = new bool[spaces.Length];
140-
for (var i = 0; i < spaces.Length; i++)
141-
{
142-
var timestamp = timestamps[i];
143-
results[i] = spaces[i].All(space => !IsOutdated(lastUpdatesBySpace[space], timestamp));
144-
}
119+
var index = 0;
120+
var lastUpdatesBySpace =
121+
_updateTimestamps
122+
.GetMany(keys)
123+
.ToDictionary(u => keys[index++], u => u as long?);
145124

146-
return results;
125+
var results = new bool[spaces.Length];
126+
for (var i = 0; i < spaces.Length; i++)
127+
{
128+
var timestamp = timestamps[i];
129+
results[i] = spaces[i].All(space => !IsOutdated(lastUpdatesBySpace[space], timestamp));
147130
}
131+
132+
return results;
148133
}
149134

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

159143
private static bool IsOutdated(long? lastUpdate, long timestamp)

0 commit comments

Comments
 (0)