Converts the media cache refresher to be a json cache refresher since it was impossible before to have media cache

cleared when media was deleted. Created base classes for cache refreshers, we now have a new event - CacheUpdated
which can now be used by code to execute on each individual server when any cache refresher is updated. Listening to events
normally only fire on the individual server so if people are wanting to refresh their own cache there was previously no way
to do that.
This commit is contained in:
Shannon Deminick
2013-03-21 22:53:58 +06:00
parent ab8b0f4ebb
commit 5f242aa3f6
15 changed files with 442 additions and 198 deletions

View File

@@ -21,7 +21,7 @@ namespace Umbraco.Web.Cache
/// <remarks>
/// This is not intended to be used directly in your code
/// </remarks>
public sealed class ContentTypeCacheRefresher : IJsonCacheRefresher
public sealed class ContentTypeCacheRefresher : JsonCacheRefresherBase<ContentTypeCacheRefresher>
{
#region Static helpers
@@ -105,16 +105,21 @@ namespace Umbraco.Web.Cache
#endregion
public Guid UniqueIdentifier
protected override ContentTypeCacheRefresher Instance
{
get { return this; }
}
public override Guid UniqueIdentifier
{
get { return new Guid(DistributedCache.ContentTypeCacheRefresherId); }
}
public string Name
public override string Name
{
get { return "ContentTypeCacheRefresher"; }
}
public void RefreshAll()
public override void RefreshAll()
{
//all property type cache
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.PropertyTypeCacheKey);
@@ -124,40 +129,42 @@ namespace Umbraco.Web.Cache
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.ContentTypeCacheKey);
//clear static object cache
global::umbraco.cms.businesslogic.ContentType.RemoveAllDataTypeCache();
base.RefreshAll();
}
public void Refresh(int id)
public override void Refresh(int id)
{
ClearContentTypeCache(false, id);
base.Refresh(id);
}
public void Remove(int id)
public override void Remove(int id)
{
ClearContentTypeCache(true, id);
base.Remove(id);
}
public void Refresh(Guid id)
{
}
/// <summary>
/// Refreshes the cache using the custom jsonPayload provided
/// </summary>
/// <param name="jsonPayload"></param>
public void Refresh(string jsonPayload)
public override void Refresh(string jsonPayload)
{
var payload = DeserializeFromJsonPayload(jsonPayload);
ClearContentTypeCache(payload);
base.Refresh(jsonPayload);
}
/// <summary>
/// Removes the cache using the custom jsonPayload provided
/// </summary>
/// <param name="jsonPayload"></param>
public void Remove(string jsonPayload)
public override void Remove(string jsonPayload)
{
var payload = DeserializeFromJsonPayload(jsonPayload);
ClearContentTypeCache(payload);
base.Remove(jsonPayload);
}
/// <summary>
@@ -173,10 +180,6 @@ namespace Umbraco.Web.Cache
/// - InMemoryCacheProvider.Current.Clear();
/// - RuntimeCacheProvider.Current.Clear();
/// - RoutesCache.Clear();
///
/// TODO: Needs to update any content items that this effects for the xml cache...
/// it is only handled in the ContentTypeControlNew.ascx, not by business logic/events. - The xml cache needs to be updated
/// when the doc type alias changes or when a property type is removed, the ContentService.RePublishAll should be executed anytime either of these happens.
/// </remarks>
private static void ClearContentTypeCache(IEnumerable<JsonPayload> payloads)
{

View File

@@ -120,16 +120,7 @@ namespace Umbraco.Web.Cache
#endregion
#region Media Cache
/// <summary>
/// Refreshes the cache amongst servers for a media item
/// </summary>
/// <param name="dc"></param>
/// <param name="mediaId"></param>
public static void RefreshMediaCache(this DistributedCache dc, int mediaId)
{
dc.Refresh(new Guid(DistributedCache.MediaCacheRefresherId), mediaId);
}
/// <summary>
/// Refreshes the cache amongst servers for a media item
/// </summary>
@@ -137,7 +128,8 @@ namespace Umbraco.Web.Cache
/// <param name="media"></param>
public static void RefreshMediaCache(this DistributedCache dc, params IMedia[] media)
{
dc.Refresh(new Guid(DistributedCache.MediaCacheRefresherId), x => x.Id, media);
dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId),
MediaCacheRefresher.SerializeToJsonPayload(media));
}
/// <summary>
@@ -145,6 +137,11 @@ namespace Umbraco.Web.Cache
/// </summary>
/// <param name="dc"></param>
/// <param name="mediaId"></param>
/// <remarks>
/// Clearing by Id will never work for load balanced scenarios for media since we require a Path
/// to clear all of the cache but the media item will be removed before the other servers can
/// look it up. Only here for legacy purposes.
/// </remarks>
public static void RemoveMediaCache(this DistributedCache dc, int mediaId)
{
dc.Remove(new Guid(DistributedCache.MediaCacheRefresherId), mediaId);
@@ -157,8 +154,10 @@ namespace Umbraco.Web.Cache
/// <param name="media"></param>
public static void RemoveMediaCache(this DistributedCache dc, params IMedia[] media)
{
dc.Remove(new Guid(DistributedCache.MediaCacheRefresherId), x => x.Id, media);
dc.RemoveByJson(new Guid(DistributedCache.MediaCacheRefresherId),
MediaCacheRefresher.SerializeToJsonPayload(media));
}
#endregion
#region Macro Cache
@@ -173,16 +172,6 @@ namespace Umbraco.Web.Cache
dc.RefreshAll(new Guid(DistributedCache.MacroCacheRefresherId), false);
}
/// <summary>
/// Refreshes the cache amongst servers for a macro item
/// </summary>
/// <param name="dc"></param>
/// <param name="macroId"></param>
public static void RefreshMacroCache(this DistributedCache dc, int macroId)
{
dc.Refresh(new Guid(DistributedCache.MacroCacheRefresherId), macroId);
}
/// <summary>
/// Refreshes the cache amongst servers for a macro item
/// </summary>
@@ -192,20 +181,11 @@ namespace Umbraco.Web.Cache
{
if (macro != null)
{
dc.Refresh(new Guid(DistributedCache.MacroCacheRefresherId), macro1 => macro1.Id, macro);
dc.RefreshByJson(new Guid(DistributedCache.MacroCacheRefresherId),
MacroCacheRefresher.SerializeToJsonPayload(macro));
}
}
/// <summary>
/// Removes the cache amongst servers for a macro item
/// </summary>
/// <param name="dc"></param>
/// <param name="macroId"></param>
public static void RemoveMacroCache(this DistributedCache dc, int macroId)
{
dc.Remove(new Guid(DistributedCache.MacroCacheRefresherId), macroId);
}
/// <summary>
/// Removes the cache amongst servers for a macro item
/// </summary>
@@ -215,7 +195,8 @@ namespace Umbraco.Web.Cache
{
if (macro != null)
{
dc.Remove(new Guid(DistributedCache.MacroCacheRefresherId), macro1 => macro1.Id, macro);
dc.RemoveByJson(new Guid(DistributedCache.MacroCacheRefresherId),
MacroCacheRefresher.SerializeToJsonPayload(macro));
}
}
@@ -228,7 +209,8 @@ namespace Umbraco.Web.Cache
{
if (macro != null && macro.Model != null)
{
dc.Remove(new Guid(DistributedCache.MacroCacheRefresherId), macro1 => macro1.Model.Id, macro);
dc.RemoveByJson(new Guid(DistributedCache.MacroCacheRefresherId),
MacroCacheRefresher.SerializeToJsonPayload(macro));
}
}
#endregion

