private IDisposable AcquireLock(string resource, TimeSpan timeout) { if (_dedicatedConnection == null) { _dedicatedConnection = _storage.CreateAndOpenConnection(); } var lockId = Guid.NewGuid(); if (!_lockedResources.ContainsKey(resource)) { try { SqlServerDistributedLock.Acquire(_dedicatedConnection, resource, timeout); } catch (Exception) { ReleaseLock(resource, lockId, true); throw; } _lockedResources.Add(resource, new HashSet <Guid>()); } _lockedResources[resource].Add(lockId); return(new DisposableLock(this, resource, lockId)); }
private void UseConnectionDistributedLock(SqlServerStorage storage, Action <DbConnection> action) { try { storage.UseConnection(null, connection => { SqlServerDistributedLock.Acquire(connection, DistributedLockKey, DefaultLockTimeout); try { action(connection); } finally { SqlServerDistributedLock.Release(connection, DistributedLockKey); } }); } catch (DistributedLockTimeoutException e) when(e.Resource == DistributedLockKey) { // DistributedLockTimeoutException here doesn't mean that outdated records weren't removed. // It just means another Hangfire server did this work. _logger.Log( LogLevel.Debug, () => $@"An exception was thrown during acquiring distributed lock on the {DistributedLockKey} resource within {DefaultLockTimeout.TotalSeconds} seconds. Outdated records were not removed. It will be retried in {_checkInterval.TotalSeconds} seconds.", e); } }
public void Execute(CancellationToken cancellationToken) { foreach (var table in ProcessedTables) { Logger.Debug($"Removing outdated records from the '{table}' table..."); _storage.UseConnection(connection => { SqlServerDistributedLock.Acquire(connection, DistributedLockKey, DefaultLockTimeout); try { ExecuteNonQuery( connection, GetQuery(_storage.SchemaName, table), cancellationToken, new SqlParameter("@count", NumberOfRecordsInSinglePass), new SqlParameter("@now", DateTime.UtcNow)); } finally { SqlServerDistributedLock.Release(connection, DistributedLockKey); } }); Logger.Trace($"Outdated records removed from the '{table}' table."); } cancellationToken.WaitHandle.WaitOne(_checkInterval); }
public void Execute(CancellationToken cancellationToken) { foreach (var table in ProcessedTables) { Logger.DebugFormat("Removing outdated records from table '{0}'...", table); int removedCount = 0; do { _storage.UseConnection(connection => { SqlServerDistributedLock.Acquire(connection, DistributedLockKey, DefaultLockTimeout); try { removedCount = connection.Execute( String.Format(@" set transaction isolation level read committed; delete top (@count) from [{0}].[{1}] with (readpast) where ExpireAt < @now;", _storage.GetSchemaName(), table), new { now = DateTime.UtcNow, count = NumberOfRecordsInSinglePass }); } finally { SqlServerDistributedLock.Release(connection, DistributedLockKey); } }); if (removedCount > 0) { Logger.Trace(String.Format("Removed {0} outdated record(s) from '{1}' table.", removedCount, table)); cancellationToken.WaitHandle.WaitOne(DelayBetweenPasses); cancellationToken.ThrowIfCancellationRequested(); } } while (removedCount != 0); } cancellationToken.WaitHandle.WaitOne(_checkInterval); }
public void Execute(CancellationToken cancellationToken) { foreach (var table in ProcessedTables) { Logger.Debug($"Removing outdated records from table '{table}'..."); int removedCount = 0; do { _storage.UseConnection(connection => { SqlServerDistributedLock.Acquire(connection, DistributedLockKey, DefaultLockTimeout); try { removedCount = connection.Execute( $@"set transaction isolation level read committed; delete top (@count) from [{_storage.SchemaName}].[{table}] with (readpast) where ExpireAt < @now;", new { now = DateTime.UtcNow, count = NumberOfRecordsInSinglePass }); } finally { SqlServerDistributedLock.Release(connection, DistributedLockKey); } }); if (removedCount > 0) { Logger.Trace($"Removed {removedCount} outdated record(s) from '{table}' table."); cancellationToken.WaitHandle.WaitOne(DelayBetweenPasses); cancellationToken.ThrowIfCancellationRequested(); } // ReSharper disable once LoopVariableIsNeverChangedInsideLoop } while (removedCount != 0); } cancellationToken.WaitHandle.WaitOne(_checkInterval); }
private void ReleaseLock(string resource, Guid lockId, bool onDisposing) { try { if (_lockedResources.ContainsKey(resource)) { if (_lockedResources[resource].Contains(lockId)) { if (_lockedResources[resource].Remove(lockId) && _lockedResources[resource].Count == 0 && _lockedResources.Remove(resource) && _dedicatedConnection.State == ConnectionState.Open) { // Session-scoped application locks are held only when connection // is open. When connection is closed or broken, for example, when // there was an error, application lock is already released by SQL // Server itself, and we shouldn't do anything. SqlServerDistributedLock.Release(_dedicatedConnection, resource); } } } } catch (Exception) { if (!onDisposing) { throw; } } finally { if (_lockedResources.Count == 0) { _storage.ReleaseConnection(_dedicatedConnection); _dedicatedConnection = null; } } }