Beispiel #1
0
        public async Task <bool> AddAsync <T>(T data)
        {
            if (data == null || !IsCachingServer)
            {
                return(false);
            }
            try
            {
                PropertyInfo[] properties = typeof(T).GetProperties();

                var key = Helper.GetClassName <T>() + ":" + properties[0].GetValue(data).ToString();
                Tuple.Create(key, data);
                //Find key
                if (await Sut.ExistsAsync(key))
                {
                    if (await Sut.RemoveAsync(key))
                    {
                        LogManager.Logger.LogError(new Exception("Error when remove object " + Helper.GetClassName <T>() + "- Key: " + key));
                        return(false);
                    }
                }
                //Add
                return(await Sut.AddAsync(key, data));
            }
            catch (Exception ex)
            {
                LogManager.Logger.LogError(ex);
                return(false);
            }
        }
Beispiel #2
0
        public async Task <T> AddAsync(CacheItem <T> item)
        {
            await client.AddAsync(keyPrefix + item.Key, item.Value, item.Duration);



            return(item.Value);
        }
Beispiel #3
0
        /// <inheritdoc />
        public override async Task SaveAsync(ICacheClient redis, CancellationToken cancellationToken)
        {
            if (redis == null)
            {
                throw new ArgumentNullException(nameof(redis));
            }

            await
            Task.WhenAll(
                redis.SetAddAsync <string>("mudpie::programs", this.DbRef),
                redis.AddAsync($"mudpie::program:{this.DbRef}", this),
                CacheManager.UpdateAsync(this.DbRef, redis, this, cancellationToken));
        }
Beispiel #4
0
        public bool RegisterServer(WorldServerDto dto)
        {
            dto.Id = Guid.NewGuid();
            IDictionary <string, CacheValue <WorldServerDto> > servers = _cache.GetAllAsync <WorldServerDto>().ConfigureAwait(false).GetAwaiter().GetResult();

            if (servers.Values.Any(s => s?.Value?.Id == dto.Id))
            {
                _log.Warn("Server with the same Guid is already registered");
                return(true);
            }

            GetRunningServer = dto;
            _cache.AddAsync(ToKey(dto), dto);
            return(false);
        }
        public override async Task EventProcessingAsync(EventContext context)
        {
            if (String.IsNullOrEmpty(context.Event.ReferenceId))
            {
                return;
            }

            // TODO: Look into using a lock on reference id so we can ensure there is no race conditions with setting keys
            if (await _cacheClient.AddAsync(GetCacheKey(context), true, TimeSpan.FromMinutes(1)).AnyContext())
            {
                return;
            }

            context.IsCancelled = true;
        }
Beispiel #6
0
        /// <inheritdoc />
        public override async Task SaveAsync(ICacheClient redis, CancellationToken cancellationToken)
        {
            if (redis == null)
            {
                throw new ArgumentNullException(nameof(redis));
            }

            // ReSharper disable PossibleNullReferenceException
            await Task.WhenAll(
                redis.SetAddAsync <string>("mudpie::things", this.DbRef),
                redis.AddAsync($"mudpie::thing:{this.DbRef}", this),
                CacheManager.UpdateAsync(this.DbRef, redis, this, cancellationToken));

            // ReSharper restore PossibleNullReferenceException
        }
Beispiel #7
0
        public override async Task EventProcessingAsync(EventContext context)
        {
            if (String.IsNullOrEmpty(context.Event.ReferenceId))
            {
                return;
            }

            // TODO: Look into using a lock on reference id so we can ensure there is no race conditions with setting keys
            if (await _cacheClient.AddAsync(GetCacheKey(context), true, TimeSpan.FromMinutes(1)).AnyContext())
            {
                return;
            }

            _logger.Warn().Project(context.Event.ProjectId).Message("Discarding event due to duplicate reference id: {0}", context.Event.ReferenceId).Write();
            context.IsCancelled = true;
        }
        public override async Task EventProcessingAsync(EventContext context)
        {
            if (String.IsNullOrEmpty(context.Event.ReferenceId))
            {
                return;
            }

            if (await _cacheClient.AddAsync(GetCacheKey(context), true, TimeSpan.FromDays(1)).AnyContext())
            {
                context.SetProperty("AddedReferenceId", true);
                return;
            }

            _logger.Warn().Project(context.Event.ProjectId).Message("Discarding event due to duplicate reference id: {0}", context.Event.ReferenceId).Write();
            context.IsCancelled = true;
        }
