// CAUTION - we disable the Obsolte warning for this extension method // IDistributedLockManager is marked as obsolete, but not because it will // go away in the near term. The webjob SDK marked it with the comment // that the current implementation is not ready for public use. however // as the Azure Functions framework already relies on it, we assume it // is also good enought for our scenarios. #pragma warning disable CS0618 public static async Task <IDistributedLock> AcquireLockAsync(this IDistributedLockManager distributedLockManager, string lockId, string lockOwner, TimeSpan?lockPeriod = default, TimeSpan?acquisitionTimeout = default, CancellationToken cancellationToken = default) { if (distributedLockManager is null) { throw new ArgumentNullException(nameof(distributedLockManager)); } if (string.IsNullOrEmpty(lockId)) { throw new ArgumentException($"'{nameof(lockId)}' cannot be null or empty", nameof(lockId)); } if (string.IsNullOrEmpty(lockOwner)) { throw new ArgumentException($"'{nameof(lockOwner)}' cannot be null or empty", nameof(lockOwner)); } using var acquisitionCancellationTokenSource = new CancellationTokenSource(acquisitionTimeout.GetValueOrDefault(TimeSpan.FromMinutes(1))); using var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(acquisitionCancellationTokenSource.Token, cancellationToken); while (!linkedCancellationTokenSource.Token.IsCancellationRequested) { var distributedLock = await distributedLockManager .TryLockAsync(null, lockId, lockOwner, null, lockPeriod.GetValueOrDefault(TimeSpan.FromMinutes(1)), linkedCancellationTokenSource.Token) .ConfigureAwait(false); if (distributedLock != null) { return(distributedLock); } } throw new TimeoutException($"Unable to acquire lock {lockId} for owner {lockOwner}"); }
private async Task AcquireOrRenewLeaseAsync() { if (_hostId == null) { _hostId = await _hostIdProvider.GetHostIdAsync(CancellationToken.None); } string lockName = GetBlobName(_hostId); DateTime requestStart = DateTime.UtcNow; if (HasLease) { try { await _lockManager.RenewAsync(LockHandle, CancellationToken.None); _lastRenewal = DateTime.UtcNow; _lastRenewalLatency = _lastRenewal - requestStart; } catch { // The lease was 'stolen'. Log details for debugging. string lastRenewalFormatted = _lastRenewal.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ", CultureInfo.InvariantCulture); int millisecondsSinceLastSuccess = (int)(DateTime.UtcNow - _lastRenewal).TotalMilliseconds; int lastRenewalMilliseconds = (int)_lastRenewalLatency.TotalMilliseconds; ProcessLeaseError($"Another host has acquired the lease. The last successful renewal completed at {lastRenewalFormatted} ({millisecondsSinceLastSuccess} milliseconds ago) with a duration of {lastRenewalMilliseconds} milliseconds."); } } else { string proposedLeaseId = _websiteInstanceId; LockHandle = await _lockManager.TryLockAsync(null, lockName, _websiteInstanceId, proposedLeaseId, _leaseTimeout, CancellationToken.None); if (LockHandle == null) { // We didn't have the lease and failed to acquire it. Common if somebody else already has it. // This is normal and does not warrant any logging. return; } _lastRenewal = DateTime.UtcNow; _lastRenewalLatency = _lastRenewal - requestStart; string message = $"Host lock lease acquired by instance ID '{_websiteInstanceId}'."; _logger.LogInformation(message); // We've successfully acquired the lease, change the timer to use our renewal interval SetTimerInterval(_renewalInterval); } }