From 4a6844b94920367b442227bc9acf00d623d1abea Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 31 Jan 2017 19:36:55 +0100 Subject: [PATCH] U4-9322 - repository caches, cleanup --- .../Cache/ScopedRepositoryCachePolicy.cs | 16 --- src/Umbraco.Core/Scoping/IScope.cs | 28 ---- src/Umbraco.Core/Scoping/NoScope.cs | 18 --- src/Umbraco.Core/Scoping/Scope.cs | 131 +----------------- src/Umbraco.Tests/Scoping/ScopeTests.cs | 22 +-- .../Scoping/ScopedRepositoryTests.cs | 49 +++++++ 6 files changed, 51 insertions(+), 213 deletions(-) diff --git a/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs index 3273b5c7ac..f4244a49a1 100644 --- a/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs @@ -24,18 +24,6 @@ namespace Umbraco.Core.Cache throw new InvalidOperationException(); // obviously } - // when the scope completes we need to clear the global isolated cache - // for now, we are not doing it selectively at all - just kill everything - // later on we might want to be more clever - private void RegisterDirty() - { - // use unique names to de-duplicate - // enlisting multiple times is not a problem - // fixme - when should we do it? BeforeEvents or BeforeDispose? + aren't the events going to do it anyways? - _scope.Enlist("dirty_" + typeof (TEntity).Name, ActionTime.BeforeDispose, - (actionTime, completed) => { if (completed) _globalIsolatedCache.ClearAllCache(); }); - } - public TEntity Get(TId id, Func performGet, Func> performGetAll) { // loads into the local cache only, ok for now @@ -58,21 +46,18 @@ namespace Umbraco.Core.Cache { // writes into the local cache _cachePolicy.Create(entity, persistNew); - RegisterDirty(); } public void Update(TEntity entity, Action persistUpdated) { // writes into the local cache _cachePolicy.Update(entity, persistUpdated); - RegisterDirty(); } public void Delete(TEntity entity, Action persistDeleted) { // deletes the local cache _cachePolicy.Delete(entity, persistDeleted); - RegisterDirty(); } public TEntity[] GetAll(TId[] ids, Func> performGetAll) @@ -85,7 +70,6 @@ namespace Umbraco.Core.Cache { // clears the local cache _cachePolicy.ClearAll(); - RegisterDirty(); } } } diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index afa296785b..9fca60fb95 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -41,34 +41,6 @@ namespace Umbraco.Core.Scoping /// void Complete(); - /// - /// Enlists an object into the scope. - /// - /// The type of the object. - /// The unique key. - /// A method creating the object. - /// The object. - T Enlist(string key, Func creator); - - /// - /// Enlists an action into the scope. - /// - /// The unique key. - /// When to execute the action. - /// The action to execute. - void Enlist(string key, ActionTime actionTimes, Action action); - - /// - /// Enlists an object and an action into the scope. - /// - /// The type of the object. - /// The unique key. - /// A method creating the object. - /// When to execute the action. - /// The action to execute. - /// The object. - T Enlist(string key, Func creator, ActionTime actionTimes, Action action); - #if DEBUG_SCOPES Guid InstanceId { get; } #endif diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index 756a2dfdc0..e99ded6b3b 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -79,24 +79,6 @@ namespace Umbraco.Core.Scoping throw new NotSupportedException(); } - /// - public T Enlist(string key, Func creator) - { - throw new NotSupportedException(); - } - - /// - public T Enlist(string key, Func creator, ActionTime actionTimes, Action action) - { - throw new NotSupportedException(); - } - - /// - public void Enlist(string key, ActionTime actionTimes, Action action) - { - throw new NotSupportedException(); - } - private void EnsureNotDisposed() { if (_disposed) diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 9d4d794302..eb63ca5a44 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -23,8 +23,6 @@ namespace Umbraco.Core.Scoping private IsolatedRuntimeCache _isolatedRuntimeCache; private UmbracoDatabase _database; - private EventMessages _messages; - private IDictionary _enlisted; private IEventDispatcher _eventDispatcher; // this is v7, in v8 this has to change to RepeatableRead @@ -215,7 +213,7 @@ namespace Umbraco.Core.Scoping get { EnsureNotDisposed(); - return ParentScope == null ? _messages : ParentScope.MessagesOrNull; + return ParentScope == null ? null : ParentScope.MessagesOrNull; } } @@ -280,22 +278,8 @@ namespace Umbraco.Core.Scoping private void DisposeLastScope() { - // note - messages - // at the moment we are totally not filtering the messages based on completion - // status, so whether the scope is committed or rolled back makes no difference - - // note - scope - // at that point, there is *no* ambient scope anymore, which means that every - // enlisted action, triggered event, *anything*, is not scoped and is free to - // do whatever needed. - // - // fixme - what does this mean for XML scope?! - var completed = _completed.HasValue && _completed.Value; - // run enlisted actions - RunEnlisted(ActionTime.BeforeCommit, completed); - if (_database != null) { try @@ -312,16 +296,10 @@ namespace Umbraco.Core.Scoping } } - // run enlisted actions - RunEnlisted(ActionTime.BeforeEvents, completed); - // deal with events if (_eventDispatcher != null) _eventDispatcher.ScopeExit(completed); - // run enlisted actions - RunEnlisted(ActionTime.BeforeDispose, completed); - // if *we* created it, then get rid of it if (_scopeProvider.AmbientContext == _scopeContext) { @@ -335,112 +313,5 @@ namespace Umbraco.Core.Scoping } } } - - private void RunEnlisted(ActionTime actionTime, bool completed) - { - List exceptions = null; - foreach (var enlisted in Enlisted.Values) - { - try - { - enlisted.Execute(actionTime, completed); - } - catch (Exception e) - { - if (exceptions == null) - exceptions = new List(); - exceptions.Add(e); - } - } - if (exceptions != null) - throw new AggregateException("Exceptions were thrown by listed actions at ActionTime " + actionTime + ".", exceptions); - } - - private IDictionary Enlisted - { - get - { - if (ParentScope != null) return ParentScope.Enlisted; - - return _enlisted ?? (_enlisted - = new Dictionary()); - } - } - - private interface IEnlistedObject - { - void Execute(ActionTime actionTime, bool completed); - } - - private class EnlistedObject : IEnlistedObject - { - private readonly ActionTime _actionTimes; - private readonly Action _action; - - public EnlistedObject(T item) - { - Item = item; - _actionTimes = ActionTime.None; - } - - public EnlistedObject(T item, ActionTime actionTimes, Action action) - { - Item = item; - _actionTimes = actionTimes; - _action = action; - } - - public T Item { get; private set; } - - public void Execute(ActionTime actionTime, bool completed) - { - if (_actionTimes.HasFlag(actionTime)) - _action(actionTime, completed, Item); - } - } - - /// - public T Enlist(string key, Func creator) - { - IEnlistedObject enlisted; - if (Enlisted.TryGetValue(key, out enlisted)) - { - var enlistedAs = enlisted as EnlistedObject; - if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key."); - return enlistedAs.Item; - } - var enlistedOfT = new EnlistedObject(creator()); - Enlisted[key] = enlistedOfT; - return enlistedOfT.Item; - } - - /// - public T Enlist(string key, Func creator, ActionTime actionTimes, Action action) - { - IEnlistedObject enlisted; - if (Enlisted.TryGetValue(key, out enlisted)) - { - var enlistedAs = enlisted as EnlistedObject; - if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key."); - return enlistedAs.Item; - } - var enlistedOfT = new EnlistedObject(creator(), actionTimes, action); - Enlisted[key] = enlistedOfT; - return enlistedOfT.Item; - } - - /// - public void Enlist(string key, ActionTime actionTimes, Action action) - { - IEnlistedObject enlisted; - if (Enlisted.TryGetValue(key, out enlisted)) - { - var enlistedAs = enlisted as EnlistedObject; - if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key."); - return; - } - var enlistedOfT = new EnlistedObject(null, actionTimes, (actionTime, completed, item) => action(actionTime, completed)); - Enlisted[key] = enlistedOfT; - } } } diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index 61e68672a1..fc9dedf264 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -141,7 +141,7 @@ namespace Umbraco.Tests.Scoping { using (var scope = scopeProvider.CreateScope()) { - scope.Enlist("test", ActionTime.BeforeDispose, (actionTime, completed) => scopeCompleted = completed); + scopeProvider.Context.Enlist("test", completed => scopeCompleted = completed); Assert.IsInstanceOf(scope); Assert.IsNotNull(scopeProvider.AmbientScope); @@ -511,26 +511,6 @@ namespace Umbraco.Tests.Scoping }); } - [TestCase(true)] - [TestCase(false)] - public void ScopeEnlist(bool complete) - { - var scopeProvider = DatabaseContext.ScopeProvider; - - bool? completed = null; - - Assert.IsNull(scopeProvider.AmbientScope); - using (var scope = scopeProvider.CreateScope()) - { - scope.Enlist("name", ActionTime.BeforeDispose, (a, c) => { completed = c; }); - if (complete) - scope.Complete(); - } - Assert.IsNull(scopeProvider.AmbientScope); - Assert.IsNotNull(completed); - Assert.AreEqual(complete, completed.Value); - } - [TestCase(true)] [TestCase(false)] public void ScopeContextEnlist(bool complete) diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index e6989d1403..5d5c2acdf2 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -1,12 +1,17 @@ using System.Collections.Generic; using System.Linq; +using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Scoping; +using Umbraco.Core.Sync; +using Umbraco.Tests.Cache.DistributedCache; using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Cache; namespace Umbraco.Tests.Scoping { @@ -14,6 +19,8 @@ namespace Umbraco.Tests.Scoping [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] public class ScopedRepositoryTests : BaseDatabaseFactoryTest { + private CacheRefresherEventHandler _cacheHandler; + // setup public override void Initialize() { @@ -22,6 +29,39 @@ namespace Umbraco.Tests.Scoping Assert.IsNull(DatabaseContext.ScopeProvider.AmbientScope); // gone } + protected override void FreezeResolution() + { + ServerRegistrarResolver.Current = new ServerRegistrarResolver( + new DistributedCacheTests.TestServerRegistrar()); + ServerMessengerResolver.Current = new ServerMessengerResolver( + new DatabaseServerMessenger(ApplicationContext, false, new DatabaseServerMessengerOptions())); + CacheRefreshersResolver.Current = new CacheRefreshersResolver( + new ActivatorServiceProvider(), Mock.Of(), () => new[] + { + typeof(PageCacheRefresher), + typeof(UnpublishedPageCacheRefresher), + typeof(DomainCacheRefresher), + typeof(MacroCacheRefresher), + typeof(UserCacheRefresher), + typeof(LanguageCacheRefresher), + typeof(DictionaryCacheRefresher) + }); + + base.FreezeResolution(); + } + + [TearDown] + public void Teardown() + { + if (_cacheHandler != null) + _cacheHandler.Destroy(); + _cacheHandler = null; + + ServerRegistrarResolver.Reset(); + ServerMessengerResolver.Reset(); + CacheRefreshersResolver.Reset(); + } + protected override CacheHelper CreateCacheHelper() { //return CacheHelper.CreateDisabledCacheHelper(); @@ -50,6 +90,9 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(user.Id, globalCached.Id); Assert.AreEqual("name", globalCached.Name); + _cacheHandler = new CacheRefresherEventHandler(); + _cacheHandler.OnApplicationStarted(null, ApplicationContext); + Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) { @@ -128,6 +171,9 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(lang.Id, globalCached.Id); Assert.AreEqual("fr-FR", globalCached.IsoCode); + _cacheHandler = new CacheRefresherEventHandler(); + _cacheHandler.OnApplicationStarted(null, ApplicationContext); + Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) { @@ -217,6 +263,9 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(item.Id, globalCached.Id); Assert.AreEqual("item-key", globalCached.ItemKey); + _cacheHandler = new CacheRefresherEventHandler(); + _cacheHandler.OnApplicationStarted(null, ApplicationContext); + Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) {