Skip to content

Commit fd4422e

Browse files
Add test proposed by Alexander.
1 parent 8a0788b commit fd4422e

File tree

2 files changed

+118
-2
lines changed

2 files changed

+118
-2
lines changed

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

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010

1111
using System.Collections;
12-
12+
using System.Collections.Concurrent;
13+
using System.Linq;
14+
using System.Threading;
1315
using NHibernate.Impl;
1416

1517
using NUnit.Framework;
@@ -56,6 +58,62 @@ public async Task WillGetSessionIdFromSessionLogsAsync()
5658
}
5759
}
5860

61+
[Test]
62+
public async Task WillGetSessionIdFromSessionLogsConcurrentAsync()
63+
{
64+
GlobalContext.Properties["sessionId"] = new SessionIdCapturer();
65+
66+
var semaphore = new ManualResetEventSlim();
67+
var failures = new ConcurrentBag<Exception>();
68+
var sessionIds = new ConcurrentDictionary<int, Guid>();
69+
var array = Enumerable.Range(1, 10).Select(
70+
i => new Thread(
71+
() =>
72+
{
73+
try
74+
{
75+
using (var s = Sfi.OpenSession())
76+
{
77+
sessionIds.AddOrUpdate(
78+
Thread.CurrentThread.ManagedThreadId,
79+
s.GetSessionImplementation().SessionId,
80+
(tId, old) => throw new InvalidOperationException(
81+
$"Thread {tId} has already session id {old}, while attempting to set it to" +
82+
$" {s.GetSessionImplementation().SessionId}"));
83+
semaphore.Wait();
84+
85+
for (int j = 0; j < 10; j++)
86+
{
87+
s.Get<Person>(i * 10 + j); //will execute some sql
88+
}
89+
}
90+
}
91+
catch (Exception e)
92+
{
93+
failures.Add(e);
94+
}
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 this way 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());
104+
105+
Assert.That(failures, Is.Empty, $"{failures.Count} thread(s) failed.");
106+
107+
var loggingEvent = spy.GetWholeLog();
108+
for (var i = 1; i < 11; i++)
109+
for (var j = 0; j < 10; j++)
110+
{
111+
var sessionId = sessionIds[array[i - 1].ManagedThreadId];
112+
Assert.That(loggingEvent, Does.Contain($"@p0 = {i * 10 + j} [Type: Int32 (0:0:0)] | SessionId: {sessionId}"));
113+
}
114+
}
115+
}
116+
59117
// IFixingRequired interface ensures the value is evaluated at log time rather than at log buffer flush time.
60118
public class SessionIdCapturer : IFixingRequired
61119
{

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

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System.Collections;
2-
2+
using System.Collections.Concurrent;
3+
using System.Linq;
4+
using System.Threading;
35
using NHibernate.Impl;
46

57
using NUnit.Framework;
@@ -45,6 +47,62 @@ public void WillGetSessionIdFromSessionLogs()
4547
}
4648
}
4749

50+
[Test]
51+
public void WillGetSessionIdFromSessionLogsConcurrent()
52+
{
53+
GlobalContext.Properties["sessionId"] = new SessionIdCapturer();
54+
55+
var semaphore = new ManualResetEventSlim();
56+
var failures = new ConcurrentBag<Exception>();
57+
var sessionIds = new ConcurrentDictionary<int, Guid>();
58+
var array = Enumerable.Range(1, 10).Select(
59+
i => new Thread(
60+
() =>
61+
{
62+
try
63+
{
64+
using (var s = Sfi.OpenSession())
65+
{
66+
sessionIds.AddOrUpdate(
67+
Thread.CurrentThread.ManagedThreadId,
68+
s.GetSessionImplementation().SessionId,
69+
(tId, old) => throw new InvalidOperationException(
70+
$"Thread {tId} has already session id {old}, while attempting to set it to" +
71+
$" {s.GetSessionImplementation().SessionId}"));
72+
semaphore.Wait();
73+
74+
for (int j = 0; j < 10; j++)
75+
{
76+
s.Get<Person>(i * 10 + j); //will execute some sql
77+
}
78+
}
79+
}
80+
catch (Exception e)
81+
{
82+
failures.Add(e);
83+
}
84+
})).ToArray();
85+
86+
using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}"))
87+
{
88+
Array.ForEach(array, thread => thread.Start());
89+
// Give some time to threads for reaching the wait, having this way all of them ready to do most of their job concurrently.
90+
Thread.Sleep(100);
91+
semaphore.Set();
92+
Array.ForEach(array, thread => thread.Join());
93+
94+
Assert.That(failures, Is.Empty, $"{failures.Count} thread(s) failed.");
95+
96+
var loggingEvent = spy.GetWholeLog();
97+
for (var i = 1; i < 11; i++)
98+
for (var j = 0; j < 10; j++)
99+
{
100+
var sessionId = sessionIds[array[i - 1].ManagedThreadId];
101+
Assert.That(loggingEvent, Does.Contain($"@p0 = {i * 10 + j} [Type: Int32 (0:0:0)] | SessionId: {sessionId}"));
102+
}
103+
}
104+
}
105+
48106
// IFixingRequired interface ensures the value is evaluated at log time rather than at log buffer flush time.
49107
public class SessionIdCapturer : IFixingRequired
50108
{

0 commit comments

Comments
 (0)