コード例 #1
0
        /// <inheritdoc/>
        public void Set <TValue>(string key, TValue value, FusionCacheEntryOptions?options = null, CancellationToken token = default)
        {
            ValidateCacheKey(key);

            token.ThrowIfCancellationRequested();

            MaybeProcessCacheKey(ref key);

            if (options is null)
            {
                options = _options.DefaultEntryOptions;
            }

            var operationId = GenerateOperationId();

            if (_logger?.IsEnabled(LogLevel.Debug) ?? false)
            {
                _logger.LogDebug("FUSION (K={CacheKey} OP={CacheOperationId}): calling Set<T> {Options}", key, operationId, options.ToLogString());
            }

            var entry = FusionCacheMemoryEntry.CreateFromOptions(value, options, false);

            _mca.SetEntry <TValue>(operationId, key, entry, options);

            var dca = GetCurrentDistributedAccessor();

            if ((dca?.IsCurrentlyUsable() ?? false) == false)
            {
                return;
            }

            dca.SetEntry <TValue>(operationId, key, entry, options, token);
        }
コード例 #2
0
        private void MaybeBackgroundCompleteTimedOutFactory <TValue>(string operationId, string key, Task <TValue>?factoryTask, FusionCacheEntryOptions options, DistributedCacheAccessor?dca, CancellationToken token)
        {
            if (options.AllowTimedOutFactoryBackgroundCompletion == false || factoryTask is null)
            {
                return;
            }

            if (factoryTask.IsFaulted)
            {
                if (_logger?.IsEnabled(_options.FactoryErrorsLogLevel) ?? false)
                {
                    _logger.Log(_options.FactoryErrorsLogLevel, factoryTask.Exception.GetSingleInnerExceptionOrSelf(), "FUSION (K={CacheKey} OP={CacheOperationId}): a timed-out factory thrown an exception", key, operationId);
                }
                return;
            }

            // CONTINUE IN THE BACKGROUND TO TRY TO KEEP THE RESULT AS SOON AS IT WILL COMPLETE SUCCESSFULLY
            if (_logger?.IsEnabled(LogLevel.Debug) ?? false)
            {
                _logger.LogDebug("FUSION (K={CacheKey} OP={CacheOperationId}): trying to complete the timed-out factory in the background", key, operationId);
            }

            _ = factoryTask.ContinueWith(antecedent =>
            {
                if (antecedent.Status == TaskStatus.Faulted)
                {
                    if (_logger?.IsEnabled(_options.FactoryErrorsLogLevel) ?? false)
                    {
                        _logger.Log(_options.FactoryErrorsLogLevel, antecedent.Exception.GetSingleInnerExceptionOrSelf(), "FUSION (K={CacheKey} OP={CacheOperationId}): a timed-out factory thrown an exception", key, operationId);
                    }
                }
                else if (antecedent.Status == TaskStatus.RanToCompletion)
                {
                    if (_logger?.IsEnabled(LogLevel.Debug) ?? false)
                    {
                        _logger.LogDebug("FUSION (K={CacheKey} OP={CacheOperationId}): a timed-out factory successfully completed in the background: keeping the result", key, operationId);
                    }

                    var lateEntry = FusionCacheMemoryEntry.CreateFromOptions(antecedent.Result, options, false);
                    _             = dca?.SetEntryAsync <TValue>(operationId, key, lateEntry, options, token);
                    _mca.SetEntry <TValue>(operationId, key, lateEntry, options);
                }
            });
        }
コード例 #3
0
        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);
        }