Beispiel #9
0
        public static async Task ProcessRateLimitedAsync(string redisId, ICacheClient cache, Func <Task <RestResponse> > t)
        {
            string    key       = $"discord:ratelimit:" + redisId;
            Ratelimit rateLimit = await cache.GetAsync <Ratelimit>(key);

            if (rateLimit != null)
            {
                rateLimit.Remaining--;
                await cache.AddAsync(key, rateLimit);
            }

            if (!IsRatelimited(rateLimit))
            {
                var response = await t();
                await HandleRateLimit(cache, response, rateLimit, key);
            }
        }
Beispiel #10
0
 private static async Task HandleRateLimit(ICacheClient cache, RestResponse rc, Ratelimit ratelimit, string key)
 {
     if (!IsRatelimited(ratelimit))
     {
         if (rc.HttpResponseMessage.Headers.Contains("X-RateLimit-Limit"))
         {
             ratelimit           = new Ratelimit();
             ratelimit.Remaining = int.Parse(rc.HttpResponseMessage.Headers.GetValues("X-RateLimit-Remaining").ToList().FirstOrDefault());
             ratelimit.Limit     = int.Parse(rc.HttpResponseMessage.Headers.GetValues("X-RateLimit-Limit").ToList().FirstOrDefault());
             ratelimit.Reset     = long.Parse(rc.HttpResponseMessage.Headers.GetValues("X-RateLimit-Reset").ToList().FirstOrDefault());
             if (rc.HttpResponseMessage.Headers.Contains("X-RateLimit-Global"))
             {
                 ratelimit.Global = int.Parse(rc.HttpResponseMessage.Headers.GetValues("X-RateLimit-Global").ToList().FirstOrDefault());
             }
             await cache.AddAsync(key, ratelimit);
         }
     }
 }
Beispiel #11
0
        protected override async Task OnEnqueuing(object sender, EnqueuingEventArgs <T> enqueuingEventArgs)
        {
            string uniqueIdentifier = GetUniqueIdentifier(enqueuingEventArgs.Data);

            if (String.IsNullOrEmpty(uniqueIdentifier))
            {
                return;
            }

            bool success = await _cacheClient.AddAsync(uniqueIdentifier, true, _detectionWindow);

            if (!success)
            {
                var logger = _loggerFactory.CreateLogger <T>();
                logger.LogInformation("Discarding queue entry due to duplicate {UniqueIdentifier}", uniqueIdentifier);
                enqueuingEventArgs.Cancel = true;
            }
        }
Beispiel #12
0
        public async Task <DiscordUserPacket> GetCurrentUserAsync()
        {
            string key = $"discord:user:self";

            if (await cache.ExistsAsync(key))
            {
                return(await cache.GetAsync <DiscordUserPacket>(key));
            }

            RestResponse <DiscordUserPacket> rc = await RestClient
                                                  .GetAsync <DiscordUserPacket>($"/users/@me");

            await cache.AddAsync(key, rc.Data);

            return(rc.Data);
        }
Beispiel #13
0
        private static CurrencyExchange GetCurrencyRate(DateTime date)
        {
            var key = date.Date.ToString("yyyy-MM-dd");

            var cachedExchange = _cache.GetAsync <CurrencyExchange>(key).Result;

            if (cachedExchange.HasValue)
            {
                Console.WriteLine("Found in cache");
                return(cachedExchange.Value);
            }

            Console.WriteLine("Fetching from service");

            var response = _httpClient.GetAsync("http://api.fixer.io/" + key + "?base=USD").Result;
            var json     = response.Content.ReadAsStringAsync().Result;

            var exchange = JsonConvert.DeserializeObject <CurrencyExchange>(json);

            _cache.AddAsync(key, exchange).Wait();

            return(exchange);
        }
