Skip to content

Commit 43625d2

Browse files
committed
Added support for query space synchronization
Fixed redundant cache clearing
1 parent 32adfd1 commit 43625d2

19 files changed

+626
-102
lines changed

src/NHibernate.Test/Async/DebugSessionFactory.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ Task ISessionFactory.EvictAsync(System.Type persistentClass, CancellationToken c
5555
return ActualFactory.EvictAsync(persistentClass, cancellationToken);
5656
}
5757

58+
Task ISessionFactory.EvictAsync(IEnumerable<System.Type> persistentClass, CancellationToken cancellationToken)
59+
{
60+
return ActualFactory.EvictAsync(persistentClass, cancellationToken);
61+
}
62+
5863
Task ISessionFactory.EvictAsync(System.Type persistentClass, object id, CancellationToken cancellationToken)
5964
{
6065
return ActualFactory.EvictAsync(persistentClass, id, cancellationToken);
@@ -65,6 +70,11 @@ Task ISessionFactory.EvictEntityAsync(string entityName, CancellationToken cance
6570
return ActualFactory.EvictEntityAsync(entityName, cancellationToken);
6671
}
6772

73+
Task ISessionFactory.EvictEntityAsync(IEnumerable<string> entityNames, CancellationToken cancellationToken)
74+
{
75+
return ActualFactory.EvictEntityAsync(entityNames, cancellationToken);
76+
}
77+
6878
Task ISessionFactory.EvictEntityAsync(string entityName, object id, CancellationToken cancellationToken)
6979
{
7080
return ActualFactory.EvictEntityAsync(entityName, id, cancellationToken);
@@ -75,6 +85,11 @@ Task ISessionFactory.EvictCollectionAsync(string roleName, CancellationToken can
7585
return ActualFactory.EvictCollectionAsync(roleName, cancellationToken);
7686
}
7787

88+
Task ISessionFactory.EvictCollectionAsync(IEnumerable<string> roleNames, CancellationToken cancellationToken)
89+
{
90+
return ActualFactory.EvictCollectionAsync(roleNames, cancellationToken);
91+
}
92+
7893
Task ISessionFactory.EvictCollectionAsync(string roleName, object id, CancellationToken cancellationToken)
7994
{
8095
return ActualFactory.EvictCollectionAsync(roleName, id, cancellationToken);
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using NHibernate.Action;
8+
using NHibernate.Engine;
9+
using NHibernate.Metadata;
10+
using NHibernate.Persister.Entity;
11+
using NSubstitute;
12+
using NUnit.Framework;
13+
14+
namespace NHibernate.Test.BulkManipulation
15+
{
16+
[TestFixture]
17+
public class BulkOperationCleanupActionFixture
18+
{
19+
private ISessionImplementor _session;
20+
private ISessionFactoryImplementor _factory;
21+
private IEntityPersister _persister;
22+
23+
[SetUp]
24+
public void SetupTest()
25+
{
26+
_session = Substitute.For<ISessionImplementor>();
27+
_factory = Substitute.For<ISessionFactoryImplementor>();
28+
_persister = Substitute.For<IEntityPersister>();
29+
_session.Factory.Returns(_factory);
30+
_factory.GetAllClassMetadata().Returns(new Dictionary<string, IClassMetadata> { ["TestClass"] = null });
31+
_factory.GetEntityPersister("TestClass").Returns(_persister);
32+
_factory.GetCollectionRolesByEntityParticipant("TestClass").Returns(new HashSet<string>(new[] { "TestClass.Children" }));
33+
_persister.QuerySpaces.Returns(new[] { "TestClass" });
34+
_persister.EntityName.Returns("TestClass");
35+
}
36+
37+
[TestCase("TestClass", true, 1, 1, 1)]
38+
[TestCase("AnotherClass", true, 1, 0, 0)]
39+
[TestCase("AnotherClass,TestClass", true, 2, 1, 1)]
40+
[TestCase("TestClass", false, 1, 0, 1)]
41+
[TestCase("", true, 1, 1, 1)]
42+
[Test]
43+
public void Init_EvictsFromCache(string querySpaces, bool persisterHasCache, int expectedPropertySpaceLength, int expectedEntityEvictionCount, int expectedCollectionEvictionCount)
44+
{
45+
_persister.HasCache.Returns(persisterHasCache);
46+
47+
var target = new BulkOperationCleanupAction(_session, new HashSet<string>(querySpaces.Split(new []{','},StringSplitOptions.RemoveEmptyEntries)));
48+
49+
target.Init();
50+
51+
Assert.AreEqual(expectedPropertySpaceLength, target.PropertySpaces.Length);
52+
53+
if (expectedEntityEvictionCount > 0)
54+
{
55+
_factory.Received(1).EvictEntity(Arg.Is<IEnumerable<string>>(x => x.Count() == expectedEntityEvictionCount));
56+
}
57+
else
58+
{
59+
_factory.DidNotReceive().EvictEntity(Arg.Any<IEnumerable<string>>());
60+
}
61+
62+
if (expectedCollectionEvictionCount > 0)
63+
{
64+
_factory.Received(1).EvictCollection(Arg.Is<IEnumerable<string>>(x => x.Count() == expectedCollectionEvictionCount));
65+
}
66+
else
67+
{
68+
_factory.DidNotReceive().EvictCollection(Arg.Any<IEnumerable<string>>());
69+
}
70+
71+
}
72+
73+
[TestCase("TestClass", true, 1, 1, 1)]
74+
[TestCase("AnotherClass", true, 1, 0, 0)]
75+
[TestCase("AnotherClass,TestClass", true, 2, 1, 1)]
76+
[TestCase("TestClass", false, 1, 0, 1)]
77+
[TestCase("", true, 1, 1, 1)]
78+
[Test]
79+
public async Task InitAsync_EvictsFromCacheAsync(string querySpaces, bool persisterHasCache, int expectedPropertySpaceLength, int expectedEntityEvictionCount, int expectedCollectionEvictionCount)
80+
{
81+
82+
_persister.HasCache.Returns(persisterHasCache);
83+
84+
var target = new BulkOperationCleanupAction(_session, new HashSet<string>(querySpaces.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)));
85+
86+
await target.InitAsync(CancellationToken.None);
87+
88+
Assert.AreEqual(expectedPropertySpaceLength, target.PropertySpaces.Length);
89+
90+
if (expectedEntityEvictionCount > 0)
91+
{
92+
await _factory.Received(1).EvictEntityAsync(Arg.Is<IEnumerable<string>>(x => x.Count() == expectedEntityEvictionCount));
93+
}
94+
else
95+
{
96+
await _factory.DidNotReceive().EvictEntityAsync(Arg.Any<IEnumerable<string>>());
97+
}
98+
99+
if (expectedCollectionEvictionCount > 0)
100+
{
101+
await _factory.Received(1).EvictCollectionAsync(Arg.Is<IEnumerable<string>>(x => x.Count() == expectedCollectionEvictionCount));
102+
}
103+
else
104+
{
105+
await _factory.DidNotReceive().EvictCollectionAsync(Arg.Any<IEnumerable<string>>());
106+
}
107+
108+
}
109+
}
110+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Concurrent;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using NHibernate.Cache;
7+
using NHibernate.Cfg;
8+
using NHibernate.Engine;
9+
using NSubstitute;
10+
using NUnit.Framework;
11+
using Environment = NHibernate.Cfg.Environment;
12+
13+
namespace NHibernate.Test.BulkManipulation
14+
{
15+
[TestFixture]
16+
public class NativeSQLBulkOperationsWithCache : TestCase
17+
{
18+
protected override string MappingsAssembly => "NHibernate.Test";
19+
20+
protected override IList Mappings => new[] { "BulkManipulation.Vehicle.hbm.xml" };
21+
22+
23+
protected override void Configure(Configuration configuration)
24+
{
25+
cfg.SetProperty(Environment.UseQueryCache, "true");
26+
cfg.SetProperty(Environment.UseSecondLevelCache, "true");
27+
cfg.SetProperty(Environment.CacheProvider, typeof(SubstituteCacheProvider).AssemblyQualifiedName);
28+
}
29+
30+
[Test]
31+
public void SimpleNativeSQLInsert_DoesNotEvictEntireCacheWhenQuerySpacesAreAdded()
32+
{
33+
List<string> clearCalls = new List<string>();
34+
(Sfi.Settings.CacheProvider as SubstituteCacheProvider).OnClear(x =>
35+
{
36+
clearCalls.Add(x);
37+
});
38+
using (var s = OpenSession())
39+
{
40+
string ssql = "UPDATE Vehicle SET Vin='123' WHERE Vin='123c'";
41+
42+
using (var t = s.BeginTransaction())
43+
{
44+
45+
s.CreateSQLQuery(ssql).ExecuteUpdate();
46+
t.Commit();
47+
48+
Assert.AreEqual(2, clearCalls.Count);
49+
}
50+
51+
clearCalls.Clear();
52+
53+
using (var t = s.BeginTransaction())
54+
{
55+
s.CreateSQLQuery(ssql).AddSynchronizedQuerySpace("Unknown").ExecuteUpdate();
56+
t.Commit();
57+
58+
Assert.AreEqual(0, clearCalls.Count);
59+
}
60+
61+
}
62+
63+
64+
}
65+
66+
}
67+
68+
public class SubstituteCacheProvider : ICacheProvider
69+
{
70+
private readonly ConcurrentDictionary<string, Lazy<ICache>> _caches = new ConcurrentDictionary<string, Lazy<ICache>>();
71+
private Action<string> _onClear;
72+
73+
public ICache BuildCache(string regionName, IDictionary<string, string> properties)
74+
{
75+
return _caches.GetOrAdd(regionName, x => new Lazy<ICache>(() =>
76+
{
77+
var cache = Substitute.For<ICache>();
78+
cache.RegionName.Returns(regionName);
79+
cache.When(c => c.Clear()).Do(c => _onClear?.Invoke(regionName));
80+
return cache;
81+
})).Value;
82+
}
83+
84+
public long NextTimestamp()
85+
{
86+
return Timestamper.Next();
87+
}
88+
89+
public void Start(IDictionary<string, string> properties)
90+
{
91+
}
92+
93+
public void Stop()
94+
{
95+
}
96+
97+
public ICache GetCache(string region)
98+
{
99+
Lazy<ICache> cache;
100+
_caches.TryGetValue(region, out cache);
101+
return cache?.Value;
102+
}
103+
104+
public IEnumerable<ICache> GetAllCaches()
105+
{
106+
return _caches.Values.Select(x => x.Value);
107+
}
108+
109+
public void OnClear(Action<string> callback)
110+
{
111+
_onClear = callback;
112+
}
113+
}
114+
}

src/NHibernate.Test/DebugSessionFactory.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ void ISessionFactory.Evict(System.Type persistentClass)
221221
ActualFactory.Evict(persistentClass);
222222
}
223223

224+
void ISessionFactory.Evict(IEnumerable<System.Type> persistentClasses)
225+
{
226+
ActualFactory.Evict(persistentClasses);
227+
}
228+
224229
void ISessionFactory.Evict(System.Type persistentClass, object id)
225230
{
226231
ActualFactory.Evict(persistentClass, id);
@@ -231,6 +236,11 @@ void ISessionFactory.EvictEntity(string entityName)
231236
ActualFactory.EvictEntity(entityName);
232237
}
233238

239+
void ISessionFactory.EvictEntity(IEnumerable<string> entityNames)
240+
{
241+
ActualFactory.EvictEntity(entityNames);
242+
}
243+
234244
void ISessionFactory.EvictEntity(string entityName, object id)
235245
{
236246
ActualFactory.EvictEntity(entityName, id);
@@ -241,6 +251,11 @@ void ISessionFactory.EvictCollection(string roleName)
241251
ActualFactory.EvictCollection(roleName);
242252
}
243253

254+
void ISessionFactory.EvictCollection(IEnumerable<string> roleNames)
255+
{
256+
ActualFactory.EvictCollection(roleNames);
257+
}
258+
244259
void ISessionFactory.EvictCollection(string roleName, object id)
245260
{
246261
ActualFactory.EvictCollection(roleName, id);

src/NHibernate.Test/MappingByCode/For.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Linq.Expressions;
33
using System.Reflection;
4-
using NHibernate.Mapping.ByCode;
4+
using TypeExtensions = NHibernate.Mapping.ByCode.TypeExtensions;
55

66
namespace NHibernate.Test.MappingByCode
77
{
@@ -16,4 +16,4 @@ public static MemberInfo Property(Expression<Func<T, object>> propertyGetter)
1616
return TypeExtensions.DecodeMemberAccessExpression(propertyGetter);
1717
}
1818
}
19-
}
19+
}

src/NHibernate.Test/MappingByCode/TypeExtensionsTests/TypeExtensionsTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Reflection;
77
using NHibernate.Mapping.ByCode;
88
using NUnit.Framework;
9+
using TypeExtensions = NHibernate.Mapping.ByCode.TypeExtensions;
910

1011
namespace NHibernate.Test.MappingByCode.TypeExtensionsTests
1112
{

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<PackageReference Include="Antlr3.Runtime" Version="3.5.1" />
3737
<PackageReference Include="Iesi.Collections" Version="4.0.2" />
3838
<PackageReference Include="log4net" Version="2.0.8" />
39+
<PackageReference Include="NSubstitute" Version="3.0.1" />
3940
<PackageReference Include="nunit" Version="3.7.1" />
4041
<PackageReference Include="Remotion.Linq" Version="2.1.2" />
4142
<PackageReference Include="Remotion.Linq.EagerFetching" Version="2.1.0" />

0 commit comments

Comments
 (0)