V13: Clear Member Username Cache in Load Balanced Environments (#19191)

* Clear usernamekey

* Odd explaining comment

* Update src/Umbraco.Core/Cache/Refreshers/Implement/MemberCacheRefresher.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Make UserNameCachePrefix readonly for better immutabilityly

* Move prefix to CacheKeys constants

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
# Conflicts:
#	src/Umbraco.Core/Cache/CacheKeys.cs
This commit is contained in:
Andy Butland
2025-04-29 18:10:44 +02:00
parent 375a0b4388
commit 14f60a108a
3 changed files with 19 additions and 7 deletions

View File

@@ -22,4 +22,6 @@ public static class CacheKeys
public const string PreviewPropertyCacheKeyPrefix = "Cache.Property.CacheValues[D:";
public const string PropertyCacheKeyPrefix = "Cache.Property.CacheValues[P:";
public const string MemberUserNameCachePrefix = "uRepo_userNameKey+";
}

View File

@@ -71,11 +71,22 @@ public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCache
foreach (JsonPayload p in payloads)
{
_idKeyMap.ClearCache(p.Id);
if (memberCache.Success)
if (memberCache.Success is false)
{
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
continue;
}
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, int>(p.Id));
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(p.Username));
// This specific cache key was introduced to fix an issue where the member username could not be the same as the member id, because the cache keys collided.
// This is done in a bit of a hacky way, because the cache key is created internally in the repository, but we need to clear it here.
// Ideally, we want to use a shared way of generating the key between this and the repository.
// Additionally, the RepositoryCacheKeys actually caches the string to avoid re-allocating memory; we would like to also use this in the repository
// See:
// https://github.com/umbraco/Umbraco-CMS/pull/17350
// https://github.com/umbraco/Umbraco-CMS/pull/17815
memberCache.Result?.Clear(RepositoryCacheKeys.GetKey<IMember, string>(CacheKeys.MemberUserNameCachePrefix + p.Username));
}
}
}

View File

@@ -39,7 +39,6 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
private readonly ITagRepository _tagRepository;
private bool _passwordConfigInitialized;
private string? _passwordConfigJson;
private const string UsernameCacheKey = "uRepo_userNameKey+";
public MemberRepository(
IScopeAccessor scopeAccessor,
@@ -327,7 +326,7 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
}
public IMember? GetByUsername(string? username) =>
_memberByUsernameCachePolicy.GetByUserName(UsernameCacheKey, username, PerformGetByUsername, PerformGetAllByUsername);
_memberByUsernameCachePolicy.GetByUserName(CacheKeys.MemberUserNameCachePrefix, username, PerformGetByUsername, PerformGetAllByUsername);
public int[] GetMemberIds(string[] usernames)
{
@@ -609,7 +608,7 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
protected override void PersistDeletedItem(IMember entity)
{
_memberByUsernameCachePolicy.DeleteByUserName(UsernameCacheKey, entity.Username);
_memberByUsernameCachePolicy.DeleteByUserName(CacheKeys.MemberUserNameCachePrefix, entity.Username);
base.PersistDeletedItem(entity);
}
@@ -943,7 +942,7 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
OnUowRefreshedEntity(new MemberRefreshNotification(entity, new EventMessages()));
_memberByUsernameCachePolicy.DeleteByUserName(UsernameCacheKey, entity.Username);
_memberByUsernameCachePolicy.DeleteByUserName(CacheKeys.MemberUserNameCachePrefix, entity.Username);
entity.ResetDirtyProperties();
}