Beispiel #14
0
 public Task SetAsync(string key, IEnumerable <Scope> item)
 {
     return(cacheClient.AddAsync(key, item, TimeSpan.FromHours(2)));
 }
Beispiel #15
0
        public async Task <ILock> AcquireAsync(string name, TimeSpan?lockTimeout = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            _logger.Trace(() => $"AcquireAsync Name: {name} WillWait: {!cancellationToken.IsCancellationRequested}");

            if (!cancellationToken.IsCancellationRequested)
            {
                EnsureTopicSubscription();
            }

            if (!lockTimeout.HasValue)
            {
                lockTimeout = TimeSpan.FromMinutes(20);
            }

            bool allowLock = false;

            do
            {
                bool gotLock = false;

                try {
                    if (lockTimeout.Value == TimeSpan.Zero) // no lock timeout
                    {
                        gotLock = await _cacheClient.AddAsync(name, SystemClock.UtcNow).AnyContext();
                    }
                    else
                    {
                        gotLock = await _cacheClient.AddAsync(name, SystemClock.UtcNow, lockTimeout.Value).AnyContext();
                    }
                } catch { }

                if (gotLock)
                {
                    allowLock = true;
                    _logger.Trace("Acquired lock: {name}", name);

                    break;
                }

                _logger.Trace("Failed to acquire lock: {name}", name);
                if (cancellationToken.IsCancellationRequested)
                {
                    _logger.Trace("Cancellation requested");
                    break;
                }

                var keyExpiration = SystemClock.UtcNow.Add(await _cacheClient.GetExpirationAsync(name).AnyContext() ?? TimeSpan.Zero);
                var delayAmount   = keyExpiration.Subtract(SystemClock.UtcNow).Max(TimeSpan.FromMilliseconds(50));

                _logger.Trace("Delay amount: {0} Delay until: {1}", delayAmount, SystemClock.UtcNow.Add(delayAmount).ToString("mm:ss.fff"));

                var delayCancellationTokenSource = new CancellationTokenSource(delayAmount);
                var linkedCancellationToken      = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, delayCancellationTokenSource.Token).Token;

                var monitor = _monitors.GetOrAdd(name, new AsyncMonitor());
                var sw      = Stopwatch.StartNew();

                try {
                    using (await monitor.EnterAsync(linkedCancellationToken).AnyContext())
                        await monitor.WaitAsync(linkedCancellationToken).AnyContext();
                } catch (OperationCanceledException) {
                    if (delayCancellationTokenSource.IsCancellationRequested)
                    {
                        _logger.Trace("Retrying: Delay exceeded. Cancellation requested: {0}", cancellationToken.IsCancellationRequested);
                        continue;
                    }
                } finally {
                    sw.Stop();
                    _logger.Trace("Lock {name} waited {milliseconds}ms", name, sw.ElapsedMilliseconds);
                }
            } while (!cancellationToken.IsCancellationRequested);

            if (cancellationToken.IsCancellationRequested)
            {
                _logger.Trace("Cancellation requested.");
            }

            if (!allowLock)
            {
                return(null);
            }

            _logger.Trace("Returning lock: {name}", name);

            return(new DisposableLock(name, this, _logger));
        }
Beispiel #16
0
 public static Task <bool> AddAsync <T>(this ICacheClient client, string key, T value, DateTime?expiresAtUtc = null)
 {
     return(client.AddAsync(key, value, expiresAtUtc?.Subtract(SystemClock.UtcNow)));
 }
