diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs index f76ff0439f..43a5ec04aa 100644 --- a/src/Umbraco.Core/Cache/CacheKeys.cs +++ b/src/Umbraco.Core/Cache/CacheKeys.cs @@ -29,6 +29,8 @@ namespace Umbraco.Core.Cache public const string MemberBusinessLogicCacheKey = "MemberCacheItem_"; public const string TemplateFrontEndCacheKey = "template"; + + [Obsolete("This is no longer used and will be removed from the codebase in the future")] public const string TemplateBusinessLogicCacheKey = "UmbracoTemplateCache"; public const string UserContextCacheKey = "UmbracoUserContext"; diff --git a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs index 542d85d42d..295ff8b408 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs @@ -18,8 +18,20 @@ namespace Umbraco.Core.Persistence.Repositories public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { + _options = new RepositoryCacheOptions + { + //We want to ensure that a zero count gets cached, even if there is nothing in the db we don't want it to lookup nothing each time + GetAllCacheAllowZeroCount = true, + //Set to 1000 just to ensure that all of them are cached, The GetAll on this repository gets called *A lot*, we want max performance + GetAllCacheThresholdLimit = 1000, + //Override to false so that a Count check against the db is NOT performed when doing a GetAll without params, we just want to + // return the raw cache without validation. The GetAll on this repository gets called *A lot*, we want max performance + GetAllCacheValidateCount = false + }; } + private readonly RepositoryCacheOptions _options; + protected override PublicAccessEntry PerformGet(Guid id) { var sql = GetBaseQuery(false); @@ -91,27 +103,13 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// The threshold entity count for which the GetAll method will cache entities + /// Returns the repository cache options /// - /// - /// Set to 1000 just to ensure that all of them are cached, The GetAll on this repository gets called *A lot*, we want max performance - /// - protected override int GetAllThresholdCacheLimit + protected override RepositoryCacheOptions RepositoryCacheOptions { - get { return 1000; } + get { return _options; } } - /// - /// Override to false so that a Count check against the db is NOT performed when doing a GetAll without params, we just want to - /// return the raw cache without validation. - /// - /// - /// The GetAll on this repository gets called *A lot*, we want max performance - /// - protected override bool GetAllValidateCount - { - get { return false; } - } protected override void PersistNewItem(PublicAccessEntry entity) { diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 880756bf6c..0d3c7db7a7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -80,6 +80,8 @@ namespace Umbraco.Core.Persistence.Repositories { } + private readonly RepositoryCacheOptions _cacheOptions = new RepositoryCacheOptions(); + #region IRepository Members /// @@ -158,18 +160,20 @@ namespace Umbraco.Core.Persistence.Repositories if (ids.Any()) { var entities = ids.Select(x => RuntimeCache.GetCacheItem(GetCacheIdKey(x))).ToArray(); - + if (ids.Count().Equals(entities.Count()) && entities.Any(x => x == null) == false) return entities; } else { - var allEntities = RuntimeCache.GetCacheItemsByKeySearch(GetCacheTypeKey()).ToArray(); + var allEntities = RuntimeCache.GetCacheItemsByKeySearch(GetCacheTypeKey()) + .WhereNotNull() + .ToArray(); if (allEntities.Any()) { - if (GetAllValidateCount) + if (RepositoryCacheOptions.GetAllCacheValidateCount) { //Get count of all entities of current type (TEntity) to ensure cached result is correct var query = Query.Builder.Where(x => x.Id != 0); @@ -183,6 +187,17 @@ namespace Umbraco.Core.Persistence.Repositories return allEntities; } } + else if (RepositoryCacheOptions.GetAllCacheAllowZeroCount) + { + //if the repository allows caching a zero count, then check the zero count cache + var zeroCount = RuntimeCache.GetCacheItem(GetCacheTypeKey()); + if (zeroCount != null && zeroCount.Any() == false) + { + //there is a zero count cache so return an empty list + return Enumerable.Empty(); + } + } + } var entityCollection = PerformGetAll(ids) @@ -194,7 +209,15 @@ namespace Umbraco.Core.Persistence.Repositories // coming back here we don't want to chuck it all into memory, this added cache here // is more for convenience when paging stuff temporarily - if (entityCollection.Length > GetAllThresholdCacheLimit) return entityCollection; + if (entityCollection.Length > RepositoryCacheOptions.GetAllCacheThresholdLimit) + return entityCollection; + + if (entityCollection.Length == 0 && RepositoryCacheOptions.GetAllCacheAllowZeroCount) + { + //there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache + // to signify that there is a zero count cache + RuntimeCache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {}); + } foreach (var entity in entityCollection) { @@ -209,25 +232,11 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// True/false as to validate the total item count when all items are returned from cache, the default is true but this - /// means that a db lookup will occur - though that lookup will probably be significantly less expensive than the normal - /// GetAll method. + /// Returns the repository cache options /// - /// - /// Overriding this to return false will improve performance of GetAll cache with no params but should only be used - /// for specific circumstances - /// - protected virtual bool GetAllValidateCount + protected virtual RepositoryCacheOptions RepositoryCacheOptions { - get { return true; } - } - - /// - /// The threshold entity count for which the GetAll method will cache entities - /// - protected virtual int GetAllThresholdCacheLimit - { - get { return 100; } + get { return _cacheOptions; } } protected abstract IEnumerable PerformGetByQuery(IQuery query); @@ -284,12 +293,16 @@ namespace Umbraco.Core.Persistence.Repositories { PersistNewItem((TEntity)entity); RuntimeCache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity); + //If there's a GetAll zero count cache, ensure it is cleared + RuntimeCache.ClearCacheItem(GetCacheTypeKey()); } catch (Exception) { //if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way // that we cache entities: http://issues.umbraco.org/issue/U4-4259 RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.Id)); + //If there's a GetAll zero count cache, ensure it is cleared + RuntimeCache.ClearCacheItem(GetCacheTypeKey()); throw; } @@ -305,13 +318,16 @@ namespace Umbraco.Core.Persistence.Repositories { PersistUpdatedItem((TEntity)entity); RuntimeCache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity); + //If there's a GetAll zero count cache, ensure it is cleared + RuntimeCache.ClearCacheItem(GetCacheTypeKey()); } catch (Exception) { //if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way // that we cache entities: http://issues.umbraco.org/issue/U4-4259 - //RepositoryCache.Delete(typeof(TEntity), entity); RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.Id)); + //If there's a GetAll zero count cache, ensure it is cleared + RuntimeCache.ClearCacheItem(GetCacheTypeKey()); throw; } @@ -325,6 +341,8 @@ namespace Umbraco.Core.Persistence.Repositories { PersistDeletedItem((TEntity)entity); RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.Id)); + //If there's a GetAll zero count cache, ensure it is cleared + RuntimeCache.ClearCacheItem(GetCacheTypeKey()); } #endregion diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheOptions.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheOptions.cs new file mode 100644 index 0000000000..9ac8aa6abd --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryCacheOptions.cs @@ -0,0 +1,36 @@ +namespace Umbraco.Core.Persistence.Repositories +{ + internal class RepositoryCacheOptions + { + /// + /// Constructor sets defaults + /// + public RepositoryCacheOptions() + { + GetAllCacheValidateCount = true; + GetAllCacheAllowZeroCount = false; + GetAllCacheThresholdLimit = 100; + } + + /// + /// True/false as to validate the total item count when all items are returned from cache, the default is true but this + /// means that a db lookup will occur - though that lookup will probably be significantly less expensive than the normal + /// GetAll method. + /// + /// + /// setting this to return false will improve performance of GetAll cache with no params but should only be used + /// for specific circumstances + /// + public bool GetAllCacheValidateCount { get; set; } + + /// + /// True if the GetAll method will cache that there are zero results so that the db is not hit when there are no results found + /// + public bool GetAllCacheAllowZeroCount { get; set; } + + /// + /// The threshold entity count for which the GetAll method will cache entities + /// + public int GetAllCacheThresholdLimit { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index d6f2a0f80e..be6d35ca27 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -80,7 +80,9 @@ namespace Umbraco.Core.Persistence public virtual IAuditRepository CreateAuditRepository(IDatabaseUnitOfWork uow) { - return new AuditRepository(uow, _cacheHelper, _logger, _sqlSyntax); + return new AuditRepository(uow, + CacheHelper.CreateDisabledCacheHelper(), //never cache + _logger, _sqlSyntax); } public virtual ITagRepository CreateTagRepository(IDatabaseUnitOfWork uow) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 02c14d375c..3419ba2517 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -346,6 +346,7 @@ + diff --git a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs b/src/Umbraco.Web/Cache/DomainCacheRefresher.cs index 11f8291e6c..aabe95548f 100644 --- a/src/Umbraco.Web/Cache/DomainCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DomainCacheRefresher.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Web.PublishedCache; using Umbraco.Web.PublishedCache.XmlPublishedCache; @@ -40,6 +41,8 @@ namespace Umbraco.Web.Cache private void ClearCache() { + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheObjectTypes(); + // SD: we need to clear the routes cache here! // // zpqrtbnk: no, not here, in fact the caches should subsribe to refresh events else we diff --git a/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs b/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs index 5ef1fbf468..6270abe7e6 100644 --- a/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs @@ -55,9 +55,6 @@ namespace Umbraco.Web.Cache ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem( string.Format("{0}{1}", CacheKeys.TemplateFrontEndCacheKey, id)); - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem( - string.Format("{0}{1}", CacheKeys.TemplateBusinessLogicCacheKey, id)); - //need to clear the runtime cache for template instances //NOTE: This is temp until we implement the correct ApplicationCache and then we can remove the RuntimeCache, etc... ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheObjectTypes(); diff --git a/src/Umbraco.Web/umbraco.presentation/template.cs b/src/Umbraco.Web/umbraco.presentation/template.cs index 40a786325a..cf1186867c 100644 --- a/src/Umbraco.Web/umbraco.presentation/template.cs +++ b/src/Umbraco.Web/umbraco.presentation/template.cs @@ -24,7 +24,7 @@ namespace umbraco /// /// Holds methods for parsing and building umbraco templates /// - /// + [Obsolete("Do not use this class, use Umbraco.Core.Service.IFileService to work with templates")] public class template { #region private variables