|
9 | 9 |
|
10 | 10 |
|
11 | 11 | using System.Collections;
|
12 |
| - |
| 12 | +using System.Collections.Concurrent; |
| 13 | +using System.Linq; |
| 14 | +using System.Threading; |
13 | 15 | using NHibernate.Impl;
|
14 | 16 |
|
15 | 17 | using NUnit.Framework;
|
@@ -56,6 +58,62 @@ public async Task WillGetSessionIdFromSessionLogsAsync()
|
56 | 58 | }
|
57 | 59 | }
|
58 | 60 |
|
| 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 | + |
59 | 117 | // IFixingRequired interface ensures the value is evaluated at log time rather than at log buffer flush time.
|
60 | 118 | public class SessionIdCapturer : IFixingRequired
|
61 | 119 | {
|
|
0 commit comments