Skip to content

Commit 73797df

Browse files
gliljashazzik
authored andcommitted
Reduce the number of calls to UpdateTimestampsCache (#1467)
- Make methods of UpdateTimestampsCache virtual - Mark some methods UpdateTimestampsCache obsolete
1 parent bf7e34d commit 73797df

File tree

9 files changed

+484
-158
lines changed

9 files changed

+484
-158
lines changed

src/AsyncGenerator.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
applyChanges: true
55
analyzation:
66
methodConversion:
7-
- conversion: Ignore
8-
hasAttributeName: ObsoleteAttribute
97
- conversion: Ignore
108
name: PostProcessInsert
119
containingTypeName: HqlSqlWalker
@@ -96,6 +94,9 @@
9694
- conversion: Ignore
9795
name: Exists
9896
containingTypeName: AbstractCollectionPersister
97+
- conversion: Ignore
98+
name: QuoteTableAndColumns
99+
containingTypeName: SchemaMetadataUpdater
99100
- conversion: ToAsync
100101
name: ExecuteReader
101102
containingTypeName: IBatcher
@@ -119,6 +120,8 @@
119120
- name: GetFieldValue
120121
- name: IsDBNull
121122
- name: WriteLine
123+
ignoreAsyncCounterparts:
124+
- rule: Obsolete
122125
callForwarding: true
123126
cancellationTokens:
124127
guards: true
@@ -259,6 +262,9 @@ methodRules:
259262
- containingType: NHibernate.Tool.hbm2ddl.SchemaValidator
260263
- containingType: NHibernate.Tool.hbm2ddl.SchemaExport
261264
name: PubliclyExposedType
265+
- filters:
266+
- hasAttributeName: ObsoleteAttribute
267+
name: Obsolete
262268
typeRules:
263269
- filters:
264270
- containingAssemblyName: NHibernate
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Collections;
12+
using System.Collections.Generic;
13+
using System.Linq;
14+
using System.Reflection;
15+
using NHibernate.Cache;
16+
using NHibernate.Cfg;
17+
using NHibernate.Engine;
18+
using NHibernate.Impl;
19+
using NHibernate.Test.SecondLevelCacheTests;
20+
using NSubstitute;
21+
using NUnit.Framework;
22+
23+
namespace NHibernate.Test.SecondLevelCacheTest
24+
{
25+
using System.Threading.Tasks;
26+
using System.Threading;
27+
[TestFixture]
28+
public class InvalidationTestsAsync : TestCase
29+
{
30+
protected override string MappingsAssembly => "NHibernate.Test";
31+
32+
protected override IList Mappings => new[] { "SecondLevelCacheTest.Item.hbm.xml" };
33+
34+
protected override void Configure(Configuration configuration)
35+
{
36+
configuration.SetProperty(Environment.CacheProvider, typeof(HashtableCacheProvider).AssemblyQualifiedName);
37+
configuration.SetProperty(Environment.UseQueryCache, "true");
38+
}
39+
40+
[Test]
41+
public async Task InvalidatesEntitiesAsync()
42+
{
43+
var debugSessionFactory = (DebugSessionFactory) Sfi;
44+
45+
var cache = Substitute.For<UpdateTimestampsCache>(Sfi.Settings, new Dictionary<string, string>());
46+
47+
var updateTimestampsCacheField = typeof(SessionFactoryImpl).GetField(
48+
"updateTimestampsCache",
49+
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
50+
51+
updateTimestampsCacheField.SetValue(debugSessionFactory.ActualFactory, cache);
52+
53+
//"Received" assertions can not be used since the collection is reused and cleared between calls.
54+
//The received args are cloned and stored
55+
var preInvalidations = new List<IReadOnlyCollection<string>>();
56+
var invalidations = new List<IReadOnlyCollection<string>>();
57+
58+
await (cache.PreInvalidateAsync(Arg.Do<IReadOnlyCollection<string>>(x => preInvalidations.Add(x.ToList())), CancellationToken.None));
59+
await (cache.InvalidateAsync(Arg.Do<IReadOnlyCollection<string>>(x => invalidations.Add(x.ToList())), CancellationToken.None));
60+
61+
using (var session = OpenSession())
62+
{
63+
using (var tx = session.BeginTransaction())
64+
{
65+
foreach (var i in Enumerable.Range(1, 10))
66+
{
67+
var item = new Item {Id = i};
68+
await (session.SaveAsync(item));
69+
}
70+
71+
await (tx.CommitAsync());
72+
}
73+
74+
using (var tx = session.BeginTransaction())
75+
{
76+
foreach (var i in Enumerable.Range(1, 10))
77+
{
78+
var item = await (session.GetAsync<Item>(i));
79+
item.Name = item.Id.ToString();
80+
}
81+
82+
await (tx.CommitAsync());
83+
}
84+
85+
using (var tx = session.BeginTransaction())
86+
{
87+
foreach (var i in Enumerable.Range(1, 10))
88+
{
89+
var item = await (session.GetAsync<Item>(i));
90+
await (session.DeleteAsync(item));
91+
}
92+
93+
await (tx.CommitAsync());
94+
}
95+
}
96+
97+
//Should receive one preinvalidation and one invalidation per commit
98+
Assert.That(preInvalidations, Has.Count.EqualTo(3));
99+
Assert.That(preInvalidations, Has.All.Count.EqualTo(1).And.Contains("Item"));
100+
101+
Assert.That(invalidations, Has.Count.EqualTo(3));
102+
Assert.That(invalidations, Has.All.Count.EqualTo(1).And.Contains("Item"));
103+
}
104+
105+
protected override void OnTearDown()
106+
{
107+
using (var s = OpenSession())
108+
using (var tx = s.BeginTransaction())
109+
{
110+
s.Delete("from Item");
111+
tx.Commit();
112+
}
113+
}
114+
}
115+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using NHibernate.Cache;
6+
using NHibernate.Cfg;
7+
using NHibernate.Impl;
8+
using NHibernate.Test.SecondLevelCacheTests;
9+
using NSubstitute;
10+
using NUnit.Framework;
11+
12+
namespace NHibernate.Test.SecondLevelCacheTest
13+
{
14+
[TestFixture]
15+
public class InvalidationTests : TestCase
16+
{
17+
protected override string MappingsAssembly => "NHibernate.Test";
18+
19+
protected override IList Mappings => new[] { "SecondLevelCacheTest.Item.hbm.xml" };
20+
21+
protected override void Configure(Configuration configuration)
22+
{
23+
configuration.SetProperty(Environment.CacheProvider, typeof(HashtableCacheProvider).AssemblyQualifiedName);
24+
configuration.SetProperty(Environment.UseQueryCache, "true");
25+
}
26+
27+
[Test]
28+
public void InvalidatesEntities()
29+
{
30+
var debugSessionFactory = (DebugSessionFactory) Sfi;
31+
32+
var cache = Substitute.For<UpdateTimestampsCache>(Sfi.Settings, new Dictionary<string, string>());
33+
34+
var updateTimestampsCacheField = typeof(SessionFactoryImpl).GetField(
35+
"updateTimestampsCache",
36+
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
37+
38+
updateTimestampsCacheField.SetValue(debugSessionFactory.ActualFactory, cache);
39+
40+
//"Received" assertions can not be used since the collection is reused and cleared between calls.
41+
//The received args are cloned and stored
42+
var preInvalidations = new List<IReadOnlyCollection<string>>();
43+
var invalidations = new List<IReadOnlyCollection<string>>();
44+
45+
cache.PreInvalidate(Arg.Do<IReadOnlyCollection<string>>(x => preInvalidations.Add(x.ToList())));
46+
cache.Invalidate(Arg.Do<IReadOnlyCollection<string>>(x => invalidations.Add(x.ToList())));
47+
48+
using (var session = OpenSession())
49+
{
50+
using (var tx = session.BeginTransaction())
51+
{
52+
foreach (var i in Enumerable.Range(1, 10))
53+
{
54+
var item = new Item {Id = i};
55+
session.Save(item);
56+
}
57+
58+
tx.Commit();
59+
}
60+
61+
using (var tx = session.BeginTransaction())
62+
{
63+
foreach (var i in Enumerable.Range(1, 10))
64+
{
65+
var item = session.Get<Item>(i);
66+
item.Name = item.Id.ToString();
67+
}
68+
69+
tx.Commit();
70+
}
71+
72+
using (var tx = session.BeginTransaction())
73+
{
74+
foreach (var i in Enumerable.Range(1, 10))
75+
{
76+
var item = session.Get<Item>(i);
77+
session.Delete(item);
78+
}
79+
80+
tx.Commit();
81+
}
82+
}
83+
84+
//Should receive one preinvalidation and one invalidation per commit
85+
Assert.That(preInvalidations, Has.Count.EqualTo(3));
86+
Assert.That(preInvalidations, Has.All.Count.EqualTo(1).And.Contains("Item"));
87+
88+
Assert.That(invalidations, Has.Count.EqualTo(3));
89+
Assert.That(invalidations, Has.All.Count.EqualTo(1).And.Contains("Item"));
90+
}
91+
92+
protected override void OnTearDown()
93+
{
94+
using (var s = OpenSession())
95+
using (var tx = s.BeginTransaction())
96+
{
97+
s.Delete("from Item");
98+
tx.Commit();
99+
}
100+
}
101+
}
102+
}

src/NHibernate/Async/Cache/UpdateTimestampsCache.cs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
using System;
1212
using System.Collections.Generic;
13+
using System.Linq;
1314
using System.Runtime.CompilerServices;
1415

1516
using NHibernate.Cfg;
@@ -24,7 +25,7 @@ public partial class UpdateTimestampsCache
2425
private readonly NHibernate.Util.AsyncLock _invalidate = new NHibernate.Util.AsyncLock();
2526
private readonly NHibernate.Util.AsyncLock _isUpToDate = new NHibernate.Util.AsyncLock();
2627

27-
public Task ClearAsync(CancellationToken cancellationToken)
28+
public virtual Task ClearAsync(CancellationToken cancellationToken)
2829
{
2930
if (cancellationToken.IsCancellationRequested)
3031
{
@@ -33,43 +34,82 @@ public Task ClearAsync(CancellationToken cancellationToken)
3334
return updateTimestamps.ClearAsync(cancellationToken);
3435
}
3536

37+
//Since v5.1
38+
[Obsolete("Please use PreInvalidate(IReadOnlyCollection<string>) instead.")]
39+
public Task PreInvalidateAsync(object[] spaces, CancellationToken cancellationToken)
40+
{
41+
if (cancellationToken.IsCancellationRequested)
42+
{
43+
return Task.FromCanceled<object>(cancellationToken);
44+
}
45+
try
46+
{
47+
//Only for backwards compatibility.
48+
return PreInvalidateAsync(spaces.OfType<string>().ToList(), cancellationToken);
49+
}
50+
catch (Exception ex)
51+
{
52+
return Task.FromException<object>(ex);
53+
}
54+
}
55+
3656
[MethodImpl()]
37-
public async Task PreInvalidateAsync(object[] spaces, CancellationToken cancellationToken)
57+
public virtual async Task PreInvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
3858
{
3959
cancellationToken.ThrowIfCancellationRequested();
4060
using (await _preInvalidate.LockAsync())
4161
{
4262
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
4363
long ts = updateTimestamps.NextTimestamp() + updateTimestamps.Timeout;
44-
for (int i = 0; i < spaces.Length; i++)
64+
foreach (var space in spaces)
4565
{
46-
await (updateTimestamps.PutAsync(spaces[i], ts, cancellationToken)).ConfigureAwait(false);
66+
await (updateTimestamps.PutAsync(space, ts, cancellationToken)).ConfigureAwait(false);
4767
}
68+
4869
//TODO: return new Lock(ts);
4970
}
71+
5072
//TODO: return new Lock(ts);
5173
}
5274

53-
/// <summary></summary>
75+
//Since v5.1
76+
[Obsolete("Please use PreInvalidate(IReadOnlyCollection<string>) instead.")]
77+
public Task InvalidateAsync(object[] spaces, CancellationToken cancellationToken)
78+
{
79+
if (cancellationToken.IsCancellationRequested)
80+
{
81+
return Task.FromCanceled<object>(cancellationToken);
82+
}
83+
try
84+
{
85+
//Only for backwards compatibility.
86+
return InvalidateAsync(spaces.OfType<string>().ToList(), cancellationToken);
87+
}
88+
catch (Exception ex)
89+
{
90+
return Task.FromException<object>(ex);
91+
}
92+
}
93+
5494
[MethodImpl()]
55-
public async Task InvalidateAsync(object[] spaces, CancellationToken cancellationToken)
95+
public virtual async Task InvalidateAsync(IReadOnlyCollection<string> spaces, CancellationToken cancellationToken)
5696
{
5797
cancellationToken.ThrowIfCancellationRequested();
5898
using (await _invalidate.LockAsync())
5999
{
60100
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
61101
long ts = updateTimestamps.NextTimestamp();
62102
//TODO: if lock.getTimestamp().equals(ts)
63-
for (int i = 0; i < spaces.Length; i++)
103+
foreach (var space in spaces)
64104
{
65-
log.Debug("Invalidating space [{0}]", spaces[i]);
66-
await (updateTimestamps.PutAsync(spaces[i], ts, cancellationToken)).ConfigureAwait(false);
105+
log.Debug("Invalidating space [{0}]", space);
106+
await (updateTimestamps.PutAsync(space, ts, cancellationToken)).ConfigureAwait(false);
67107
}
68108
}
69109
}
70110

71111
[MethodImpl()]
72-
public async Task<bool> IsUpToDateAsync(ISet<string> spaces, long timestamp /* H2.1 has Long here */, CancellationToken cancellationToken)
112+
public virtual async Task<bool> IsUpToDateAsync(ISet<string> spaces, long timestamp /* H2.1 has Long here */, CancellationToken cancellationToken)
73113
{
74114
cancellationToken.ThrowIfCancellationRequested();
75115
using (await _isUpToDate.LockAsync())

0 commit comments

Comments
 (0)