U4-6675 - in-memory temp media cache

This commit is contained in:
Stephan
2015-06-18 15:36:04 +02:00
parent 48dd3fe0c7
commit 012c3289cd
4 changed files with 201 additions and 68 deletions

View File

@@ -18,6 +18,9 @@ using Umbraco.Core.Xml;
using Umbraco.Web.Models;
using UmbracoExamine;
using umbraco;
using Umbraco.Core.Cache;
using Umbraco.Core.Sync;
using Umbraco.Web.Cache;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
@@ -167,9 +170,20 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
return null;
}
private IPublishedContent GetUmbracoMedia(int id)
{
var searchProvider = GetSearchProviderSafe();
private IPublishedContent GetUmbracoMedia(int id)
{
// this recreates an IPublishedContent and model each time
// it is called, but at least it should NOT hit the database
// nor Lucene each time, relying on the memory cache instead
var cacheValues = GetCacheValues(id, GetUmbracoMediaCacheValues);
return cacheValues == null ? null : CreateFromCacheValues(cacheValues);
}
private CacheValues GetUmbracoMediaCacheValues(int id)
{
var searchProvider = GetSearchProviderSafe();
if (searchProvider != null)
{
@@ -202,12 +216,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
"Could not retrieve media {0} from Examine index, reverting to looking up media via legacy library.GetMedia method",
() => id);
var media = global::umbraco.library.GetMedia(id, true);
var media = global::umbraco.library.GetMedia(id, false);
return ConvertFromXPathNodeIterator(media, id);
}
internal IPublishedContent ConvertFromXPathNodeIterator(XPathNodeIterator media, int id)
internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id)
{
if (media != null && media.Current != null)
{
@@ -223,7 +237,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
return null;
}
internal IPublishedContent ConvertFromSearchResult(SearchResult searchResult)
internal CacheValues ConvertFromSearchResult(SearchResult searchResult)
{
//NOTE: Some fields will not be included if the config section for the internal index has been
//mucked around with. It should index everything and so the index definition should simply be:
@@ -253,19 +267,24 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
values.Add("level", values["__Path"].Split(',').Length.ToString());
}
return new CacheValues
{
Values = values,
FromExamine = true
};
var content = new DictionaryPublishedContent(values,
d => d.ParentId != -1 //parent should be null if -1
? GetUmbracoMedia(d.ParentId)
: null,
//callback to return the children of the current node
d => GetChildrenMedia(d.Id),
GetProperty,
true);
return content.CreateModel();
//var content = new DictionaryPublishedContent(values,
// d => d.ParentId != -1 //parent should be null if -1
// ? GetUmbracoMedia(d.ParentId)
// : null,
// //callback to return the children of the current node
// d => GetChildrenMedia(d.Id),
// GetProperty,
// true);
//return content.CreateModel();
}
internal IPublishedContent ConvertFromXPathNavigator(XPathNavigator xpath)
internal CacheValues ConvertFromXPathNavigator(XPathNavigator xpath, bool forceNav = false)
{
if (xpath == null) throw new ArgumentNullException("xpath");
@@ -313,15 +332,21 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
}
var content = new DictionaryPublishedContent(values,
d => d.ParentId != -1 //parent should be null if -1
? GetUmbracoMedia(d.ParentId)
: null,
//callback to return the children of the current node based on the xml structure already found
d => GetChildrenMedia(d.Id, xpath),
GetProperty,
false);
return content.CreateModel();
return new CacheValues
{
Values = values,
XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator!
};
//var content = new DictionaryPublishedContent(values,
// d => d.ParentId != -1 //parent should be null if -1
// ? GetUmbracoMedia(d.ParentId)
// : null,
// //callback to return the children of the current node based on the xml structure already found
// d => GetChildrenMedia(d.Id, xpath),
// GetProperty,
// false);
//return content.CreateModel();
}
/// <summary>
@@ -398,9 +423,17 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
if (results.Any())
{
return useLuceneSort
? results.Select(ConvertFromSearchResult) //will already be sorted by lucene
: results.Select(ConvertFromSearchResult).OrderBy(x => x.SortOrder);
// var medias = results.Select(ConvertFromSearchResult);
var medias = results.Select(x =>
{
int nid;
if (int.TryParse(x["__NodeId"], out nid) == false && int.TryParse(x["NodeId"], out nid) == false)
throw new Exception("Failed to extract NodeId from search result.");
var cacheValues = GetCacheValues(nid, id => ConvertFromSearchResult(x));
return CreateFromCacheValues(cacheValues);
});
return useLuceneSort ? medias : medias.OrderBy(x => x.SortOrder);
}
else
{
@@ -432,29 +465,51 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
}
//The xpath might be the whole xpath including the current ones ancestors so we need to select the current node
var item = xpath.Select("//*[@id='" + parentId + "']");
if (item.Current == null)
{
return Enumerable.Empty<IPublishedContent>();
}
var children = item.Current.SelectChildren(XPathNodeType.Element);
var mediaList = new List<IPublishedContent>();
// this is so bad, really
var item = xpath.Select("//*[@id='" + parentId + "']");
if (item.Current == null)
return Enumerable.Empty<IPublishedContent>();
var items = item.Current.SelectChildren(XPathNodeType.Element);
// and this does not work, because... meh
//var q = "//* [@id='" + parentId + "']/* [@id]";
//var items = xpath.Select(q);
foreach (XPathNavigator itemm in items)
{
int id;
if (int.TryParse(itemm.GetAttribute("id", ""), out id) == false)
continue; // wtf?
var captured = itemm;
var cacheValues = GetCacheValues(id, idd => ConvertFromXPathNavigator(captured));
mediaList.Add(CreateFromCacheValues(cacheValues));
}
////The xpath might be the whole xpath including the current ones ancestors so we need to select the current node
//var item = xpath.Select("//*[@id='" + parentId + "']");
//if (item.Current == null)
//{
// return Enumerable.Empty<IPublishedContent>();
//}
//var children = item.Current.SelectChildren(XPathNodeType.Element);
//foreach(XPathNavigator x in children)
//{
// //NOTE: I'm not sure why this is here, it is from legacy code of ExamineBackedMedia, but
// // will leave it here as it must have done something!
// if (x.Name != "contents")
// {
// //make sure it's actually a node, not a property
// if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) &&
// !string.IsNullOrEmpty(x.GetAttribute("id", "")))
// {
// mediaList.Add(ConvertFromXPathNavigator(x));
// }
// }
//}
var mediaList = new List<IPublishedContent>();
foreach(XPathNavigator x in children)
{
//NOTE: I'm not sure why this is here, it is from legacy code of ExamineBackedMedia, but
// will leave it here as it must have done something!
if (x.Name != "contents")
{
//make sure it's actually a node, not a property
if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) &&
!string.IsNullOrEmpty(x.GetAttribute("id", "")))
{
mediaList.Add(ConvertFromXPathNavigator(x));
}
}
}
return mediaList;
}
@@ -477,23 +532,25 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
public DictionaryPublishedContent(
IDictionary<string, string> valueDictionary,
Func<DictionaryPublishedContent, IPublishedContent> getParent,
Func<DictionaryPublishedContent, IEnumerable<IPublishedContent>> getChildren,
Func<int, IPublishedContent> getParent,
Func<int, XPathNavigator, IEnumerable<IPublishedContent>> getChildren,
Func<DictionaryPublishedContent, string, IPublishedProperty> getProperty,
XPathNavigator nav,
bool fromExamine)
{
if (valueDictionary == null) throw new ArgumentNullException("valueDictionary");
if (getParent == null) throw new ArgumentNullException("getParent");
if (getProperty == null) throw new ArgumentNullException("getProperty");
_getParent = getParent;
_getChildren = getChildren;
_getParent = new Lazy<IPublishedContent>(() => getParent(ParentId));
_getChildren = new Lazy<IEnumerable<IPublishedContent>>(() => getChildren(Id, nav));
_getProperty = getProperty;
LoadedFromExamine = fromExamine;
ValidateAndSetProperty(valueDictionary, val => _id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int!
ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId");
// wtf are we dealing with templates for medias?!
ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId");
ValidateAndSetProperty(valueDictionary, val => _sortOrder = int.Parse(val), "sortOrder");
ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName");
ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName");
@@ -576,8 +633,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
/// </summary>
internal bool LoadedFromExamine { get; private set; }
private readonly Func<DictionaryPublishedContent, IPublishedContent> _getParent;
private readonly Func<DictionaryPublishedContent, IEnumerable<IPublishedContent>> _getChildren;
//private readonly Func<DictionaryPublishedContent, IPublishedContent> _getParent;
private readonly Lazy<IPublishedContent> _getParent;
//private readonly Func<DictionaryPublishedContent, IEnumerable<IPublishedContent>> _getChildren;
private readonly Lazy<IEnumerable<IPublishedContent>> _getChildren;
private readonly Func<DictionaryPublishedContent, string, IPublishedProperty> _getProperty;
/// <summary>
@@ -590,7 +649,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
public override IPublishedContent Parent
{
get { return _getParent(this); }
get { return _getParent.Value; }
}
public int ParentId { get; private set; }
@@ -690,7 +749,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
public override IEnumerable<IPublishedContent> Children
{
get { return _getChildren(this); }
get { return _getChildren.Value; }
}
public override IPublishedProperty GetProperty(string alias)
@@ -768,5 +827,72 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
_keysAdded.Add(key);
}
}
}
// REFACTORING
// caching the basic atomic values - and the parent id
// but NOT caching actual parent nor children and NOT even
// the list of children ids - BUT caching the path
internal class CacheValues
{
public IDictionary<string, string> Values { get; set; }
public XPathNavigator XPath { get; set; }
public bool FromExamine { get; set; }
}
public const string PublishedMediaCacheKey = "MediaCacheMeh.";
private const int PublishedMediaCacheTimespanSeconds = 60;
private static readonly TimeSpan PublishedMediaCacheTimespan = TimeSpan.FromSeconds(PublishedMediaCacheTimespanSeconds);
internal IPublishedContent CreateFromCacheValues(CacheValues cacheValues)
{
var content = new DictionaryPublishedContent(
cacheValues.Values,
parentId => parentId < 0 ? null : GetUmbracoMedia(parentId),
GetChildrenMedia,
GetProperty,
cacheValues.XPath, // though, outside of tests, that should be null
cacheValues.FromExamine
);
return content.CreateModel();
}
private static CacheValues GetCacheValues(int id, Func<int, CacheValues> func)
{
var cache = ApplicationContext.Current.ApplicationCache.RuntimeCache;
var key = PublishedMediaCacheKey + id;
return (CacheValues) cache.GetCacheItem(key, () => func(id), PublishedMediaCacheTimespan);
}
internal static void ClearCache(int id)
{
var cache = ApplicationContext.Current.ApplicationCache.RuntimeCache;
var sid = id.ToString();
var key = PublishedMediaCacheKey + sid;
// we do clear a lot of things... but the cache refresher is somewhat
// convoluted and it's hard to tell what to clear exactly ;-(
// clear the parent - NOT (why?)
//var exist = (CacheValues) cache.GetCacheItem(key);
//if (exist != null)
// cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID"));
// clear the item
cache.ClearCacheItem(key);
// clear all children - in case we moved and their path has changed
var fid = "/" + sid + "/";
cache.ClearCacheObjectTypes<CacheValues>((k, v) =>
GetValuesValue(v.Values, "path", "__Path").Contains(fid));
}
private static string GetValuesValue(IDictionary<string, string> d, params string[] keys)
{
string value = null;
var ignored = keys.Any(x => d.TryGetValue(x, out value));
return value ?? "";
}
}
}