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); }
private IFusionCacheEntry?MaybeGetFallbackEntry <TValue>(string operationId, string key, FusionCacheDistributedEntry <TValue>?distributedEntry, FusionCacheMemoryEntry?memoryEntry, FusionCacheEntryOptions options, out bool failSafeActivated) { failSafeActivated = false; if (options.IsFailSafeEnabled) { if (_logger?.IsEnabled(LogLevel.Trace) ?? false) { _logger.LogTrace("FUSION (K={CacheKey} OP={CacheOperationId}): trying to activate FAIL-SAFE", key, operationId); } if (distributedEntry is object) { // FAIL SAFE (FROM DISTRIBUTED) if (_logger?.IsEnabled(_options.FailSafeActivationLogLevel) ?? false) { _logger.Log(_options.FailSafeActivationLogLevel, "FUSION (K={CacheKey} OP={CacheOperationId}): FAIL-SAFE activated (from distributed)", key, operationId); } failSafeActivated = true; return(distributedEntry); } else if (memoryEntry is object) { // FAIL SAFE (FROM MEMORY) if (_logger?.IsEnabled(_options.FailSafeActivationLogLevel) ?? false) { _logger.Log(_options.FailSafeActivationLogLevel, "FUSION (K={CacheKey} OP={CacheOperationId}): FAIL-SAFE activated (from memory)", key, operationId); } failSafeActivated = true; return(memoryEntry); } else { if (_logger?.IsEnabled(_options.FailSafeActivationLogLevel) ?? false) { _logger.Log(_options.FailSafeActivationLogLevel, "FUSION (K={CacheKey} OP={CacheOperationId}): unable to activate FAIL-SAFE (no entries in memory or distributed)", key, operationId); } return(null); } } else { if (_logger?.IsEnabled(LogLevel.Trace) ?? false) { _logger.LogTrace("FUSION (K={CacheKey} OP={CacheOperationId}): FAIL-SAFE not enabled", key, operationId); } return(null); } }