View File

@@ -1,4 +1,5 @@
using System;
using System.Web.Script.Serialization;
using Umbraco.Core;
using Umbraco.Core.Cache;
using umbraco;
@@ -14,8 +15,10 @@ namespace Umbraco.Web.Cache
/// <remarks>
/// This is not intended to be used directly in your code and it should be sealed but due to legacy code we cannot seal it.
/// </remarks>
public class MacroCacheRefresher : ICacheRefresher<Macro>, ICacheRefresher<macro>
public class MacroCacheRefresher : JsonCacheRefresherBase<MacroCacheRefresher>
{
#region Static helpers
internal static string[] GetAllMacroCacheKeys()
{
return new[]
@@ -33,7 +36,92 @@ namespace Umbraco.Web.Cache
return GetAllMacroCacheKeys().Select(x => x + alias).ToArray();
}
public string Name
/// <summary>
/// Converts the json to a JsonPayload object
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
private static JsonPayload[] DeserializeFromJsonPayload(string json)
{
var serializer = new JavaScriptSerializer();
var jsonObject = serializer.Deserialize<JsonPayload[]>(json);
return jsonObject;
}
/// <summary>
/// Creates the custom Json payload used to refresh cache amongst the servers
/// </summary>
/// <param name="macros"></param>
/// <returns></returns>
internal static string SerializeToJsonPayload(params Macro[] macros)
{
var serializer = new JavaScriptSerializer();
var items = macros.Select(FromMacro).ToArray();
var json = serializer.Serialize(items);
return json;
}
/// <summary>
/// Creates the custom Json payload used to refresh cache amongst the servers
/// </summary>
/// <param name="macros"></param>
/// <returns></returns>
internal static string SerializeToJsonPayload(params macro[] macros)
{
var serializer = new JavaScriptSerializer();
var items = macros.Select(FromMacro).ToArray();
var json = serializer.Serialize(items);
return json;
}
/// <summary>
/// Converts a macro to a jsonPayload object
/// </summary>
/// <param name="macro"></param>
/// <returns></returns>
private static JsonPayload FromMacro(Macro macro)
{
var payload = new JsonPayload
{
Alias = macro.Alias,
Id = macro.Id
};
return payload;
}
/// <summary>
/// Converts a macro to a jsonPayload object
/// </summary>
/// <param name="macro"></param>
/// <returns></returns>
private static JsonPayload FromMacro(macro macro)
{
var payload = new JsonPayload
{
Alias = macro.Alias,
Id = macro.Model.Id
};
return payload;
}
#endregion
#region Sub classes
private class JsonPayload
{
public string Alias { get; set; }
public int Id { get; set; }
}
#endregion
protected override MacroCacheRefresher Instance
{
get { return this; }
}
public override string Name
{
get
{
@@ -41,7 +129,7 @@ namespace Umbraco.Web.Cache
}
}
public Guid UniqueIdentifier
public override Guid UniqueIdentifier
{
get
{
@@ -49,60 +137,34 @@ namespace Umbraco.Web.Cache
}
}
public void RefreshAll()
public override void RefreshAll()
{
ApplicationContext.Current.ApplicationCache.ClearCacheObjectTypes<MacroCacheContent>();
GetAllMacroCacheKeys().ForEach(
prefix =>
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(prefix));
base.RefreshAll();
}
public void Refresh(Guid id)
public override void Refresh(string jsonPayload)
{
Remove(jsonPayload);
base.Refresh(jsonPayload);
}
public void Refresh(int id)
public override void Remove(string jsonPayload)
{
if (id <= 0) return;
var m = new Macro(id);
Remove(m);
}
var payloads = DeserializeFromJsonPayload(jsonPayload);
public void Remove(int id)
{
if (id <= 0) return;
var m = new Macro(id);
Remove(m);
}
payloads.ForEach(payload =>
{
GetCacheKeysForAlias(payload.Alias).ForEach(
alias =>
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(alias));
});
public void Refresh(Macro instance)
{
Remove(instance);
}
public void Remove(Macro instance)
{
if (instance != null && instance.Id > 0)
{
GetCacheKeysForAlias(instance.Alias).ForEach(
alias =>
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(alias));
}
}
public void Refresh(macro instance)
{
Remove(instance);
}
public void Remove(macro instance)
{
if (instance == null || instance.Model == null) return;
var m = instance.Model;
GetCacheKeysForAlias(m.Alias).ForEach(
alias =>
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(alias));
base.Remove(jsonPayload);
}
}
}