Beispiel #17
0
        public async Task OnMessage(WebhookResponse response)
        {
            using (var context = new MikiContext())
            {
                DblVoteObject voteObject = response.data.ToObject <DblVoteObject>();

                if (voteObject.Type == "upvote")
                {
                    User u = await context.Users.FindAsync(voteObject.UserId);

                    if (!await redisClient.ExistsAsync($"dbl:vote:{voteObject.UserId}"))
                    {
                        u.DblVotes++;
                        await redisClient.AddAsync($"dbl:vote:{voteObject.UserId}", 1, new TimeSpan(1, 0, 0, 0));

                        int addedCurrency = 100 * ((await u.IsDonatorAsync(context)) ? 2 : 1);

                        u.Currency += addedCurrency;

                        DogStatsd.Increment("votes.dbl");

                        Achievement achievement = await context.Achievements.FindAsync("voter", u.Id);

                        bool unlockedAchievement = false;

                        switch (u.DblVotes)
                        {
                        case 1:
                        {
                            achievement = new Achievement()
                            {
                                Name       = "voter",
                                Rank       = 0,
                                UnlockedAt = DateTime.Now,
                                Id         = u.Id
                            };
                            unlockedAchievement = true;
                        }
                        break;

                        case 25:
                        {
                            achievement.Rank    = 1;
                            unlockedAchievement = true;
                        }
                        break;

                        case 200:
                        {
                            achievement.Rank    = 2;
                            unlockedAchievement = true;
                        }
                        break;
                        }

                        await context.SaveChangesAsync();
                    }
                }
            }

            var client = new RestClient(ApiUrl)
                         .SetAuthorization(ApiAuthorization);

            await client.PostAsync("api/users/121919449996460033/messages", "{\"content\": \"yo.\"}");
        }
Beispiel #18
0
        private async Task OnGuildMemberUpdate(GuildMemberUpdateEventArgs arg)
        {
            DiscordGuildPacket guild = await _discordClient.GetGuildAsync(arg.GuildId);

            if (guild.Members == null)
            {
                guild.Members = new List <DiscordGuildMemberPacket>();
            }

            int index = guild.Members.FindIndex(x => x.UserId == arg.User.Id);

            DiscordGuildMemberPacket packet;

            if (index == -1)
            {
                packet = new DiscordGuildMemberPacket
                {
                    User     = arg.User,
                    Roles    = arg.RoleIds.ToList(),
                    Nickname = arg.Nickname,
                    UserId   = arg.User.Id,
                    GuildId  = arg.GuildId,
                };

                guild.Members.Add(packet);
            }
            else
            {
                guild.Members[index].Nickname = arg.Nickname;
                guild.Members[index].Roles    = arg.RoleIds.ToList();
                guild.Members[index].User     = arg.User;

                packet = guild.Members[index];
            }

            await _cacheClient.AddAsync($"discord:guild:{arg.GuildId}:user:{arg.User.Id}", packet);

            await _cacheClient.AddAsync($"discord:guild:{arg.GuildId}", guild);
        }
 public Task SetAsync(string key, Client item)
 {
     return(cacheClient.AddAsync(key, item, TimeSpan.FromHours(2)));
 }
