Skip to content

Commit 13ed227

Browse files
Consecutive & interlaced session test, async compatible concurrent test. To be squashed.
1 parent 0747d74 commit 13ed227

File tree

2 files changed

+277
-41
lines changed

2 files changed

+277
-41
lines changed

src/NHibernate.Test/Async/NHSpecificTest/Logs/LogsFixture.cs

Lines changed: 139 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
1212
using System.Collections.Concurrent;
1313
using System.Linq;
1414
using System.Threading;
15+
using System.Threading.Tasks;
16+
using NHibernate.Cfg;
1517
using NHibernate.Impl;
16-
18+
using NHibernate.SqlCommand;
19+
using NHibernate.Type;
1720
using NUnit.Framework;
21+
using NHibernate.Linq;
1822

1923
namespace NHibernate.Test.NHSpecificTest.Logs
2024
{
@@ -26,7 +30,6 @@ namespace NHibernate.Test.NHSpecificTest.Logs
2630
using log4net.Core;
2731
using log4net.Layout;
2832
using log4net.Repository.Hierarchy;
29-
using System.Threading.Tasks;
3033

3134
[TestFixture]
3235
public class LogsFixtureAsync : TestCase
@@ -41,6 +44,33 @@ protected override string MappingsAssembly
4144
get { return "NHibernate.Test"; }
4245
}
4346

47+
protected override void Configure(Configuration configuration)
48+
{
49+
base.Configure(configuration);
50+
configuration.SetProperty(Cfg.Environment.UseSecondLevelCache, "false");
51+
}
52+
53+
protected override void OnSetUp()
54+
{
55+
using (var s = Sfi.OpenSession())
56+
using (var t = s.BeginTransaction())
57+
{
58+
s.Save(new Person());
59+
s.Save(new Person());
60+
t.Commit();
61+
}
62+
}
63+
64+
protected override void OnTearDown()
65+
{
66+
using (var s = Sfi.OpenSession())
67+
using (var t = s.BeginTransaction())
68+
{
69+
s.CreateQuery("delete from Person").ExecuteUpdate();
70+
t.Commit();
71+
}
72+
}
73+
4474
[Test]
4575
public async Task WillGetSessionIdFromSessionLogsAsync()
4676
{
@@ -49,27 +79,92 @@ public async Task WillGetSessionIdFromSessionLogsAsync()
4979
using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}"))
5080
using (var s = Sfi.OpenSession())
5181
{
52-
var sessionId = ((SessionImpl)s).SessionId;
82+
var sessionId = ((SessionImpl) s).SessionId;
5383

54-
await (s.GetAsync<Person>(1));//will execute some sql
84+
await (s.GetAsync<Person>(1)); //will execute some sql
5585

5686
var loggingEvent = spy.GetWholeLog();
5787
Assert.That(loggingEvent.Contains(sessionId.ToString()), Is.True);
5888
}
5989
}
6090

