示例#1
0
        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void ExecuteOperation(string operationId, string key, Action <CancellationToken> action, string actionDescription, FusionCacheEntryOptions options, DistributedCacheEntryOptions?distributedOptions, CancellationToken token)
        {
            if (IsCurrentlyUsable() == false)
            {
                return;
            }

            token.ThrowIfCancellationRequested();

            var actionDescriptionInner = actionDescription + (options.AllowBackgroundDistributedCacheOperations ? " (background)" : null);

            if (_logger?.IsEnabled(LogLevel.Debug) ?? false)
            {
                _logger.LogDebug("FUSION (K={CacheKey} OP={CacheOperationId}): " + actionDescriptionInner + " {DistributedOptions}", key, operationId, distributedOptions.ToLogString());
            }

            FusionCacheExecutionUtils.RunSyncActionAdvanced(
                action,
                options.DistributedCacheHardTimeout,
                false,
                options.AllowBackgroundDistributedCacheOperations == false,
                exc => ProcessCacheError(operationId, key, exc, actionDescriptionInner),
                false,
                token
                );
        }
        public void ZeroTimeoutDoesNotStartAsyncAction()
        {
            bool _hasRun = false;

            Assert.Throws <SyntheticTimeoutException>(() =>
            {
                FusionCacheExecutionUtils.RunAsyncActionWithTimeout(async ct => { _hasRun = true; }, TimeSpan.Zero, false, t => { });
            });
            Assert.False(_hasRun);
        }
        public async Task ZeroTimeoutDoesNotStartAsyncActionAsync()
        {
            bool _hasRun = false;

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

            Assert.False(_hasRun);
        }
        public void DoNotCancelWhenTimeoutActuallyWorks()
        {
            var factoryCompleted = false;
            var timeoutMs        = 500;
            var innerDelayMs     = 2_000;

            Assert.ThrowsAny <TimeoutException>(() =>
            {
                FusionCacheExecutionUtils.RunAsyncActionWithTimeout(async ct => { await Task.Delay(innerDelayMs); ct.ThrowIfCancellationRequested(); factoryCompleted = true; }, TimeSpan.FromMilliseconds(timeoutMs), false);
            });
            Thread.Sleep(innerDelayMs);

            Assert.True(factoryCompleted);
        }
        public async Task CancelWhenTimeoutActuallyWorksAsync()
        {
            var factoryCompleted = false;
            var timeoutMs        = 500;
            var innerDelayMs     = 2_000;
            await Assert.ThrowsAnyAsync <TimeoutException>(async() =>
            {
                await FusionCacheExecutionUtils.RunAsyncActionWithTimeoutAsync(async ct => { await Task.Delay(innerDelayMs); ct.ThrowIfCancellationRequested(); factoryCompleted = true; }, TimeSpan.FromMilliseconds(timeoutMs), true);
            });

            await Task.Delay(innerDelayMs);

            Assert.False(factoryCompleted);
        }
        public void CancelingSyncFuncActuallyCancelsIt()
        {
            int res = -1;
            var factoryTerminated  = false;
            var outerCancelDelayMs = 500;
            var innerDelayMs       = 2_000;

            Assert.Throws <OperationCanceledException>(() =>
            {
                var cts = new CancellationTokenSource(outerCancelDelayMs);
                res     = FusionCacheExecutionUtils.RunSyncFuncWithTimeout(ct => { Thread.Sleep(innerDelayMs); ct.ThrowIfCancellationRequested(); factoryTerminated = true; return(42); }, Timeout.InfiniteTimeSpan, true, token: cts.Token);
            });

            Assert.Equal(-1, res);
            Assert.False(factoryTerminated);
        }
        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);
        }
        public void TimeoutEffectivelyWorks()
        {
            int res          = -1;
            var timeoutMs    = 500;
            var innerDelayMs = 2_000;
            var sw           = Stopwatch.StartNew();

            Assert.ThrowsAny <TimeoutException>(() =>
            {
                res = FusionCacheExecutionUtils.RunAsyncFuncWithTimeout(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);
        }
        private IFusionCacheEntry?GetOrSetEntryInternal <TValue>(string operationId, string key, Func <CancellationToken, 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 = _reactor.AcquireLock(key, operationId, options.LockTimeout, _logger);

            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) = dca.TryGetEntry <TValue>(operationId, key, options, _memoryEntry is object, token);
                }

                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 = FusionCacheExecutionUtils.RunSyncFuncWithTimeout(ct => factory(ct), timeout, options.AllowTimedOutFactoryBackgroundCompletion == false, x => factoryTask = x, token);
                        }
                        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)
                        dca.SetEntry <TValue>(operationId, key, _entry, options);
                    }
                }

                // 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);
        }