コード例 #1
0
        public async Task ZeroTimeoutDoesNotStartAsyncFuncAsync()
        {
            bool _hasRun = false;

            await Assert.ThrowsAsync <SyntheticTimeoutException>(async() =>
            {
                await FusionCacheExecutionUtils.RunAsyncFuncWithTimeoutAsync(async ct => { _hasRun = true; return(42); }, TimeSpan.Zero, false, t => { });
            });

            Assert.False(_hasRun);
        }
コード例 #2
0
        public async Task CancelingAsyncFuncActuallyCancelsItAsync()
        {
            int res = -1;
            var factoryTerminated  = false;
            var outerCancelDelayMs = 500;
            var innerDelayMs       = 2_000;
            await Assert.ThrowsAsync <OperationCanceledException>(async() =>
            {
                var cts = new CancellationTokenSource(outerCancelDelayMs);
                res     = await FusionCacheExecutionUtils.RunAsyncFuncWithTimeoutAsync(async ct => { await Task.Delay(innerDelayMs); ct.ThrowIfCancellationRequested(); factoryTerminated = true; return(42); }, Timeout.InfiniteTimeSpan, true, token: cts.Token);
            });

            await Task.Delay(innerDelayMs);

            Assert.Equal(-1, res);
            Assert.False(factoryTerminated);
        }
コード例 #3
0
        public async Task TimeoutEffectivelyWorksAsync()
        {
            int res          = -1;
            var timeoutMs    = 500;
            var innerDelayMs = 2_000;
            var sw           = Stopwatch.StartNew();
            await Assert.ThrowsAnyAsync <TimeoutException>(async() =>
            {
                res = await FusionCacheExecutionUtils.RunAsyncFuncWithTimeoutAsync(async ct => { await Task.Delay(innerDelayMs); ct.ThrowIfCancellationRequested(); return(42); }, TimeSpan.FromMilliseconds(timeoutMs));
            });

            sw.Stop();

            Assert.Equal(-1, res);
            Assert.True(sw.ElapsedMilliseconds >= timeoutMs);
            Assert.True(sw.ElapsedMilliseconds < innerDelayMs);
        }