View File

@@ -1,73 +1,138 @@
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using umbraco.interfaces;
using System.Linq;
namespace Umbraco.Web.Cache
{
/// <summary>
/// A cache refresher to ensure media cache is updated when members change
/// A cache refresher to ensure media cache is updated
/// </summary>
/// <remarks>
/// This is not intended to be used directly in your code and it should be sealed but due to legacy code we cannot seal it.
/// </remarks>
public class MediaCacheRefresher : ICacheRefresher<IMedia>
public class MediaCacheRefresher : JsonCacheRefresherBase<MediaCacheRefresher>
{
public Guid UniqueIdentifier
#region Static helpers
/// <summary>
/// Converts the json to a JsonPayload object
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
private static JsonPayload[] DeserializeFromJsonPayload(string json)
{
var serializer = new JavaScriptSerializer();
var jsonObject = serializer.Deserialize<JsonPayload[]>(json);
return jsonObject;
}
/// <summary>
/// Creates the custom Json payload used to refresh cache amongst the servers
/// </summary>
/// <param name="media"></param>
/// <returns></returns>
internal static string SerializeToJsonPayload(params IMedia[] media)
{
var serializer = new JavaScriptSerializer();
var items = media.Select(FromMedia).ToArray();
var json = serializer.Serialize(items);
return json;
}
/// <summary>
/// Converts a macro to a jsonPayload object
/// </summary>
/// <param name="media"></param>
/// <returns></returns>
private static JsonPayload FromMedia(IMedia media)
{
if (media == null) return null;
var payload = new JsonPayload
{
Id = media.Id,
Path = media.Path
};
return payload;
}
#endregion
#region Sub classes
private class JsonPayload
{
public string Path { get; set; }
public int Id { get; set; }
}
#endregion
protected override MediaCacheRefresher Instance
{
get { return this; }
}
public override Guid UniqueIdentifier
{
get { return new Guid(DistributedCache.MediaCacheRefresherId); }
}
public string Name
public override string Name
{
get { return "Clears Media Cache from umbraco.library"; }
}
public void RefreshAll()
public override void Refresh(string jsonPayload)
{
ClearCache(DeserializeFromJsonPayload(jsonPayload));
base.Refresh(jsonPayload);
}
public void Refresh(int id)
public override void Remove(string jsonPayload)
{
ClearCache(ApplicationContext.Current.Services.MediaService.GetById(id));
ClearCache(DeserializeFromJsonPayload(jsonPayload));
base.Remove(jsonPayload);
}
public void Remove(int id)
public override void Refresh(int id)
{
ClearCache(ApplicationContext.Current.Services.MediaService.GetById(id));
ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id)));
base.Refresh(id);
}
public void Refresh(Guid id)
public override void Remove(int id)
{
ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id)));
base.Remove(id);
}
public void Refresh(IMedia instance)
private static void ClearCache(params JsonPayload[] payloads)
{
ClearCache(instance);
}
if (payloads == null) return;
public void Remove(IMedia instance)
{
ClearCache(instance);
}
payloads.ForEach(payload =>
{
foreach (var idPart in payload.Path.Split(','))
{
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}_{1}_True", CacheKeys.MediaCacheKey, idPart));
private static void ClearCache(IMedia media)
{
if (media == null) return;
// Also clear calls that only query this specific item!
if (idPart == payload.Id.ToString())
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}_{1}", CacheKeys.MediaCacheKey, payload.Id));
foreach (var idPart in media.Path.Split(','))
{
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}_{1}_True", CacheKeys.MediaCacheKey, idPart));
}
});
// Also clear calls that only query this specific item!
if (idPart == media.Id.ToString())
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}_{1}", CacheKeys.MediaCacheKey, media.Id));
}
}
}
}

