Backport cache key fix and optimizations (#10199)
* Add GetKey<T, TId> * Update Usages of GetKey and remove GetKey<T>(object) We shouldn't have to retain this since RepositoryCacheKeys is internal. * Apply changes to DefaultRepositoryCachePolicy * Add check for default/less than -1 on UserRepository PerformGet Co-authored-by: Nikolaj <nel@umbraco.dk>
This commit is contained in:
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Cache
|
||||
internal class DefaultRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
|
||||
private static readonly TEntity[] s_emptyEntities = new TEntity[0]; // const
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
@@ -29,16 +29,24 @@ namespace Umbraco.Core.Cache
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
protected string GetEntityCacheKey(object id)
|
||||
protected string GetEntityCacheKey(int id) => EntityTypeCacheKey + id;
|
||||
|
||||
protected string GetEntityCacheKey(TId id)
|
||||
{
|
||||
if (id == null) throw new ArgumentNullException(nameof(id));
|
||||
return GetEntityTypeCacheKey() + id;
|
||||
if (EqualityComparer<TId>.Default.Equals(id, default))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (typeof(TId).IsValueType)
|
||||
{
|
||||
return EntityTypeCacheKey + id;
|
||||
}
|
||||
|
||||
return EntityTypeCacheKey + id.ToString().ToUpperInvariant();
|
||||
}
|
||||
|
||||
protected string GetEntityTypeCacheKey()
|
||||
{
|
||||
return $"uRepo_{typeof (TEntity).Name}_";
|
||||
}
|
||||
protected string EntityTypeCacheKey { get; } = $"uRepo_{typeof(TEntity).Name}_";
|
||||
|
||||
protected virtual void InsertEntity(string cacheKey, TEntity entity)
|
||||
{
|
||||
@@ -52,7 +60,7 @@ namespace Umbraco.Core.Cache
|
||||
// getting all of them, and finding nothing.
|
||||
// if we can cache a zero count, cache an empty array,
|
||||
// for as long as the cache is not cleared (no expiration)
|
||||
Cache.Insert(GetEntityTypeCacheKey(), () => EmptyEntities);
|
||||
Cache.Insert(EntityTypeCacheKey, () => s_emptyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -81,7 +89,7 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
Cache.Clear(EntityTypeCacheKey);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -91,7 +99,7 @@ namespace Umbraco.Core.Cache
|
||||
Cache.Clear(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
Cache.Clear(EntityTypeCacheKey);
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -113,7 +121,7 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
Cache.Clear(EntityTypeCacheKey);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -123,7 +131,7 @@ namespace Umbraco.Core.Cache
|
||||
Cache.Clear(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
Cache.Clear(EntityTypeCacheKey);
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -144,7 +152,7 @@ namespace Umbraco.Core.Cache
|
||||
var cacheKey = GetEntityCacheKey(entity.Id);
|
||||
Cache.Clear(cacheKey);
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
Cache.Clear(EntityTypeCacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +203,7 @@ namespace Umbraco.Core.Cache
|
||||
else
|
||||
{
|
||||
// get everything we have
|
||||
var entities = Cache.GetCacheItemsByKeySearch<TEntity>(GetEntityTypeCacheKey())
|
||||
var entities = Cache.GetCacheItemsByKeySearch<TEntity>(EntityTypeCacheKey)
|
||||
.ToArray(); // no need for null checks, we are not caching nulls
|
||||
|
||||
if (entities.Length > 0)
|
||||
@@ -218,7 +226,7 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
// if none of them were in the cache
|
||||
// and we allow zero count - check for the special (empty) entry
|
||||
var empty = Cache.GetCacheItem<TEntity[]>(GetEntityTypeCacheKey());
|
||||
var empty = Cache.GetCacheItem<TEntity[]>(EntityTypeCacheKey);
|
||||
if (empty != null) return empty;
|
||||
}
|
||||
}
|
||||
@@ -238,7 +246,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
Cache.ClearByKey(GetEntityTypeCacheKey());
|
||||
Cache.ClearByKey(EntityTypeCacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
Database.Update(dto);
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IConsent>(entity.Id));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IConsent, int>(entity.Id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
foreach (var translation in dictionaryItem.Translations)
|
||||
translation.Value = translation.Value.ToValidXmlString();
|
||||
|
||||
|
||||
var dto = DictionaryItemFactory.BuildDto(dictionaryItem);
|
||||
|
||||
var id = Convert.ToInt32(Database.Insert(dto));
|
||||
@@ -152,7 +152,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
foreach (var translation in entity.Translations)
|
||||
translation.Value = translation.Value.ToValidXmlString();
|
||||
|
||||
|
||||
var dto = DictionaryItemFactory.BuildDto(entity);
|
||||
|
||||
Database.Update(dto);
|
||||
@@ -174,8 +174,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
//Clear the cache entries that exist by uniqueid/item key
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.ItemKey));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.Key));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem, string>(entity.ItemKey));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem, Guid>(entity.Key));
|
||||
}
|
||||
|
||||
protected override void PersistDeletedItem(IDictionaryItem entity)
|
||||
@@ -186,8 +186,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
Database.Delete<DictionaryDto>("WHERE id = @Id", new { Id = entity.Key });
|
||||
|
||||
//Clear the cache entries that exist by uniqueid/item key
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.ItemKey));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.Key));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem, string>(entity.ItemKey));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem, Guid>(entity.Key));
|
||||
|
||||
entity.DeleteDate = DateTime.Now;
|
||||
}
|
||||
@@ -203,8 +203,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
Database.Delete<DictionaryDto>("WHERE id = @Id", new { Id = dto.UniqueId });
|
||||
|
||||
//Clear the cache entries that exist by uniqueid/item key
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(dto.Key));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(dto.UniqueId));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem, string>(dto.Key));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem, Guid>(dto.UniqueId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1163,7 +1163,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
if (withCache)
|
||||
{
|
||||
// if the cache contains the (proper version of the) item, use it
|
||||
var cached = IsolatedCache.GetCacheItem<IContent>(RepositoryCacheKeys.GetKey<IContent>(dto.NodeId));
|
||||
var cached = IsolatedCache.GetCacheItem<IContent>(RepositoryCacheKeys.GetKey<IContent, int>(dto.NodeId));
|
||||
if (cached != null && cached.VersionId == dto.DocumentVersionDto.ContentVersionDto.Id)
|
||||
{
|
||||
content[i] = (Content)cached;
|
||||
|
||||
@@ -508,7 +508,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
if (withCache)
|
||||
{
|
||||
// if the cache contains the (proper version of the) item, use it
|
||||
var cached = IsolatedCache.GetCacheItem<IMedia>(RepositoryCacheKeys.GetKey<IMedia>(dto.NodeId));
|
||||
var cached = IsolatedCache.GetCacheItem<IMedia>(RepositoryCacheKeys.GetKey<IMedia, int>(dto.NodeId));
|
||||
if (cached != null && cached.VersionId == dto.ContentVersionDto.Id)
|
||||
{
|
||||
content[i] = (Models.Media)cached;
|
||||
|
||||
@@ -331,7 +331,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(IMember entity)
|
||||
{
|
||||
{
|
||||
// update
|
||||
entity.UpdatingEntity();
|
||||
|
||||
@@ -534,7 +534,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
var sqlSelectTemplateVersion = SqlContext.Templates.Get("Umbraco.Core.MemberRepository.SetLastLogin2", s => s
|
||||
.Select<ContentVersionDto>(x => x.Id)
|
||||
.From<ContentVersionDto>()
|
||||
.From<ContentVersionDto>()
|
||||
.InnerJoin<NodeDto>().On<NodeDto, ContentVersionDto>((l, r) => l.NodeId == r.NodeId)
|
||||
.InnerJoin<MemberDto>().On<MemberDto, NodeDto>((l, r) => l.NodeId == r.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == SqlTemplate.Arg<Guid>("nodeObjectType"))
|
||||
@@ -606,7 +606,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
if (withCache)
|
||||
{
|
||||
// if the cache contains the (proper version of the) item, use it
|
||||
var cached = IsolatedCache.GetCacheItem<IMember>(RepositoryCacheKeys.GetKey<IMember>(dto.NodeId));
|
||||
var cached = IsolatedCache.GetCacheItem<IMember>(RepositoryCacheKeys.GetKey<IMember, int>(dto.NodeId));
|
||||
if (cached != null && cached.VersionId == dto.ContentVersionDto.Id)
|
||||
{
|
||||
content[i] = (Member) cached;
|
||||
|
||||
@@ -8,14 +8,27 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
internal static class RepositoryCacheKeys
|
||||
{
|
||||
private static readonly Dictionary<Type, string> Keys = new Dictionary<Type, string>();
|
||||
private static readonly Dictionary<Type, string> s_keys = new Dictionary<Type, string>();
|
||||
|
||||
public static string GetKey<T>()
|
||||
{
|
||||
var type = typeof(T);
|
||||
return Keys.TryGetValue(type, out var key) ? key : (Keys[type] = "uRepo_" + type.Name + "_");
|
||||
return s_keys.TryGetValue(type, out var key) ? key : (s_keys[type] = "uRepo_" + type.Name + "_");
|
||||
}
|
||||
|
||||
public static string GetKey<T>(object id) => GetKey<T>() + id;
|
||||
public static string GetKey<T, TId>(TId id)
|
||||
{
|
||||
if (EqualityComparer<TId>.Default.Equals(id, default))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (typeof(TId).IsValueType)
|
||||
{
|
||||
return GetKey<T>() + id;
|
||||
}
|
||||
|
||||
return GetKey<T>() + id.ToString().ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
protected override IUser PerformGet(int id)
|
||||
{
|
||||
// This will never resolve to a user, yet this is asked
|
||||
// for all of the time (especially in cases of members).
|
||||
// Don't issue a SQL call for this, we know it will not exist.
|
||||
if (id == default || id < -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var sql = SqlContext.Sql()
|
||||
.Select<UserDto>()
|
||||
.From<UserDto>()
|
||||
@@ -168,7 +176,7 @@ ORDER BY colName";
|
||||
}
|
||||
|
||||
public Guid CreateLoginSession(int userId, string requestingIpAddress, bool cleanStaleSessions = true)
|
||||
{
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var dto = new UserLoginDto
|
||||
{
|
||||
|
||||
@@ -54,9 +54,9 @@ namespace Umbraco.Web.Cache
|
||||
foreach (var payload in payloads.Where(x => x.Id != default))
|
||||
{
|
||||
//By INT Id
|
||||
isolatedCache.Clear(RepositoryCacheKeys.GetKey<IContent>(payload.Id));
|
||||
isolatedCache.Clear(RepositoryCacheKeys.GetKey<IContent, int>(payload.Id));
|
||||
//By GUID Key
|
||||
isolatedCache.Clear(RepositoryCacheKeys.GetKey<IContent>(payload.Key));
|
||||
isolatedCache.Clear(RepositoryCacheKeys.GetKey<IContent, Guid?>(payload.Key));
|
||||
|
||||
_idkMap.ClearCache(payload.Id);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Umbraco.Web.Cache
|
||||
var macroRepoCache = AppCaches.IsolatedCaches.Get<IMacro>();
|
||||
if (macroRepoCache)
|
||||
{
|
||||
macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey<IMacro>(payload.Id));
|
||||
macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey<IMacro, int>(payload.Id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ namespace Umbraco.Web.Cache
|
||||
// repository cache
|
||||
// it *was* done for each pathId but really that does not make sense
|
||||
// only need to do it for the current media
|
||||
mediaCache.Result.Clear(RepositoryCacheKeys.GetKey<IMedia>(payload.Id));
|
||||
mediaCache.Result.Clear(RepositoryCacheKeys.GetKey<IMedia>(payload.Key));
|
||||
mediaCache.Result.Clear(RepositoryCacheKeys.GetKey<IMedia, int>(payload.Id));
|
||||
mediaCache.Result.Clear(RepositoryCacheKeys.GetKey<IMedia, Guid?>(payload.Key));
|
||||
|
||||
// remove those that are in the branch
|
||||
if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.RefreshBranch | TreeChangeTypes.Remove))
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Web.Cache
|
||||
_legacyMemberRefresher = new LegacyMemberCacheRefresher(this, appCaches);
|
||||
}
|
||||
|
||||
public class JsonPayload
|
||||
public class JsonPayload
|
||||
{
|
||||
[JsonConstructor]
|
||||
public JsonPayload(int id, string username)
|
||||
@@ -87,11 +87,11 @@ namespace Umbraco.Web.Cache
|
||||
_idkMap.ClearCache(p.Id);
|
||||
if (memberCache)
|
||||
{
|
||||
memberCache.Result.Clear(RepositoryCacheKeys.GetKey<IMember>(p.Id));
|
||||
memberCache.Result.Clear(RepositoryCacheKeys.GetKey<IMember>(p.Username));
|
||||
}
|
||||
memberCache.Result.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
|
||||
memberCache.Result.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Umbraco.Web.Cache
|
||||
public override void Refresh(int id)
|
||||
{
|
||||
var cache = AppCaches.IsolatedCaches.Get<IRelationType>();
|
||||
if (cache) cache.Result.Clear(RepositoryCacheKeys.GetKey<IRelationType>(id));
|
||||
if (cache) cache.Result.Clear(RepositoryCacheKeys.GetKey<IRelationType, int>(id));
|
||||
base.Refresh(id);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Web.Cache
|
||||
public override void Remove(int id)
|
||||
{
|
||||
var cache = AppCaches.IsolatedCaches.Get<IRelationType>();
|
||||
if (cache) cache.Result.Clear(RepositoryCacheKeys.GetKey<IRelationType>(id));
|
||||
if (cache) cache.Result.Clear(RepositoryCacheKeys.GetKey<IRelationType, int>(id));
|
||||
base.Remove(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,13 @@ namespace Umbraco.Web.Cache
|
||||
var userCache = AppCaches.IsolatedCaches.Get<IUser>();
|
||||
if (userCache)
|
||||
{
|
||||
userCache.Result.Clear(RepositoryCacheKeys.GetKey<IUser>(id));
|
||||
userCache.Result.Clear(RepositoryCacheKeys.GetKey<IUser, int>(id));
|
||||
userCache.Result.ClearByKey(CacheKeys.UserContentStartNodePathsPrefix + id);
|
||||
userCache.Result.ClearByKey(CacheKeys.UserMediaStartNodePathsPrefix + id);
|
||||
userCache.Result.ClearByKey(CacheKeys.UserAllContentStartNodesPrefix + id);
|
||||
userCache.Result.ClearByKey(CacheKeys.UserAllMediaStartNodesPrefix + id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
base.Remove(id);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Umbraco.Web.Cache
|
||||
var userGroupCache = AppCaches.IsolatedCaches.Get<IUserGroup>();
|
||||
if (userGroupCache)
|
||||
{
|
||||
userGroupCache.Result.Clear(RepositoryCacheKeys.GetKey<IUserGroup>(id));
|
||||
userGroupCache.Result.Clear(RepositoryCacheKeys.GetKey<IUserGroup, int>(id));
|
||||
userGroupCache.Result.ClearByKey(UserGroupRepository.GetByAliasCacheKeyPrefix);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user