14
14
using System . IO ;
15
15
using System . Linq ;
16
16
using System . Text . RegularExpressions ;
17
+ using System . Threading ;
17
18
using System . Transactions ;
18
19
using log4net ;
19
20
using log4net . Repository . Hierarchy ;
21
+ using NHibernate . Cfg ;
20
22
using NHibernate . Dialect ;
21
23
using NHibernate . Driver ;
22
24
using NHibernate . Engine ;
25
27
namespace NHibernate . Test . NHSpecificTest . NH3023
26
28
{
27
29
using System . Threading . Tasks ;
28
- using System . Threading ;
29
30
[ TestFixture ]
30
- public class DeadlockConnectionPoolIssueTestAsync : BugTestCase
31
+ public class DeadlockConnectionPoolIssueAsync : BugTestCase
31
32
{
32
- private static readonly ILog _log = LogManager . GetLogger ( typeof ( DeadlockConnectionPoolIssueTestAsync ) ) ;
33
+ private static readonly ILog _log = LogManager . GetLogger ( typeof ( DeadlockConnectionPoolIssueAsync ) ) ;
34
+
35
+ protected virtual bool UseConnectionOnSystemTransactionPrepare => true ;
36
+
37
+ protected override void Configure ( Configuration configuration )
38
+ {
39
+ configuration . SetProperty (
40
+ Cfg . Environment . UseConnectionOnSystemTransactionPrepare ,
41
+ UseConnectionOnSystemTransactionPrepare . ToString ( ) ) ;
42
+ }
33
43
34
44
// Uses directly SqlConnection.
35
45
protected override bool AppliesTo ( ISessionFactoryImplementor factory )
@@ -47,27 +57,31 @@ protected override void OnSetUp()
47
57
48
58
protected override void OnTearDown ( )
49
59
{
60
+ // Before clearing the pool for dodging pool corruption, we need to wait
61
+ // for late transaction processing not yet ended.
62
+ Thread . Sleep ( 100 ) ;
50
63
//
51
64
// Hopefully this will clean up the pool so that teardown can succeed
52
65
//
53
66
SqlConnection . ClearAllPools ( ) ;
54
67
68
+ RunScript ( "db-teardown.sql" ) ;
69
+
55
70
using ( var s = OpenSession ( ) )
56
71
{
57
72
s . CreateQuery ( "delete from System.Object" ) . ExecuteUpdate ( ) ;
58
73
}
59
- RunScript ( "db-teardown.sql" ) ;
60
74
}
61
75
62
76
[ Theory ]
63
- public async Task ConnectionPoolCorruptionAfterDeadlockAsync ( bool distributed )
77
+ public async Task ConnectionPoolCorruptionAfterDeadlockAsync ( bool distributed , bool disposeSessionBeforeScope )
64
78
{
65
79
var tryCount = 0 ;
66
80
var id = 1 ;
67
- var missingDeadlock = false ;
68
81
do
69
82
{
70
83
tryCount ++ ;
84
+ var missingDeadlock = false ;
71
85
72
86
try
73
87
{
@@ -78,13 +92,13 @@ public async Task ConnectionPoolCorruptionAfterDeadlockAsync(bool distributed)
78
92
// wrong when disposing a connection from transaction scope completion.
79
93
// Note that the transaction completion event can execute as soon as the deadlock occurs. It does
80
94
// not wait for the scope disposal.
81
- using ( var session = OpenSession ( ) )
82
- //using ( var session = Sfi.WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession())
83
- using ( var scope = distributed ? CreateDistributedTransactionScope ( ) : new TransactionScope ( TransactionScopeAsyncFlowOption . Enabled ) )
95
+ var session = OpenSession ( ) ;
96
+ var scope = distributed ? CreateDistributedTransactionScope ( ) : new TransactionScope ( TransactionScopeAsyncFlowOption . Enabled ) ;
97
+ try
84
98
{
85
99
_log . Debug ( "Session and scope opened" ) ;
86
100
session . GetSessionImplementation ( ) . Factory . TransactionFactory
87
- . EnlistInSystemTransactionIfNeeded ( session . GetSessionImplementation ( ) ) ;
101
+ . EnlistInSystemTransactionIfNeeded ( session . GetSessionImplementation ( ) ) ;
88
102
_log . Debug ( "Session enlisted" ) ;
89
103
try
90
104
{
@@ -101,17 +115,6 @@ public async Task ConnectionPoolCorruptionAfterDeadlockAsync(bool distributed)
101
115
// It did what it was supposed to do.
102
116
//
103
117
_log . InfoFormat ( "Expected deadlock on attempt {0}. {1}" , tryCount , x . Message ) ;
104
-
105
- // Check who takes time in the disposing
106
- var chrono = new Stopwatch ( ) ;
107
- chrono . Start ( ) ;
108
- scope . Dispose ( ) ;
109
- _log . Debug ( "Scope disposed" ) ;
110
- Assert . That ( chrono . Elapsed , Is . LessThan ( TimeSpan . FromSeconds ( 2 ) ) , "Abnormal scope disposal duration" ) ;
111
- chrono . Restart ( ) ;
112
- session . Dispose ( ) ;
113
- _log . Debug ( "Session disposed" ) ;
114
- Assert . That ( chrono . Elapsed , Is . LessThan ( TimeSpan . FromSeconds ( 2 ) ) , "Abnormal session disposal duration" ) ;
115
118
continue ;
116
119
}
117
120
@@ -130,7 +133,7 @@ public async Task ConnectionPoolCorruptionAfterDeadlockAsync(bool distributed)
130
133
new DomainClass
131
134
{
132
135
Id = id ++ ,
133
- ByteData = new byte [ ] { 1 , 2 , 3 }
136
+ ByteData = new byte [ ] { 1 , 2 , 3 }
134
137
} ) ) ;
135
138
136
139
await ( session . FlushAsync ( ) ) ;
@@ -151,6 +154,57 @@ public async Task ConnectionPoolCorruptionAfterDeadlockAsync(bool distributed)
151
154
scope . Complete ( ) ;
152
155
_log . Debug ( "Scope completed" ) ;
153
156
}
157
+ finally
158
+ {
159
+ // Check who takes time in the disposing
160
+ var chrono = new Stopwatch ( ) ;
161
+ if ( disposeSessionBeforeScope )
162
+ {
163
+ try
164
+ {
165
+ chrono . Start ( ) ;
166
+ session . Dispose ( ) ;
167
+ _log . Debug ( "Session disposed" ) ;
168
+ Assert . That ( chrono . Elapsed , Is . LessThan ( TimeSpan . FromSeconds ( 2 ) ) , "Abnormal session disposal duration" ) ;
169
+ }
170
+ catch ( Exception ex )
171
+ {
172
+ // Log in case it gets hidden by the next finally
173
+ _log . Warn ( "Session disposal failure" , ex ) ;
174
+ throw ;
175
+ }
176
+ finally
177
+ {
178
+ chrono . Restart ( ) ;
179
+ scope . Dispose ( ) ;
180
+ _log . Debug ( "Scope disposed" ) ;
181
+ Assert . That ( chrono . Elapsed , Is . LessThan ( TimeSpan . FromSeconds ( 2 ) ) , "Abnormal scope disposal duration" ) ;
182
+ }
183
+ }
184
+ else
185
+ {
186
+ try
187
+ {
188
+ chrono . Start ( ) ;
189
+ scope . Dispose ( ) ;
190
+ _log . Debug ( "Scope disposed" ) ;
191
+ Assert . That ( chrono . Elapsed , Is . LessThan ( TimeSpan . FromSeconds ( 2 ) ) , "Abnormal scope disposal duration" ) ;
192
+ }
193
+ catch ( Exception ex )
194
+ {
195
+ // Log in case it gets hidden by the next finally
196
+ _log . Warn ( "Scope disposal failure" , ex ) ;
197
+ throw ;
198
+ }
199
+ finally
200
+ {
201
+ chrono . Restart ( ) ;
202
+ session . Dispose ( ) ;
203
+ _log . Debug ( "Session disposed" ) ;
204
+ Assert . That ( chrono . Elapsed , Is . LessThan ( TimeSpan . FromSeconds ( 2 ) ) , "Abnormal session disposal duration" ) ;
205
+ }
206
+ }
207
+ }
154
208
_log . Debug ( "Session and scope disposed" ) ;
155
209
}
156
210
catch ( AssertionException )
0 commit comments