コード例 #4
0
        private async Task <IFusionCacheEntry?> GetOrSetEntryInternalAsync <TValue>(string operationId, string key, Func <CancellationToken, Task <TValue> >?factory, FusionCacheEntryOptions?options, CancellationToken token)
        {
            if (options is null)
            {
                options = _options.DefaultEntryOptions;
            }

            token.ThrowIfCancellationRequested();

            FusionCacheMemoryEntry?_memoryEntry;
            bool _memoryEntryIsValid;

            // DIRECTLY CHECK MEMORY CACHE (TO AVOID LOCKING)
            (_memoryEntry, _memoryEntryIsValid) = _mca.TryGetEntry <TValue>(operationId, key);
            if (_memoryEntryIsValid)
            {
                if (_logger?.IsEnabled(LogLevel.Trace) ?? false)
                {
                    _logger.LogTrace("FUSION (K={CacheKey} OP={CacheOperationId}): using memory entry", key, operationId);
                }
                return(_memoryEntry);
            }

            var dca = GetCurrentDistributedAccessor();

            // SHORT-CIRCUIT: NO FACTORY AND NO USABLE DISTRIBUTED CACHE
            if (factory is null && (dca?.IsCurrentlyUsable() ?? false) == false)
            {
                if (options.IsFailSafeEnabled && _memoryEntry is object)
                {
                    if (_logger?.IsEnabled(LogLevel.Trace) ?? false)
                    {
                        _logger.LogTrace("FUSION (K={CacheKey} OP={CacheOperationId}): using memory entry (expired)", key, operationId);
                    }
                    return(_memoryEntry);
                }

                return(null);
            }

            IFusionCacheEntry?_entry;

            // LOCK
            var lockObj = await _reactor.AcquireLockAsync(key, operationId, options.LockTimeout, _logger, token).ConfigureAwait(false);

            try
            {
                // TRY AGAIN WITH MEMORY CACHE (AFTER THE LOCK HAS BEEN ACQUIRED, MAYBE SOMETHING CHANGED)
                (_memoryEntry, _memoryEntryIsValid) = _mca.TryGetEntry <TValue>(operationId, key);
                if (_memoryEntryIsValid)
                {
                    if (_logger?.IsEnabled(LogLevel.Trace) ?? false)
                    {
                        _logger.LogTrace("FUSION (K={CacheKey} OP={CacheOperationId}): using memory entry", key, operationId);
                    }
                    return(_memoryEntry);
                }

                // TRY WITH DISTRIBUTED CACHE (IF ANY)
                FusionCacheDistributedEntry <TValue>?distributedEntry = null;
                bool distributedEntryIsValid = false;

                if (dca?.IsCurrentlyUsable() ?? false)
                {
                    (distributedEntry, distributedEntryIsValid) = await dca.TryGetEntryAsync <TValue>(operationId, key, options, _memoryEntry is object, token).ConfigureAwait(false);
                }

                if (distributedEntryIsValid)
                {
                    _entry = FusionCacheMemoryEntry.CreateFromOptions(distributedEntry !.Value, options, false);
                }
                else
                {
                    TValue value;
                    bool   failSafeActivated = false;

                    if (factory is null)
                    {
                        // NO FACTORY

                        var fallbackEntry = MaybeGetFallbackEntry(operationId, key, distributedEntry, _memoryEntry, options, out failSafeActivated);
                        if (fallbackEntry is object)
                        {
                            value = fallbackEntry.GetValue <TValue>();
                        }
                        else
                        {
                            return(null);
                        }
                    }
                    else
                    {
                        // FACTORY

                        Task <TValue>?factoryTask = null;

                        try
                        {
                            var timeout = options.GetAppropriateFactoryTimeout(_memoryEntry is object || distributedEntry is object);

                            if (_logger?.IsEnabled(LogLevel.Debug) ?? false)
                            {
                                _logger.LogDebug("FUSION (K={CacheKey} OP={CacheOperationId}): calling the factory (timeout={Timeout})", key, operationId, timeout.ToLogString_Timeout());
                            }

                            value = await FusionCacheExecutionUtils.RunAsyncFuncWithTimeoutAsync(ct => factory(ct), timeout, options.AllowTimedOutFactoryBackgroundCompletion == false, x => factoryTask = x, token).ConfigureAwait(false);
                        }
                        catch (OperationCanceledException)
                        {
                            throw;
                        }
                        catch (Exception exc)
                        {
                            ProcessFactoryError(operationId, key, exc);

                            MaybeBackgroundCompleteTimedOutFactory <TValue>(operationId, key, factoryTask, options, dca, token);

                            var fallbackEntry = MaybeGetFallbackEntry(operationId, key, distributedEntry, _memoryEntry, options, out failSafeActivated);
                            if (fallbackEntry is object)
                            {
                                value = fallbackEntry.GetValue <TValue>();
                            }
                            else
                            {
                                throw;
                            }
                        }
                    }

                    _entry = FusionCacheMemoryEntry.CreateFromOptions(value, options, failSafeActivated);

                    if ((dca?.IsCurrentlyUsable() ?? false) && failSafeActivated == false)
                    {
                        // SAVE IN THE DISTRIBUTED CACHE (BUT ONLY IF NO FAIL-SAFE HAS BEEN EXECUTED)
                        await dca.SetEntryAsync <TValue>(operationId, key, _entry, options, token).ConfigureAwait(false);
                    }
                }

                // SAVING THE DATA IN THE MEMORY CACHE (EVEN IF IT IS FROM FAIL-SAFE)
                if (_entry is object)
                {
                    _mca.SetEntry <TValue>(operationId, key, _entry.AsMemoryEntry(), options);
                }
            }
            finally
            {
                ReleaseLock(operationId, key, lockObj);
            }

            return(_entry);
        }