12
12
using System . Collections . Concurrent ;
13
13
using System . Linq ;
14
14
using System . Threading ;
15
+ using System . Threading . Tasks ;
16
+ using NHibernate . Cfg ;
15
17
using NHibernate . Impl ;
16
-
18
+ using NHibernate . SqlCommand ;
19
+ using NHibernate . Type ;
17
20
using NUnit . Framework ;
21
+ using NHibernate . Linq ;
18
22
19
23
namespace NHibernate . Test . NHSpecificTest . Logs
20
24
{
@@ -26,7 +30,6 @@ namespace NHibernate.Test.NHSpecificTest.Logs
26
30
using log4net . Core ;
27
31
using log4net . Layout ;
28
32
using log4net . Repository . Hierarchy ;
29
- using System . Threading . Tasks ;
30
33
31
34
[ TestFixture ]
32
35
public class LogsFixtureAsync : TestCase
@@ -41,6 +44,33 @@ protected override string MappingsAssembly
41
44
get { return "NHibernate.Test" ; }
42
45
}
43
46
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
+
44
74
[ Test ]
45
75
public async Task WillGetSessionIdFromSessionLogsAsync ( )
46
76
{
@@ -49,27 +79,92 @@ public async Task WillGetSessionIdFromSessionLogsAsync()
49
79
using ( var spy = new TextLogSpy ( "NHibernate.SQL" , "%message | SessionId: %property{sessionId}" ) )
50
80
using ( var s = Sfi . OpenSession ( ) )
51
81
{
52
- var sessionId = ( ( SessionImpl ) s ) . SessionId ;
82
+ var sessionId = ( ( SessionImpl ) s ) . SessionId ;
53
83
54
- await ( s . GetAsync < Person > ( 1 ) ) ; //will execute some sql
84
+ await ( s . GetAsync < Person > ( 1 ) ) ; //will execute some sql
55
85
56
86
var loggingEvent = spy . GetWholeLog ( ) ;
57
87
Assert . That ( loggingEvent . Contains ( sessionId . ToString ( ) ) , Is . True ) ;
58
88
}
59
89
}
60
90
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
+
61
147
[ Test ]
62
148
public async Task WillGetSessionIdFromSessionLogsConcurrentAsync ( )
63
149
{
64
150
GlobalContext . Properties [ "sessionId" ] = new SessionIdCapturer ( ) ;
65
151
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 ) ;
67
155
var failures = new ConcurrentBag < Exception > ( ) ;
68
156
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 =>
72
160
{
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
+ }
73
168
try
74
169
{
75
170
using ( var s = Sfi . OpenSession ( ) )
@@ -80,29 +175,21 @@ public async Task WillGetSessionIdFromSessionLogsConcurrentAsync()
80
175
( ti , old ) => throw new InvalidOperationException (
81
176
$ "Thread number { ti } has already session id { old } , while attempting to set it to" +
82
177
$ " { s . GetSessionImplementation ( ) . SessionId } ") ) ;
83
- semaphore . Wait ( ) ;
178
+ await ( semaphore . WaitAsync ( ) ) ;
84
179
85
180
for ( int j = 0 ; j < 10 ; j ++ )
86
181
{
87
- s . Get < Person > ( i * 10 + j ) ; //will execute some sql
182
+ await ( s . GetAsync < Person > ( i * 10 + j ) ) ; //will execute some sql
88
183
}
89
184
}
90
185
}
91
186
catch ( Exception e )
92
187
{
93
188
failures . Add ( e ) ;
94
189
}
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
+ } ) ) ) ;
104
191
105
- Assert . That ( failures , Is . Empty , $ "{ failures . Count } thread (s) failed.") ;
192
+ Assert . That ( failures , Is . Empty , $ "{ failures . Count } task (s) failed.") ;
106
193
107
194
var loggingEvent = spy . GetWholeLog ( ) ;
108
195
for ( var i = 1 ; i < 11 ; i ++ )
@@ -141,7 +228,7 @@ public TextLogSpy(string loggerName, string pattern)
141
228
Threshold = Level . All ,
142
229
Writer = new StringWriter ( stringBuilder )
143
230
} ;
144
- loggerImpl = ( Logger ) LogManager . GetLogger ( typeof ( LogsFixtureAsync ) . Assembly , loggerName ) . Logger ;
231
+ loggerImpl = ( Logger ) LogManager . GetLogger ( typeof ( LogsFixtureAsync ) . Assembly , loggerName ) . Logger ;
145
232
loggerImpl . AddAppender ( appender ) ;
146
233
previousLevel = loggerImpl . Level ;
147
234
loggerImpl . Level = Level . All ;
@@ -158,7 +245,37 @@ public void Dispose()
158
245
loggerImpl . Level = previousLevel ;
159
246
}
160
247
}
161
- }
162
248
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
+ }
163
269
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
+ }
164
281
}
0 commit comments