Beispiel #20
0
        public async Task <ILock> AcquireAsync(string name, TimeSpan?lockTimeout = null, CancellationToken cancellationToken = default(CancellationToken))
        {
#if DEBUG
            Logger.Trace().Message($"AcquireAsync: {name}").Write();
#endif
            EnsureTopicSubscription();
            if (!lockTimeout.HasValue)
            {
                lockTimeout = TimeSpan.FromMinutes(20);
            }

            bool allowLock = false;

            do
            {
                bool gotLock;
                if (lockTimeout.Value == TimeSpan.Zero) // no lock timeout
                {
                    gotLock = await _cacheClient.AddAsync(name, DateTime.UtcNow).AnyContext();
                }
                else
                {
                    gotLock = await _cacheClient.AddAsync(name, DateTime.UtcNow, lockTimeout.Value).AnyContext();
                }

                if (gotLock)
                {
                    allowLock = true;
#if DEBUG
                    Logger.Trace().Message($"Acquired lock: {name}").Write();
#endif
                    break;
                }

#if DEBUG
                Logger.Trace().Message($"Failed to acquire lock: {name}").Write();
#endif
                if (cancellationToken.IsCancellationRequested)
                {
                    Logger.Trace().Message("Cancellation Requested").Write();
                    break;
                }

                var keyExpiration = DateTime.UtcNow.Add(await _cacheClient.GetExpirationAsync(name).AnyContext() ?? TimeSpan.Zero);
                var delayAmount   = keyExpiration.Subtract(DateTime.UtcNow).Max(TimeSpan.FromMilliseconds(50));
#if DEBUG
                Logger.Trace().Message("Delay amount: {0} Delay until: {1}", delayAmount, DateTime.UtcNow.Add(delayAmount).ToString("mm:ss.fff")).Write();
#endif
                var delayCancellationTokenSource = new CancellationTokenSource(delayAmount);
                var linkedCancellationToken      = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, delayCancellationTokenSource.Token).Token;

                var monitor = _monitors.GetOrAdd(name, new AsyncMonitor());
#if DEBUG
                var sw = Stopwatch.StartNew();
#endif
                try {
                    using (await monitor.EnterAsync(linkedCancellationToken))
                        await monitor.WaitAsync(linkedCancellationToken).AnyContext();
                } catch (TaskCanceledException) {
                    if (delayCancellationTokenSource.IsCancellationRequested)
                    {
#if DEBUG
                        Logger.Trace().Message("Retrying: Delay exceeded").Write();
#endif
                        continue;
                    }
                } finally {
#if DEBUG
                    sw.Stop();
                    Logger.Trace().Message($"Lock {name} waited {sw.ElapsedMilliseconds}ms").Write();
#endif
                }
            } while (!cancellationToken.IsCancellationRequested);

            if (cancellationToken.IsCancellationRequested)
            {
                Logger.Trace().Message("Cancellation requested.").Write();
            }

            if (!allowLock)
            {
                return(null);
            }
#if DEBUG
            Logger.Trace().Message($"Returning lock: {name}").Write();
#endif
            return(new DisposableLock(name, this));
        }
Beispiel #21
0
 public Task SetAsync(string key, IEnumerable <Claim> item)
 {
     return(cacheClient.AddAsync(key, item, TimeSpan.FromMinutes(30)));
 }
Beispiel #22
0
 public void RegisterSession(PlayerSessionDto dto)
 {
     _cache.AddAsync(ToKey(dto), dto).GetAwaiter().GetResult();
 }
