Skip to content

Commit 5878986

Browse files
authored
Merge pull request #1 from hazzik/gh-1467
Move invalidation of caches to ActionQueue
2 parents 38e569c + bcf4965 commit 5878986

File tree

5 files changed

+124
-123
lines changed

5 files changed

+124
-123
lines changed

src/NHibernate.Test/SecondLevelCacheTest/InvalidationTests.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ protected override void Configure(Configuration configuration)
3131
public void InvalidatesEntities()
3232
{
3333
var cache = Substitute.For<UpdateTimestampsCache>(Sfi.Settings, new Dictionary<string, string>());
34-
((SessionFactoryImpl) (Sfi as DebugSessionFactory).ActualFactory).SetPropertyUsingReflection(x => x.UpdateTimestampsCache, cache);
34+
((SessionFactoryImpl) (Sfi as DebugSessionFactory).ActualFactory).SetPropertyUsingReflection(
35+
x => x.UpdateTimestampsCache,
36+
cache);
3537

3638
var items = new List<Item>();
3739
using (ISession session = OpenSession())
@@ -40,9 +42,10 @@ public void InvalidatesEntities()
4042
{
4143
foreach (var i in Enumerable.Range(1, 10))
4244
{
43-
var item = new Item { Id = i };
45+
var item = new Item {Id = i};
4446
session.Save(item);
4547
}
48+
4649
tx.Commit();
4750
}
4851

@@ -53,6 +56,7 @@ public void InvalidatesEntities()
5356
var item = session.Get<Item>(i);
5457
item.Name = item.Id.ToString();
5558
}
59+
5660
tx.Commit();
5761
}
5862

@@ -63,13 +67,14 @@ public void InvalidatesEntities()
6367
var item = session.Get<Item>(i);
6468
session.Delete(item);
6569
}
70+
6671
tx.Commit();
6772
}
6873
}
69-
//Should receive one preinvalidation and one invalidation per commit
70-
cache.Received(3).PreInvalidate(Arg.Is<object[]>(x => x.Length==1 && (string)x[0] == "Item"));
71-
cache.Received(3).Invalidate(Arg.Is<object[]>(x => x.Length == 1 && (string) x[0] == "Item"));
7274

75+
//Should receive one preinvalidation and one invalidation per commit
76+
cache.Received(3).PreInvalidate(Arg.Is<IReadOnlyCollection<object>>(x => x.Count == 1 && (string) x.First() == "Item"));
77+
cache.Received(3).Invalidate(Arg.Is<IReadOnlyCollection<object>>(x => x.Count == 1 && (string) x.First() == "Item"));
7378
}
7479

7580
public void CleanUp()

src/NHibernate/Async/Cache/UpdateTimestampsCache.cs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,37 +33,57 @@ public Task ClearAsync(CancellationToken cancellationToken)
3333
return updateTimestamps.ClearAsync(cancellationToken);
3434
}
3535

36+
public virtual Task PreInvalidateAsync(object[] spaces, CancellationToken cancellationToken)
37+
{
38+
if (cancellationToken.IsCancellationRequested)
39+
{
40+
return Task.FromCanceled<object>(cancellationToken);
41+
}
42+
return PreInvalidateAsync((IReadOnlyList<object>) spaces, cancellationToken);
43+
}
44+
3645
[MethodImpl()]
37-
public virtual async Task PreInvalidateAsync(object[] spaces, CancellationToken cancellationToken)
46+
public virtual async Task PreInvalidateAsync(IReadOnlyCollection<object> spaces, CancellationToken cancellationToken)
3847
{
3948
cancellationToken.ThrowIfCancellationRequested();
4049
using (await _preInvalidate.LockAsync())
4150
{
4251
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
4352
long ts = updateTimestamps.NextTimestamp() + updateTimestamps.Timeout;
44-
for (int i = 0; i < spaces.Length; i++)
53+
foreach (var space in spaces)
4554
{
46-
await (updateTimestamps.PutAsync(spaces[i], ts, cancellationToken)).ConfigureAwait(false);
55+
await (updateTimestamps.PutAsync(space, ts, cancellationToken)).ConfigureAwait(false);
4756
}
57+
4858
//TODO: return new Lock(ts);
4959
}
60+
5061
//TODO: return new Lock(ts);
5162
}
5263