91+
[Test]
92+
public async Task WillGetSessionIdFromConsecutiveSessionsLogsAsync()
93+
{
94+
GlobalContext.Properties["sessionId"] = new SessionIdCapturer();
95+
96+
using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}"))
97+
{
98+
var sessions = Enumerable.Range(1, 10).Select(i => Sfi.OpenSession()).ToArray();
99+
try
100+
{
101+
for (var i = 0; i < 10; i++)
102+
for (var j = 0; j < 10; j++)
103+
{
104+
var s = sessions[j];
105+
await (s.GetAsync<Person>(i * 10 + j)); //will execute some sql
106+
}
107+
}
108+
finally
109+
{
110+
foreach (var s in sessions)
111+
{
112+
s.Dispose();
113+
}
114+
}
115+
116+
var loggingEvent = spy.GetWholeLog();
117+
for (var i = 0; i < 10; i++)
118+
for (var j = 0; j < 10; j++)
119+
{
120+
var sessionId = sessions[j].GetSessionImplementation().SessionId;
121+
Assert.That(loggingEvent, Does.Contain($"p0 = {i * 10 + j} [Type: Int32 (0:0:0)] | SessionId: {sessionId}"));
122+
}
123+
}
124+
}
125+
126+
[Test]
127+
public async Task WillGetSessionIdFromInterlacedSessionsLogsAsync()
128+
{
129+
GlobalContext.Properties["sessionId"] = new SessionIdCapturer();
130+
var interceptor = new InterlacedSessionInterceptor(Sfi);
131+
using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}"))
132+
using (var s = Sfi.WithOptions().Interceptor(interceptor).OpenSession())
133+
{
134+
// Trigger an operation which will fire many interceptor events, before and after s own logging.
135+
var persons = await (s.Query<Person>().ToListAsync());
136+
137+
var loggingEvent = spy.GetWholeLog();
138+
for (var i = 0; i < interceptor.SessionIds.Count; i++)
139+
{
140+
var sessionId = interceptor.SessionIds[i];
141+
Assert.That(loggingEvent, Does.Contain($"p0 = {i + 1} [Type: Int32 (0:0:0)] | SessionId: {sessionId}"));
142+
}
143+
Assert.That(loggingEvent, Does.Contain($"Person person0_ | SessionId: {s.GetSessionImplementation().SessionId}"));
144+
}
145+
}
146+
61147
[Test]
62148
public async Task WillGetSessionIdFromSessionLogsConcurrentAsync()
63149
{
64150
GlobalContext.Properties["sessionId"] = new SessionIdCapturer();
65151

66-
var semaphore = new ManualResetEventSlim();
152+
// Do not use a ManualResetEventSlim, it does not support async and exhausts the task thread pool in the
153+
// async counterparts of this test. SemaphoreSlim has the async support and release the thread when waiting.
154+
var semaphore = new SemaphoreSlim(0);
67155
var failures = new ConcurrentBag<Exception>();
68156
var sessionIds = new ConcurrentDictionary<int, Guid>();
69-
var array = Enumerable.Range(1, 10).Select(
70-
i => new Thread(
71-
() =>
157+
using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}"))
158+
{
159+
await (Task.WhenAll(Enumerable.Range( 1, 12).Select( async i =>
72160
{
161+
if (i > 10)
162+
{
163+
// Give some time to threads for reaching the wait, having all of them ready to do most of their job concurrently.
164+
await (Task.Delay(100));
165+
semaphore.Release(10);
166+
return;
167+
}
73168
try
74169
{
75170
using (var s = Sfi.OpenSession())
@@ -80,29 +175,21 @@ public async Task WillGetSessionIdFromSessionLogsConcurrentAsync()
80175
(ti, old) => throw new InvalidOperationException(
81176
$"Thread number {ti} has already session id {old}, while attempting to set it to" +
82177
$" {s.GetSessionImplementation().SessionId}"));
83-
semaphore.Wait();
178+
await (semaphore.WaitAsync());
84179

85180
for (int j = 0; j < 10; j++)
86181
{
87-
s.Get<Person>(i * 10 + j); //will execute some sql
182+
await (s.GetAsync<Person>(i * 10 + j)); //will execute some sql
88183
}
89184
}
90185
}
91186
catch (Exception e)
92187
{
93188
failures.Add(e);
94189
}
95-
})).ToArray();
96-
97-
using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}"))
98-
{
99-
Array.ForEach(array, thread => thread.Start());
100-
// Give some time to threads for reaching the wait, having all of them ready to do most of their job concurrently.
101-
await (Task.Delay(100));
102-
semaphore.Set();
103-
Array.ForEach(array, thread => thread.Join());
190+
})));
104191

105-
Assert.That(failures, Is.Empty, $"{failures.Count} thread(s) failed.");
192+
Assert.That(failures, Is.Empty, $"{failures.Count} task(s) failed.");
106193

107194
var loggingEvent = spy.GetWholeLog();
108195
for (var i = 1; i < 11; i++)
@@ -141,7 +228,7 @@ public TextLogSpy(string loggerName, string pattern)
141228
Threshold = Level.All,
142229
Writer = new StringWriter(stringBuilder)
143230
};
144-
loggerImpl = (Logger)LogManager.GetLogger(typeof(LogsFixtureAsync).Assembly, loggerName).Logger;
231+
loggerImpl = (Logger) LogManager.GetLogger(typeof(LogsFixtureAsync).Assembly, loggerName).Logger;
145232
loggerImpl.AddAppender(appender);
146233
previousLevel = loggerImpl.Level;
147234
loggerImpl.Level = Level.All;
@@ -158,7 +245,37 @@ public void Dispose()
158245
loggerImpl.Level = previousLevel;
159246
}
160247
}
161-
}
162248

249+
public class InterlacedSessionInterceptor : EmptyInterceptor
250+
{
251+
private readonly ISessionFactory _sfi;
252+
253+
public System.Collections.Generic.List<Guid> SessionIds { get; } = new System.Collections.Generic.List<Guid>();
254+
255+
public InterlacedSessionInterceptor(ISessionFactory sfi)
256+
{
257+
_sfi = sfi;
258+
}
259+
260+
public override SqlString OnPrepareStatement(SqlString sql)
261+
{
262+
using (var s = _sfi.OpenSession())
263+
{
264+
SessionIds.Add(s.GetSessionImplementation().SessionId);
265+
s.Get<Person>(SessionIds.Count); //will execute some sql
266+
}
267+
return base.OnPrepareStatement(sql);
268+
}
163269

270+
public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types)
271+
{
272+
using (var s = _sfi.OpenSession())
273+
{
274+
SessionIds.Add(s.GetSessionImplementation().SessionId);
275+
s.Get<Person>(SessionIds.Count); //will execute some sql
276+
}
277+
return base.OnLoad(entity, id, state, propertyNames, types);
278+
}
279+
}
280+
}
164281
}

0 commit comments

Comments
 (0)