Beispiel #23
0
        public async Task <ILock> AcquireAsync(string resource, TimeSpan?timeUntilExpires = null, bool releaseOnDispose = true, CancellationToken cancellationToken = default)
        {
            bool isTraceLogLevelEnabled = _logger.IsEnabled(LogLevel.Trace);
            bool shouldWait             = !cancellationToken.IsCancellationRequested;

            if (isTraceLogLevelEnabled)
            {
                _logger.LogTrace("Attempting to acquire lock: {Resource}", resource);
            }

            if (!timeUntilExpires.HasValue)
            {
                timeUntilExpires = TimeSpan.FromMinutes(20);
            }

            bool   gotLock = false;
            string lockId  = GenerateNewLockId();
            var    sw      = Stopwatch.StartNew();

            try {
                do
                {
                    try {
                        if (timeUntilExpires.Value == TimeSpan.Zero) // no lock timeout
                        {
                            gotLock = await _cacheClient.AddAsync(resource, lockId).AnyContext();
                        }
                        else
                        {
                            gotLock = await _cacheClient.AddAsync(resource, lockId, timeUntilExpires).AnyContext();
                        }
                    } catch { }

                    if (gotLock)
                    {
                        break;
                    }

                    if (isTraceLogLevelEnabled)
                    {
                        _logger.LogTrace("Failed to acquire lock: {Resource}", resource);
                    }

                    if (cancellationToken.IsCancellationRequested)
                    {
                        if (isTraceLogLevelEnabled && shouldWait)
                        {
                            _logger.LogTrace("Cancellation requested");
                        }

                        break;
                    }

                    var autoResetEvent = _autoResetEvents.AddOrUpdate(resource, new ResetEventWithRefCount {
                        RefCount = 1, Target = new AsyncAutoResetEvent()
                    }, (n, e) => { e.RefCount++; return(e); });
                    if (!_isSubscribed)
                    {
                        await EnsureTopicSubscriptionAsync().AnyContext();
                    }

                    var keyExpiration = SystemClock.UtcNow.SafeAdd(await _cacheClient.GetExpirationAsync(resource).AnyContext() ?? TimeSpan.Zero);
                    var delayAmount   = keyExpiration.Subtract(SystemClock.UtcNow);

                    // delay a minimum of 50ms
                    if (delayAmount < TimeSpan.FromMilliseconds(50))
                    {
                        delayAmount = TimeSpan.FromMilliseconds(50);
                    }

                    // delay a maximum of 3 seconds
                    if (delayAmount > TimeSpan.FromSeconds(3))
                    {
                        delayAmount = TimeSpan.FromSeconds(3);
                    }

                    if (isTraceLogLevelEnabled)
                    {
                        _logger.LogTrace("Will wait {Delay:g} before retrying to acquire lock: {Resource}", delayAmount, resource);
                    }

                    // wait until we get a message saying the lock was released or 3 seconds has elapsed or cancellation has been requested
                    using (var maxWaitCancellationTokenSource = new CancellationTokenSource(delayAmount))
                        using (var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, maxWaitCancellationTokenSource.Token)) {
                            try {
                                await autoResetEvent.Target.WaitAsync(linkedCancellationTokenSource.Token).AnyContext();
                            } catch (OperationCanceledException) {}
                        }

                    Thread.Yield();
                } while (!cancellationToken.IsCancellationRequested);
            } finally {
                bool shouldRemove = false;
                _autoResetEvents.TryUpdate(resource, (n, e) => {
                    e.RefCount--;
                    if (e.RefCount == 0)
                    {
                        shouldRemove = true;
                    }
                    return(e);
                });

                if (shouldRemove)
                {
                    _autoResetEvents.TryRemove(resource, out var _);
                }
            }
            sw.Stop();

            if (!gotLock)
            {
                if (cancellationToken.IsCancellationRequested && isTraceLogLevelEnabled)
                {
                    _logger.LogTrace("Cancellation requested for lock {Resource} after {Duration:g}", resource, sw.Elapsed);
                }
                else if (_logger.IsEnabled(LogLevel.Warning))
                {
                    _logger.LogWarning("Failed to acquire lock {Resource} after {Duration:g}", resource, lockId, sw.Elapsed);
                }

                return(null);
            }

            if (sw.Elapsed > TimeSpan.FromSeconds(5) && _logger.IsEnabled(LogLevel.Warning))
            {
                _logger.LogWarning("Acquired lock {Resource} ({LockId}) after {Duration:g}", resource, lockId, sw.Elapsed);
            }
            else if (_logger.IsEnabled(LogLevel.Debug))
            {
                _logger.LogDebug("Acquired lock {Resource} ({LockId}) after {Duration:g}", resource, lockId, sw.Elapsed);
            }

            return(new DisposableLock(resource, lockId, sw.Elapsed, this, _logger, releaseOnDispose));
        }
Beispiel #24
0
 public Task <bool> AddAsync <T>(object key, T value)
 {
     return(_clientExt.AddAsync(RedisUtils.KeyBuilder <T>(key), value));
 }
Beispiel #25
0
 public override Task SetAsync(string key, object value, TimeSpan?slidingExpireTime = null, TimeSpan?absoluteExpireTime = null)
 {
     return(_cacheClient.AddAsync(GetLocalizedKey(key), value, absoluteExpireTime ?? slidingExpireTime ?? DefaultAbsoluteExpireTime ?? DefaultSlidingExpireTime));
 }
