Skip to content

Commit 94a752d

Browse files
Tests for cache build.
1 parent cc4f78d commit 94a752d

File tree

5 files changed

+251
-127
lines changed

5 files changed

+251
-127
lines changed

src/NHibernate.Test/Async/CacheTest/GetQueryCacheFixture.cs renamed to src/NHibernate.Test/Async/CacheTest/BuildCacheFixture.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,23 @@
1515
using System.Threading;
1616
using NHibernate.Cache;
1717
using NHibernate.Cfg;
18+
using NHibernate.Engine;
19+
using NHibernate.Util;
1820
using NUnit.Framework;
1921
using Environment = NHibernate.Cfg.Environment;
2022

2123
namespace NHibernate.Test.CacheTest
2224
{
2325
using System.Threading.Tasks;
2426
[TestFixture]
25-
public class GetQueryCacheFixtureAsync : TestCase
27+
public class BuildCacheFixtureAsync : TestCase
2628
{
27-
protected override IList Mappings => new[] { "Simple.hbm.xml" };
29+
protected override string MappingsAssembly => "NHibernate.Test";
30+
31+
protected override IList Mappings => new[] { "CacheTest.EntitiesInSameRegion.hbm.xml" };
32+
33+
// Disable the TestCase cache overrides.
34+
protected override string CacheConcurrencyStrategy => null;
2835

2936
protected override void Configure(Configuration configuration)
3037
{
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Concurrent;
4+
using System.Collections.Generic;
5+
using System.Threading;
6+
using NHibernate.Cache;
7+
using NHibernate.Cfg;
8+
using NHibernate.Engine;
9+
using NHibernate.Util;
10+
using NUnit.Framework;
11+
using Environment = NHibernate.Cfg.Environment;
12+
13+
namespace NHibernate.Test.CacheTest
14+
{
15+
[TestFixture]
16+
public class BuildCacheFixture : TestCase
17+
{
18+
protected override string MappingsAssembly => "NHibernate.Test";
19+
20+
protected override IList Mappings => new[] { "CacheTest.EntitiesInSameRegion.hbm.xml" };
21+
22+
// Disable the TestCase cache overrides.
23+
protected override string CacheConcurrencyStrategy => null;
24+
25+
protected override void Configure(Configuration configuration)
26+
{
27+
configuration.SetProperty(Environment.UseQueryCache, "true");
28+
configuration.SetProperty(Environment.CacheProvider, typeof(LockedCacheProvider).AssemblyQualifiedName);
29+
}
30+
31+
[Theory]
32+
public void CommonRegionHasOneUniqueCacheAndExpectedConcurrency(bool withPrefix)
33+
{
34+
const string prefix = "Prefix";
35+
const string region = "Common";
36+
var fullRegion = (withPrefix ? prefix + "." : "") + region;
37+
ISessionFactoryImplementor sfi = null;
38+
if (withPrefix)
39+
cfg.SetProperty(Environment.CacheRegionPrefix, prefix);
40+
try
41+
{
42+
sfi = withPrefix ? BuildSessionFactory() : Sfi;
43+
var commonRegionCache = sfi.GetSecondLevelCacheRegion(fullRegion);
44+
var entityAName = typeof(EntityA).FullName;
45+
var entityAConcurrencyCache = sfi.GetEntityPersister(entityAName).Cache;
46+
var entityACache = entityAConcurrencyCache.Cache;
47+
var entityBName = typeof(EntityB).FullName;
48+
var entityBConcurrencyCache = sfi.GetEntityPersister(entityBName).Cache;
49+
var entityBCache = entityBConcurrencyCache.Cache;
50+
var relatedAConcurrencyCache =
51+
sfi.GetCollectionPersister(StringHelper.Qualify(entityAName, nameof(EntityA.Related))).Cache;
52+
var relatedACache = relatedAConcurrencyCache.Cache;
53+
var relatedBConcurrencyCache =
54+
sfi.GetCollectionPersister(StringHelper.Qualify(entityBName, nameof(EntityB.Related))).Cache;
55+
var relatedBCache = relatedBConcurrencyCache.Cache;
56+
var queryCache = sfi.GetQueryCache(region).Cache;
57+
Assert.Multiple(
58+
() =>
59+
{
60+
Assert.That(commonRegionCache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for common region");
61+
Assert.That(entityACache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for EntityA");
62+
Assert.That(entityBCache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for EntityB");
63+
Assert.That(relatedACache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for RelatedA");
64+
Assert.That(relatedBCache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for RelatedB");
65+
Assert.That(queryCache.RegionName, Is.EqualTo(fullRegion), "Unexpected region name for query cache");
66+
});
67+
Assert.Multiple(
68+
() =>
69+
{
70+
Assert.That(entityAConcurrencyCache, Is.InstanceOf<ReadWriteCache>(), "Unexpected concurrency for EntityA");
71+
Assert.That(relatedAConcurrencyCache, Is.InstanceOf<NonstrictReadWriteCache>(), "Unexpected concurrency for RelatedA");
72+
Assert.That(entityBConcurrencyCache, Is.InstanceOf<ReadOnlyCache>(), "Unexpected concurrency for EntityB");
73+
Assert.That(relatedBConcurrencyCache, Is.InstanceOf<ReadWriteCache>(), "Unexpected concurrency for RelatedB");
74+
Assert.That(entityACache, Is.EqualTo(commonRegionCache), "Unexpected cache for EntityA");
75+
Assert.That(entityBCache, Is.EqualTo(commonRegionCache), "Unexpected cache for EntityB");
76+
Assert.That(relatedACache, Is.EqualTo(commonRegionCache), "Unexpected cache for RelatedA");
77+
Assert.That(relatedBCache, Is.EqualTo(commonRegionCache), "Unexpected cache for RelatedB");
78+
Assert.That(queryCache, Is.EqualTo(commonRegionCache), "Unexpected cache for query cache");
79+
});
80+
}
81+
finally
82+
{
83+
if (withPrefix)
84+
{
85+
cfg.Properties.Remove(Environment.CacheRegionPrefix);
86+
sfi?.Dispose();
87+
}
88+
}
89+
}
90+
91+
[Test]
92+
public void RetrievedQueryCacheMatchesGloballyStoredOne()
93+
{
94+
var region = "RetrievedQueryCacheMatchesGloballyStoredOne";
95+
LockedCache.Semaphore = new SemaphoreSlim(0);
96+
LockedCache.CreationCount = 0;
97+
try
98+
{
99+
var failures = new ConcurrentBag<Exception>();
100+
var thread1 = new Thread(
101+
() =>
102+
{
103+
try
104+
{
105+
Sfi.GetQueryCache(region);
106+
}
107+
catch (Exception e)
108+
{
109+
failures.Add(e);
110+
}
111+
});
112+
var thread2 = new Thread(
113+
() =>
114+
{
115+
try
116+
{
117+
Sfi.GetQueryCache(region);
118+
}
119+
catch (Exception e)
120+
{
121+
failures.Add(e);
122+
}
123+
});
124+
thread1.Start();
125+
thread2.Start();
126+
127+
// Give some time to threads for reaching the wait, having all of them ready to do most of their job concurrently.
128+
Thread.Sleep(100);
129+
// Let only one finish its job, it should be the one being stored in query cache dictionary.
130+
LockedCache.Semaphore.Release(1);
131+
// Give some time to released thread to finish its job.
132+
Thread.Sleep(100);
133+
// Release other thread
134+
LockedCache.Semaphore.Release(10);
135+
136+
thread1.Join();
137+
thread2.Join();
138+
Assert.That(failures, Is.Empty, $"{failures.Count} thread(s) failed.");
139+
}
140+
finally
141+
{
142+
LockedCache.Semaphore.Dispose();
143+
LockedCache.Semaphore = null;
144+
}
145+
146+
var queryCache = Sfi.GetQueryCache(region).Cache;
147+
var globalCache = Sfi.GetSecondLevelCacheRegion(region);
148+
Assert.That(globalCache, Is.SameAs(queryCache));
149+
Assert.That(LockedCache.CreationCount, Is.EqualTo(1));
150+
}
151+
}
152+
153+
public class LockedCache : HashtableCache
154+
{
155+
public static SemaphoreSlim Semaphore { get; set; }
156+
157+
private static int _creationCount;
158+
159+
public static int CreationCount
160+
{
161+
get => _creationCount;
162+
set => _creationCount = value;
163+
}
164+
165+
public LockedCache(string regionName) : base(regionName)
166+
{
167+
Semaphore?.Wait();
168+
Interlocked.Increment(ref _creationCount);
169+
}
170+
}
171+
172+
public class LockedCacheProvider : ICacheProvider
173+
{
174+
public ICache BuildCache(string regionName, IDictionary<string, string> properties)
175+
{
176+
return new LockedCache(regionName);
177+
}
178+
179+
public long NextTimestamp()
180+
{
181+
return Timestamper.Next();
182+
}
183+
184+
public void Start(IDictionary<string, string> properties)
185+
{
186+
}
187+
188+
public void Stop()
189+
{
190+
}
191+
}
192+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Collections.Generic;
2+
3+
namespace NHibernate.Test.CacheTest
4+
{
5+
public class EntityInSameRegion
6+
{
7+
public virtual int Id { get; set; }
8+
public virtual string Description { get; set; }
9+
public virtual int Value { get; set; }
10+
public virtual ISet<EntityInSameRegion> Related { get; set; }
11+
}
12+
13+
public class EntityA : EntityInSameRegion
14+
{
15+
}
16+
17+
public class EntityB : EntityInSameRegion
18+
{
19+
}
20+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3+
assembly="NHibernate.Test"
4+
namespace="NHibernate.Test.CacheTest">
5+
<class name="EntityA">
6+
<cache usage="read-write" region="Common" />
7+
<id type="int" />
8+
<property name="Description"/>
9+
<property name="Value" column="`Value`"/>
10+
<set name="Related">
11+
<cache usage="nonstrict-read-write" region="Common" />
12+
<key column="Related" />
13+
<one-to-many class="EntityA"/>
14+
</set>
15+
</class>
16+
<class name="EntityB">
17+
<cache usage="read-only" region="Common" />
18+
<id type="int" />
19+
<property name="Description"/>
20+
<property name="Value" column="`Value`"/>
21+
<set name="Related">
22+
<cache usage="read-write" region="Common" />
23+
<key column="Related" />
24+
<one-to-many class="EntityB"/>
25+
</set>
26+
</class>
27+
<query name="EntityA.All" cache-region="Common" cacheable="true">
28+
from EntityA
29+
</query>
30+
</hibernate-mapping>

src/NHibernate.Test/CacheTest/GetQueryCacheFixture.cs

Lines changed: 0 additions & 125 deletions
This file was deleted.

0 commit comments

Comments
 (0)