5364
/// <summary></summary>
65+
public Task InvalidateAsync(object[] spaces, CancellationToken cancellationToken)
66+
{
67+
if (cancellationToken.IsCancellationRequested)
68+
{
69+
return Task.FromCanceled<object>(cancellationToken);
70+
}
71+
return InvalidateAsync((IReadOnlyList<object>) spaces, cancellationToken);
72+
}
73+
5474
[MethodImpl()]
55-
public virtual async Task InvalidateAsync(object[] spaces, CancellationToken cancellationToken)
75+
public virtual async Task InvalidateAsync(IReadOnlyCollection<object> spaces, CancellationToken cancellationToken)
5676
{
5777
cancellationToken.ThrowIfCancellationRequested();
5878
using (await _invalidate.LockAsync())
5979
{
6080
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
6181
long ts = updateTimestamps.NextTimestamp();
6282
//TODO: if lock.getTimestamp().equals(ts)
63-
for (int i = 0; i < spaces.Length; i++)
83+
foreach (var space in spaces)
6484
{
65-
log.Debug(string.Format("Invalidating space [{0}]", spaces[i]));
66-
await (updateTimestamps.PutAsync(spaces[i], ts, cancellationToken)).ConfigureAwait(false);
85+
log.Debug(string.Format("Invalidating space [{0}]", space));
86+
await (updateTimestamps.PutAsync(space, ts, cancellationToken)).ConfigureAwait(false);
6787
}
6888
}
6989
}

src/NHibernate/Async/Engine/ActionQueue.cs

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,26 @@ private async Task ExecuteActionsAsync(IList list, CancellationToken cancellatio
3838
}
3939
list.Clear();
4040
await (session.Batcher.ExecuteBatchAsync(cancellationToken)).ConfigureAwait(false);
41-
4241
}
4342

44-
private async Task AfterExecutionsAsync(CancellationToken cancellationToken)
43+
private Task PreInvalidateCachesAsync(CancellationToken cancellationToken)
4544
{
46-
cancellationToken.ThrowIfCancellationRequested();
47-
if (session.Factory.Settings.IsQueryCacheEnabled)
45+
if (cancellationToken.IsCancellationRequested)
4846
{
49-
var spaces = executedSpaces.ToArray();
50-
afterTransactionProcesses.AddSpacesToInvalidate(spaces);
51-
await (session.Factory.UpdateTimestampsCache.PreInvalidateAsync(spaces, cancellationToken)).ConfigureAwait(false);
47+
return Task.FromCanceled<object>(cancellationToken);
48+
}
49+
try
50+
{
51+
if (session.Factory.Settings.IsQueryCacheEnabled)
52+
{
53+
return session.Factory.UpdateTimestampsCache.PreInvalidateAsync(executedSpaces, cancellationToken);
54+
}
55+
return Task.CompletedTask;
56+
}
57+
catch (Exception ex)
58+
{
59+
return Task.FromException<object>(ex);
5260
}
53-
executedSpaces.Clear();
5461
}
5562

5663
public async Task ExecuteAsync(IExecutable executable, CancellationToken cancellationToken)
@@ -62,23 +69,23 @@ public async Task ExecuteAsync(IExecutable executable, CancellationToken cancell
6269
}
6370
finally
6471
{
65-
await (AfterExecutionsAsync(cancellationToken)).ConfigureAwait(false);
72+
await (PreInvalidateCachesAsync(cancellationToken)).ConfigureAwait(false);
6673
}
6774
}
6875

6976
private async Task InnerExecuteAsync(IExecutable executable, CancellationToken cancellationToken)
7077
{
7178
cancellationToken.ThrowIfCancellationRequested();
72-
if (executable.PropertySpaces != null)
73-
{
74-
executedSpaces.UnionWith(executable.PropertySpaces);
75-
}
7679
try
7780
{
7881
await (executable.ExecuteAsync(cancellationToken)).ConfigureAwait(false);
7982
}
8083
finally
8184
{
85+
if (executable.PropertySpaces != null)
86+
{
87+
executedSpaces.UnionWith(executable.PropertySpaces);
88+
}
8289
RegisterCleanupActions(executable);
8390
}
8491
}
@@ -96,7 +103,7 @@ public async Task ExecuteInsertsAsync(CancellationToken cancellationToken)
96103
}
97104
finally
98105
{
99-
await (AfterExecutionsAsync(cancellationToken)).ConfigureAwait(false);
106+
await (PreInvalidateCachesAsync(cancellationToken)).ConfigureAwait(false);
100107
}
101108
}
102109

@@ -118,11 +125,11 @@ public async Task ExecuteActionsAsync(CancellationToken cancellationToken)
118125
}
119126
finally
120127
{
121-
await (AfterExecutionsAsync(cancellationToken)).ConfigureAwait(false);
128+
await (PreInvalidateCachesAsync(cancellationToken)).ConfigureAwait(false);
122129
}
123130
}
124131