View File

@@ -12,36 +12,34 @@ namespace Umbraco.Web.Cache
/// <remarks>
/// This is not intended to be used directly in your code and it should be sealed but due to legacy code we cannot seal it.
/// </remarks>
public class MemberCacheRefresher : ICacheRefresher<Member>
public class MemberCacheRefresher : CacheRefresherBase<MemberCacheRefresher>
{
public Guid UniqueIdentifier
protected override MemberCacheRefresher Instance
{
get { return this; }
}
public override Guid UniqueIdentifier
{
get { return new Guid(DistributedCache.MemberCacheRefresherId); }
}
public string Name
public override string Name
{
get { return "Clears Member Cache from umbraco.library"; }
}
public void RefreshAll()
{
}
public void Refresh(int id)
public override void Refresh(int id)
{
ClearCache(id);
base.Refresh(id);
}
public void Remove(int id)
public override void Remove(int id)
{
ClearCache(id);
}
public void Refresh(Guid id)
{
base.Remove(id);
}
private void ClearCache(int id)
@@ -49,15 +47,5 @@ namespace Umbraco.Web.Cache
ApplicationContext.Current.ApplicationCache.
ClearCacheByKeySearch(string.Format("{0}_{1}", CacheKeys.MemberCacheKey, id));
}
public void Refresh(Member instance)
{
throw new NotImplementedException();
}
public void Remove(Member instance)
{
throw new NotImplementedException();
}
}
}

View File

