public EnterLock(IDbFactory db, IIntegrationDatabaseConfiguration configuration, DistributedMutexContext context, TimeSpan queryLockInterval, CancellationToken cancellationToken) { _db = db; _configuration = configuration; _newLock = new DbDistributedMutexLock(context.Name) { MachineName = Environment.MachineName, Description = context.Description.MaxLength(255) }; using (IDbSession session = db.OpenSession()) { TimeSpan waitTime = context.WaitTime; int maxRetries = Math.Max((int)Math.Ceiling(waitTime.TotalMilliseconds / queryLockInterval.TotalMilliseconds), 1); int attempts = 0; DbDistributedMutexLock currentLock; while (true) { if (TryEnterLock(session, out currentLock)) { _onCancel = cancellationToken.Register(ReleaseLock); return; } if (currentLock == null) { throw new InvalidOperationException($"Cannot obtain lock for '{_newLock.Name}', but current lock is null."); } if (++attempts >= maxRetries) { break; } context.OnWaiting($"{currentLock}. Waiting for {queryLockInterval} (attemt {attempts} of {maxRetries})."); cancellationToken.WaitHandle.WaitOne(queryLockInterval); } throw new DistributedMutexTimeoutException($"Unable to acquire lock '{_newLock.Name}' within wait time ({waitTime}) using {attempts} attempts with a query interval of {queryLockInterval}. {currentLock}"); } }
private bool TryEnterLock(IDbSession session, out DbDistributedMutexLock currentLock) { currentLock = session.Query <DbDistributedMutexLock>($@" BEGIN TRY INSERT INTO [{_configuration.TableName(IntegrationDbTable.DistributedMutex)}] (Name, LockId, CreatedAt, MachineName, Description) VALUES (@Name, @LockId, @CreatedAt, @MachineName, @Description) END TRY BEGIN CATCH DECLARE @ErrorNumber AS INT SELECT @ErrorNumber = ERROR_NUMBER(); IF @ErrorNumber = 2627 -- PRIMARY KEY VIOLATION SELECT * FROM [{_configuration.TableName(IntegrationDbTable.DistributedMutex)}] WHERE (Name = @Name); ELSE THROW END CATCH", _newLock).FirstOrDefault(); return(currentLock == null); }