125-
private async Task PrepareActionsAsync(IList queue, CancellationToken cancellationToken)
132+
private static async Task PrepareActionsAsync(IList queue, CancellationToken cancellationToken)
126133
{
127134
cancellationToken.ThrowIfCancellationRequested();
128135
foreach (IExecutable executable in queue)
@@ -140,7 +147,7 @@ public async Task PrepareActionsAsync(CancellationToken cancellationToken)
140147
await (PrepareActionsAsync(collectionUpdates, cancellationToken)).ConfigureAwait(false);
141148
await (PrepareActionsAsync(collectionCreations, cancellationToken)).ConfigureAwait(false);
142149
}
143-
150+
144151
/// <summary>
145152
/// Performs cleanup of any held cache softlocks.
146153
/// </summary>
@@ -152,41 +159,27 @@ public Task AfterTransactionCompletionAsync(bool success, CancellationToken canc
152159
{
153160
return Task.FromCanceled<object>(cancellationToken);
154161
}
155-
return afterTransactionProcesses.AfterTransactionCompletionAsync(success, cancellationToken);
162+
try
163+
{
164+
afterTransactionProcesses.AfterTransactionCompletion(success);
165+
166+
return InvalidateCachesAsync(cancellationToken);
167+
}
168+
catch (Exception ex)
169+
{
170+
return Task.FromException<object>(ex);
171+
}
156172
}
157-
private partial class AfterTransactionCompletionProcessQueue
173+
174+
private async Task InvalidateCachesAsync(CancellationToken cancellationToken)
158175
{
159-
160-
public async Task AfterTransactionCompletionAsync(bool success, CancellationToken cancellationToken)
176+
cancellationToken.ThrowIfCancellationRequested();
177+
if (session.Factory.Settings.IsQueryCacheEnabled)
161178
{
162-
cancellationToken.ThrowIfCancellationRequested();
163-
int size = processes.Count;
164-
165-
for (int i = 0; i < size; i++)
166-
{
167-
try
168-
{
169-
AfterTransactionCompletionProcessDelegate process = processes[i];
170-
process(success);
171-
}
172-
catch (CacheException e)
173-
{
174-
log.Error( "could not release a cache lock", e);
175-
// continue loop
176-
}
177-
catch (Exception e)
178-
{
179-
throw new AssertionFailure("Unable to perform AfterTransactionCompletion callback", e);
180-
}
181-
}
182-
processes.Clear();
183-
184-
if (session.Factory.Settings.IsQueryCacheEnabled)
185-
{
186-
await (session.Factory.UpdateTimestampsCache.InvalidateAsync(querySpacesToInvalidate.ToArray(), cancellationToken)).ConfigureAwait(false);
187-
}
188-
querySpacesToInvalidate.Clear();
179+
await (session.Factory.UpdateTimestampsCache.InvalidateAsync(executedSpaces, cancellationToken)).ConfigureAwait(false);
189180
}
181+
182+
executedSpaces.Clear();
190183
}
191184
}
192185
}

src/NHibernate/Cache/UpdateTimestampsCache.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,40 @@ public UpdateTimestampsCache(Settings settings, IDictionary<string, string> prop
3333
updateTimestamps = settings.CacheProvider.BuildCache(regionName, props);
3434
}
3535

36+
public void PreInvalidate(object[] spaces)
37+
{
38+
PreInvalidate((IReadOnlyList<object>) spaces);
39+
}
40+
3641
[MethodImpl(MethodImplOptions.Synchronized)]
37-
public virtual void PreInvalidate(object[] spaces)
42+
public virtual void PreInvalidate(IReadOnlyCollection<object> spaces)
3843
{
3944
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
4045
long ts = updateTimestamps.NextTimestamp() + updateTimestamps.Timeout;
41-
for (int i = 0; i < spaces.Length; i++)
46+
foreach (var space in spaces)
4247
{
43-
updateTimestamps.Put(spaces[i], ts);
48+
updateTimestamps.Put(space, ts);
4449
}
50+
4551
//TODO: return new Lock(ts);
4652
}
4753

4854
/// <summary></summary>
55+
public void Invalidate(object[] spaces)
56+
{
57+
Invalidate((IReadOnlyList<object>) spaces);
58+
}
59+
4960
[MethodImpl(MethodImplOptions.Synchronized)]
50-
public virtual void Invalidate(object[] spaces)
61+
public virtual void Invalidate(IReadOnlyCollection<object> spaces)
5162
{
5263
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
5364
long ts = updateTimestamps.NextTimestamp();
5465
//TODO: if lock.getTimestamp().equals(ts)
55-
for (int i = 0; i < spaces.Length; i++)
66+
foreach (var space in spaces)
5667
{
57-
log.Debug(string.Format("Invalidating space [{0}]", spaces[i]));
58-
updateTimestamps.Put(spaces[i], ts);
68+
log.Debug(string.Format("Invalidating space [{0}]", space));
69+
updateTimestamps.Put(space, ts);
5970
}
6071
}
6172

0 commit comments

Comments
 (0)