@@ -16,13 +16,19 @@ namespace Umbraco.Web.Cache
/// If Load balancing is enabled (by default disabled, is set in umbracoSettings.config) PageCacheRefresher will be called
/// everytime content is added/updated/removed to ensure that the content cache is identical on all load balanced servers
/// </remarks>
public class PageCacheRefresher : ICacheRefresher<IContent>
{
public class PageCacheRefresher : TypedCacheRefresherBase<PageCacheRefresher, IContent>
{
protected override PageCacheRefresher Instance
{
get { return this; }
}
/// <summary>
/// Gets the unique identifier of the CacheRefresher.
/// </summary>
/// <value>The unique identifier.</value>
public Guid UniqueIdentifier
public override Guid UniqueIdentifier
{
get
{
@@ -34,7 +40,7 @@ namespace Umbraco.Web.Cache
/// Gets the name of the CacheRefresher
/// </summary>
/// <value>The name.</value>
public string Name
public override string Name
{
get { return "Page Refresher"; }
}
@@ -42,46 +48,42 @@ namespace Umbraco.Web.Cache
/// <summary>
/// Refreshes all nodes in umbraco.
/// </summary>
public void RefreshAll()
public override void RefreshAll()
{
content.Instance.RefreshContentFromDatabaseAsync();
}
/// <summary>
/// Not used with content.
/// </summary>
/// <param name="id">The id.</param>
public void Refresh(Guid id)
{
// Not used when pages
base.RefreshAll();
}
/// <summary>
/// Refreshes the cache for the node with specified id
/// </summary>
/// <param name="id">The id.</param>
public void Refresh(int id)
public override void Refresh(int id)
{
content.Instance.UpdateDocumentCache(id);
base.Refresh(id);
}
/// <summary>
/// Removes the node with the specified id from the cache
/// </summary>
/// <param name="id">The id.</param>
public void Remove(int id)
public override void Remove(int id)
{
content.Instance.ClearDocumentCache(id);
base.Remove(id);
}
public void Refresh(IContent instance)
public override void Refresh(IContent instance)
{
content.Instance.UpdateDocumentCache(new Document(instance));
base.Refresh(instance);
}
public void Remove(IContent instance)
public override void Remove(IContent instance)
{
content.Instance.ClearDocumentCache(new Document(instance));
base.Remove(instance);
}
}
}

View File

@@ -13,10 +13,14 @@ namespace Umbraco.Web.Cache
/// <remarks>
/// This is not intended to be used directly in your code and it should be sealed but due to legacy code we cannot seal it.
/// </remarks>
public class TemplateCacheRefresher : ICacheRefresher
public class TemplateCacheRefresher : CacheRefresherBase<TemplateCacheRefresher>
{
public string Name
protected override TemplateCacheRefresher Instance
{
get { return this; }
}
public override string Name
{
get
{
@@ -24,7 +28,7 @@ namespace Umbraco.Web.Cache
}
}
public Guid UniqueIdentifier
public override Guid UniqueIdentifier
{
get
{
@@ -32,22 +36,16 @@ namespace Umbraco.Web.Cache
}
}
public void RefreshAll()
{
}
public void Refresh(Guid id)
{
}
public void Refresh(int id)
public override void Refresh(int id)
{
RemoveFromCache(id);
base.Refresh(id);
}
public void Remove(int id)
public override void Remove(int id)
{
RemoveFromCache(id);
base.Remove(id);
}
private void RemoveFromCache(int id)

View File

@@ -8,35 +8,37 @@ namespace Umbraco.Web.Cache
/// <summary>
/// Handles User cache invalidation/refreshing
/// </summary>
public sealed class UserCacheRefresher : ICacheRefresher
public sealed class UserCacheRefresher : CacheRefresherBase<UserCacheRefresher>
{
public Guid UniqueIdentifier
protected override UserCacheRefresher Instance
{
get { return this; }
}
public override Guid UniqueIdentifier
{
get { return Guid.Parse(DistributedCache.UserCacheRefresherId); }
}
public string Name
public override string Name
{
get { return "User cache refresher"; }
}
public void RefreshAll()
public override void RefreshAll()
{
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserCacheKey);
}
public void Refresh(int id)
public override void Refresh(int id)
{
Remove(id);
}
public void Remove(int id)
public override void Remove(int id)
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserCacheKey, id.ToString()));
ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserCacheKey, id));
}
public void Refresh(Guid id)
{
}
}
}