Beispiel #26
0
        public async Task <ILock> AcquireAsync(string name, TimeSpan?lockTimeout = null, CancellationToken cancellationToken = default)
        {
            bool isTraceLogLevelEnabled = _logger.IsEnabled(LogLevel.Trace);

            if (isTraceLogLevelEnabled)
            {
                _logger.LogTrace("AcquireAsync Name: {Name} WillWait: {WillWait}", name, !cancellationToken.IsCancellationRequested);
            }

            if (!cancellationToken.IsCancellationRequested)
            {
                await EnsureTopicSubscriptionAsync().AnyContext();
            }

            if (!lockTimeout.HasValue)
            {
                lockTimeout = TimeSpan.FromMinutes(20);
            }

            bool allowLock = false;

            do
            {
                bool gotLock = false;

                try {
                    if (lockTimeout.Value == TimeSpan.Zero) // no lock timeout
                    {
                        gotLock = await _cacheClient.AddAsync(name, SystemClock.UtcNow).AnyContext();
                    }
                    else
                    {
                        gotLock = await _cacheClient.AddAsync(name, SystemClock.UtcNow, lockTimeout.Value).AnyContext();
                    }
                } catch { }

                if (gotLock)
                {
                    allowLock = true;
                    if (isTraceLogLevelEnabled)
                    {
                        _logger.LogTrace("Acquired lock: {Name}", name);
                    }

                    break;
                }

                if (isTraceLogLevelEnabled)
                {
                    _logger.LogTrace("Failed to acquire lock: {Name}", name);
                }
                if (cancellationToken.IsCancellationRequested)
                {
                    if (isTraceLogLevelEnabled)
                    {
                        _logger.LogTrace("Cancellation requested");
                    }
                    break;
                }

                var keyExpiration = SystemClock.UtcNow.SafeAdd(await _cacheClient.GetExpirationAsync(name).AnyContext() ?? TimeSpan.Zero);
                var delayAmount   = keyExpiration.Subtract(SystemClock.UtcNow).Max(TimeSpan.FromMilliseconds(50));

                if (isTraceLogLevelEnabled)
                {
                    _logger.LogTrace("Delay amount: {Delay} Delay until: {DelayUntil}", delayAmount, SystemClock.UtcNow.SafeAdd(delayAmount).ToString("mm:ss.fff"));
                }

                using (var delayCancellationTokenSource = new CancellationTokenSource(delayAmount))
                    using (var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, delayCancellationTokenSource.Token)) {
                        var autoResetEvent = _autoResetEvents.GetOrAdd(name, new AsyncAutoResetEvent());
                        var sw             = Stopwatch.StartNew();

                        try {
                            await autoResetEvent.WaitAsync(linkedCancellationTokenSource.Token).AnyContext();
                        } catch (OperationCanceledException) {
                            if (delayCancellationTokenSource.IsCancellationRequested)
                            {
                                if (isTraceLogLevelEnabled)
                                {
                                    _logger.LogTrace("Retrying: Delay exceeded. Cancellation requested: {IsCancellationRequested}", cancellationToken.IsCancellationRequested);
                                }
                                continue;
                            }
                        } finally {
                            sw.Stop();
                            if (isTraceLogLevelEnabled)
                            {
                                _logger.LogTrace("Lock {Name} waited {Milliseconds}ms", name, sw.ElapsedMilliseconds);
                            }
                        }
                    }
            } while (!cancellationToken.IsCancellationRequested);

            if (cancellationToken.IsCancellationRequested && isTraceLogLevelEnabled)
            {
                _logger.LogTrace("Cancellation requested.");
            }

            if (!allowLock)
            {
                return(null);
            }

            if (isTraceLogLevelEnabled)
            {
                _logger.LogTrace("Returning lock: {Name}", name);
            }
            return(new DisposableLock(name, this, _logger));
        }