Example #1
0
        // 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);
            }
        }