1
- using System ;
1
+ using NPoco ;
2
+ using System ;
2
3
using System . Data ;
3
4
using System . Data . SqlClient ;
4
5
using System . Diagnostics ;
@@ -48,19 +49,23 @@ public async Task<bool> AcquireLockAsync(int millisecondsTimeout)
48
49
}
49
50
50
51
if ( ! ( _dbFactory . SqlContext . SqlSyntax is SqlServerSyntaxProvider sqlServerSyntaxProvider ) )
52
+ {
51
53
throw new NotSupportedException ( "SqlMainDomLock is only supported for Sql Server" ) ;
54
+ }
52
55
53
56
_sqlServerSyntax = sqlServerSyntaxProvider ;
54
57
55
58
_logger . Debug < SqlMainDomLock > ( "Acquiring lock..." ) ;
56
59
57
60
var tempId = Guid . NewGuid ( ) . ToString ( ) ;
58
61
59
- using var db = _dbFactory . CreateDatabase ( ) ;
60
- using var transaction = db . GetTransaction ( IsolationLevel . ReadCommitted ) ;
62
+ IUmbracoDatabase db = null ;
61
63
62
64
try
63
65
{
66
+ db = _dbFactory . CreateDatabase ( ) ;
67
+ db . BeginTransaction ( IsolationLevel . ReadCommitted ) ;
68
+
64
69
try
65
70
{
66
71
// wait to get a write lock
@@ -101,7 +106,8 @@ public async Task<bool> AcquireLockAsync(int millisecondsTimeout)
101
106
}
102
107
finally
103
108
{
104
- transaction . Complete ( ) ;
109
+ db ? . CompleteTransaction ( ) ;
110
+ db ? . Dispose ( ) ;
105
111
}
106
112
107
113
@@ -154,11 +160,11 @@ private void ListeningLoop()
154
160
// new MainDom will just take over.
155
161
if ( _cancellationTokenSource . IsCancellationRequested )
156
162
return ;
157
-
158
- using var db = _dbFactory . CreateDatabase ( ) ;
159
- using var transaction = db . GetTransaction ( IsolationLevel . ReadCommitted ) ;
163
+ IUmbracoDatabase db = null ;
160
164
try
161
165
{
166
+ db = _dbFactory . CreateDatabase ( ) ;
167
+ db . BeginTransaction ( IsolationLevel . ReadCommitted ) ;
162
168
// get a read lock
163
169
_sqlServerSyntax . ReadLock ( db , Constants . Locks . MainDom ) ;
164
170
@@ -182,7 +188,8 @@ private void ListeningLoop()
182
188
}
183
189
finally
184
190
{
185
- transaction . Complete ( ) ;
191
+ db ? . CompleteTransaction ( ) ;
192
+ db ? . Dispose ( ) ;
186
193
}
187
194
}
188
195
@@ -201,34 +208,47 @@ private Task<bool> WaitForExistingAsync(string tempId, int millisecondsTimeout)
201
208
202
209
return Task . Run ( ( ) =>
203
210
{
204
- using var db = _dbFactory . CreateDatabase ( ) ;
205
-
206
- var watch = new Stopwatch ( ) ;
207
- watch . Start ( ) ;
208
- while ( true )
211
+ try
209
212
{
210
- // poll very often, we need to take over as fast as we can
211
- // local testing shows the actual query to be executed from client/server is approx 300ms but would change depending on environment/IO
212
- Thread . Sleep ( 1000 ) ;
213
-
214
- var acquired = TryAcquire ( db , tempId , updatedTempId ) ;
215
- if ( acquired . HasValue )
216
- return acquired . Value ;
213
+ using var db = _dbFactory . CreateDatabase ( ) ;
217
214
218
- if ( watch . ElapsedMilliseconds >= millisecondsTimeout )
215
+ var watch = new Stopwatch ( ) ;
216
+ watch . Start ( ) ;
217
+ while ( true )
219
218
{
220
- return AcquireWhenMaxWaitTimeElapsed ( db ) ;
219
+ // poll very often, we need to take over as fast as we can
220
+ // local testing shows the actual query to be executed from client/server is approx 300ms but would change depending on environment/IO
221
+ Thread . Sleep ( 1000 ) ;
222
+
223
+ var acquired = TryAcquire ( db , tempId , updatedTempId ) ;
224
+ if ( acquired . HasValue )
225
+ return acquired . Value ;
226
+
227
+ if ( watch . ElapsedMilliseconds >= millisecondsTimeout )
228
+ {
229
+ return AcquireWhenMaxWaitTimeElapsed ( db ) ;
230
+ }
221
231
}
222
232
}
233
+ catch ( Exception ex )
234
+ {
235
+ _logger . Error < SqlMainDomLock > ( ex , "An error occurred trying to acquire and waiting for existing SqlMainDomLock to shutdown" ) ;
236
+ return false ;
237
+ }
238
+
223
239
} , _cancellationTokenSource . Token ) ;
224
240
}
225
241
226
242
private bool ? TryAcquire ( IUmbracoDatabase db , string tempId , string updatedTempId )
227
243
{
228
- using var transaction = db . GetTransaction ( IsolationLevel . ReadCommitted ) ;
244
+ // Creates a separate transaction to the DB instance so we aren't allocating tons of new DB instances for each transaction
245
+ // since this is executed in a tight loop
246
+
247
+ ITransaction transaction = null ;
229
248
230
249
try
231
250
{
251
+ transaction = db . GetTransaction ( IsolationLevel . ReadCommitted ) ;
232
252
// get a read lock
233
253
_sqlServerSyntax . ReadLock ( db , Constants . Locks . MainDom ) ;
234
254
@@ -274,14 +294,18 @@ private Task<bool> WaitForExistingAsync(string tempId, int millisecondsTimeout)
274
294
}
275
295
finally
276
296
{
277
- transaction . Complete ( ) ;
297
+ transaction ? . Complete ( ) ;
298
+ transaction ? . Dispose ( ) ;
278
299
}
279
300
280
301
return null ; // continue
281
302
}
282
303
283
304
private bool AcquireWhenMaxWaitTimeElapsed ( IUmbracoDatabase db )
284
305
{
306
+ // Creates a separate transaction to the DB instance so we aren't allocating tons of new DB instances for each transaction
307
+ // since this is executed in a tight loop
308
+
285
309
// if the timeout has elapsed, it either means that the other main dom is taking too long to shutdown,
286
310
// or it could mean that the previous appdomain was terminated and didn't clear out the main dom SQL row
287
311
// and it's just been left as an orphan row.
@@ -291,10 +315,12 @@ private bool AcquireWhenMaxWaitTimeElapsed(IUmbracoDatabase db)
291
315
292
316
_logger . Debug < SqlMainDomLock > ( "Timeout elapsed, assuming orphan row, acquiring MainDom." ) ;
293
317
294
- using var transaction = db . GetTransaction ( IsolationLevel . ReadCommitted ) ;
318
+ ITransaction transaction = null ;
295
319
296
320
try
297
321
{
322
+ transaction = db . GetTransaction ( IsolationLevel . ReadCommitted ) ;
323
+
298
324
_sqlServerSyntax . WriteLock ( db , Constants . Locks . MainDom ) ;
299
325
300
326
// so now we update the row with our appdomain id
@@ -317,7 +343,8 @@ private bool AcquireWhenMaxWaitTimeElapsed(IUmbracoDatabase db)
317
343
}
318
344
finally
319
345
{
320
- transaction . Complete ( ) ;
346
+ transaction ? . Complete ( ) ;
347
+ transaction ? . Dispose ( ) ;
321
348
}
322
349
}
323
350
@@ -368,11 +395,12 @@ protected virtual void Dispose(bool disposing)
368
395
369
396
if ( _dbFactory . Configured )
370
397
{
371
- using var db = _dbFactory . CreateDatabase ( ) ;
372
- using var transaction = db . GetTransaction ( IsolationLevel . ReadCommitted ) ;
373
-
398
+ IUmbracoDatabase db = null ;
374
399
try
375
400
{
401
+ db = _dbFactory . CreateDatabase ( ) ;
402
+ db . BeginTransaction ( IsolationLevel . ReadCommitted ) ;
403
+
376
404
// get a write lock
377
405
_sqlServerSyntax . WriteLock ( db , Constants . Locks . MainDom ) ;
378
406
@@ -399,7 +427,15 @@ protected virtual void Dispose(bool disposing)
399
427
}
400
428
finally
401
429
{
402
- transaction . Complete ( ) ;
430
+ try
431
+ {
432
+ db ? . CompleteTransaction ( ) ;
433
+ db ? . Dispose ( ) ;
434
+ }
435
+ catch ( Exception ex )
436
+ {
437
+ _logger . Error < SqlMainDomLock > ( ex , "Unexpected error during dispose when completing transaction." ) ;
438
+ }
403
439
}
404
440
}
405
441
}
0 commit comments