private void RegisterCacheBackplane(ICacheBackplane <TKey, TValue> backplane) { // this should have been checked during activation already, just to be totally sure... if (_cacheHandles.Any(p => p.Configuration.IsBackplaneSource)) { // added includeSource param to get the handles which need to be synced. // in case the backplane source is non-distributed (in-memory), only remotly triggered remove and clear should also // trigger a sync locally. For distribtued caches, we expect that the distributed cache is already the source and in sync // as that's the layer which triggered the event. In this case, only other in-memory handles above the distribtued, would be synced. var handles = new Func <bool, IBaseCacheHandle <TKey, TValue>[]>((includSource) => { var handleList = new List <IBaseCacheHandle <TKey, TValue> >(); foreach (var handle in _cacheHandles) { if (!handle.Configuration.IsBackplaneSource || (includSource && handle.Configuration.IsBackplaneSource && !handle.IsDistributedCache)) { handleList.Add(handle); } } return(handleList.ToArray()); }); backplane.Changed += (sender, args) => { if (Logger.IsEnabled(LogLevel.Debug)) { Logger.LogDebug($"Backplane event: [Changed] for '{args.Key}'."); } EvictFromHandles(args.Key, handles(false)); switch (args.Action) { case CacheItemChangedEventAction.Add: TriggerOnAdd(args.Key, CacheActionEventArgOrigin.Remote); break; case CacheItemChangedEventAction.Put: TriggerOnPut(args.Key, CacheActionEventArgOrigin.Remote); break; case CacheItemChangedEventAction.Update: TriggerOnUpdate(args.Key, CacheActionEventArgOrigin.Remote); break; } }; backplane.Removed += (sender, args) => { if (_logTrace) { Logger.LogTrace($"Backplane event: [Remove] of {args.Key}."); } EvictFromHandles(args.Key, handles(true)); TriggerOnRemove(args.Key, CacheActionEventArgOrigin.Remote); }; backplane.Cleared += (sender, args) => { if (_logTrace) { Logger.LogTrace("Backplane event: [Clear]."); } ClearHandles(handles(true)); TriggerOnClear(CacheActionEventArgOrigin.Remote); }; } }
/// <summary> /// Initializes a new instance of the <see cref="BaseCacheManager{TCacheValue}"/> class /// using the specified <paramref name="configuration"/>. /// If the name of the <paramref name="configuration"/> is defined, the cache manager will /// use it. Otherwise a random string will be generated. /// </summary> /// <param name="configuration"> /// The configuration which defines the structure and complexity of the cache manager. /// </param> /// <exception cref="System.ArgumentNullException"> /// When <paramref name="configuration"/> is null. /// </exception> /// <see cref="CacheFactory"/> /// <see cref="ConfigurationBuilder"/> /// <see cref="BaseCacheHandle{TCacheValue}"/> public BaseCacheManager(IServiceProvider sp, ILogger <BaseCacheManager <TKey, TValue> > logger, IOptions <CacheManagerConfiguration <TKey, TValue> > configuration, IEnumerable <IBaseCacheHandle <TKey, TValue> > handles) { CacheManagerConfiguration <TKey, TValue> config = configuration.Value; NotNullOrWhiteSpace(config.Name, nameof(config.Name)); Configuration = configuration.Value; //var serializer = CacheReflectionHelper.CreateSerializer(configuration, loggerFactory); Logger = logger; _logTrace = Logger.IsEnabled(LogLevel.Trace); Logger.LogInformation("Cache manager: adding cache handles..."); try { _cacheHandles = handles.ToArray();//CacheReflectionHelper.CreateCacheHandles(this, loggerFactory, serializer).ToArray(); var index = 0; foreach (var handle in _cacheHandles) { var handleIndex = index; handle.OnCacheSpecificRemove += (sender, args) => { // added sync for using backplane with in-memory caches on cache specific removal // but commented for now, this is not really needed if all instances use the same expiration etc, would just cause dublicated events ////if (_cacheBackplane != null && handle.Configuration.IsBackplaneSource && !handle.IsDistributedCache) ////{ //// if (string.IsNullOrEmpty(args.Region)) //// { //// _cacheBackplane.NotifyRemove(args.Key); //// } //// else //// { //// _cacheBackplane.NotifyRemove(args.Key, args.Region); //// } ////} // base cache handle does logging for this if (Configuration.UpdateMode == CacheUpdateMode.Up) { if (_logTrace) { Logger.LogTrace($"Cleaning handles above '{handleIndex}' because of remove event."); } EvictFromHandlesAbove(args.Key, handleIndex); } // moving down below cleanup, optherwise the item could still be in memory TriggerOnRemoveByHandle(args.Key, args.Reason, handleIndex + 1, args.Value); }; index++; } _cacheBackplane = sp.GetService <ICacheBackplane <TKey, TValue> >(); if (_cacheBackplane != null) { RegisterCacheBackplane(_cacheBackplane); } } catch (Exception ex) { Logger.LogError(ex, "Error occurred while creating the cache manager."); throw; } }