Fixes: #U4-1732
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
@@ -28,34 +29,44 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
if (bindToEvents)
|
||||
{
|
||||
// content - whenever the entire XML cache is rebuilt (from disk cache or from database)
|
||||
// we must clear the cache entirely
|
||||
global::umbraco.content.AfterRefreshContent += (sender, e) => Clear();
|
||||
|
||||
// document - whenever a document is updated in, or removed from, the XML cache
|
||||
// we must clear the cache - at the moment, we clear the entire cache
|
||||
// TODO could we do partial updates instead of clearing the whole cache?
|
||||
global::umbraco.content.AfterUpdateDocumentCache += (sender, e) => Clear();
|
||||
global::umbraco.content.AfterClearDocumentCache += (sender, e) => Clear();
|
||||
|
||||
// domains - whenever a domain change we must clear the cache
|
||||
// because routes contain the id of root nodes of domains
|
||||
// TODO could we do partial updates instead of clearing the whole cache?
|
||||
global::umbraco.cms.businesslogic.web.Domain.AfterDelete += (sender, e) => Clear();
|
||||
global::umbraco.cms.businesslogic.web.Domain.AfterSave += (sender, e) => Clear();
|
||||
global::umbraco.cms.businesslogic.web.Domain.New += (sender, e) => Clear();
|
||||
|
||||
// FIXME
|
||||
// the content class needs to be refactored - at the moment
|
||||
// content.XmlContentInternal setter does not trigger any event
|
||||
// content.UpdateDocumentCache(List<Document> Documents) does not trigger any event
|
||||
// content.RefreshContentFromDatabaseAsync triggers AfterRefresh _while_ refreshing
|
||||
// etc...
|
||||
// in addition some events do not make sense... we trigger Publish when moving
|
||||
// a node, which we should not (the node is moved, not published...) etc.
|
||||
Resolution.Frozen += ResolutionFrozen;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Once resolution is frozen, then we can bind to the events that we require
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <param name="args"></param>
|
||||
void ResolutionFrozen(object s, EventArgs args)
|
||||
{
|
||||
// content - whenever the entire XML cache is rebuilt (from disk cache or from database)
|
||||
// we must clear the cache entirely
|
||||
global::umbraco.content.AfterRefreshContent += (sender, e) => Clear();
|
||||
|
||||
// document - whenever a document is updated in, or removed from, the XML cache
|
||||
// we must clear the cache - at the moment, we clear the entire cache
|
||||
// TODO could we do partial updates instead of clearing the whole cache?
|
||||
global::umbraco.content.AfterUpdateDocumentCache += (sender, e) => Clear();
|
||||
global::umbraco.content.AfterClearDocumentCache += (sender, e) => Clear();
|
||||
|
||||
// domains - whenever a domain change we must clear the cache
|
||||
// because routes contain the id of root nodes of domains
|
||||
// TODO could we do partial updates instead of clearing the whole cache?
|
||||
global::umbraco.cms.businesslogic.web.Domain.AfterDelete += (sender, e) => Clear();
|
||||
global::umbraco.cms.businesslogic.web.Domain.AfterSave += (sender, e) => Clear();
|
||||
global::umbraco.cms.businesslogic.web.Domain.New += (sender, e) => Clear();
|
||||
|
||||
// FIXME
|
||||
// the content class needs to be refactored - at the moment
|
||||
// content.XmlContentInternal setter does not trigger any event
|
||||
// content.UpdateDocumentCache(List<Document> Documents) does not trigger any event
|
||||
// content.RefreshContentFromDatabaseAsync triggers AfterRefresh _while_ refreshing
|
||||
// etc...
|
||||
// in addition some events do not make sense... we trigger Publish when moving
|
||||
// a node, which we should not (the node is moved, not published...) etc.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used ONLY for unit tests
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Threading;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using umbraco.BusinessLogic;
|
||||
using umbraco.BusinessLogic.Actions;
|
||||
@@ -15,7 +16,6 @@ using umbraco.cms.businesslogic;
|
||||
using umbraco.cms.businesslogic.cache;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
using umbraco.DataLayer;
|
||||
using umbraco.IO;
|
||||
using umbraco.presentation.nodeFactory;
|
||||
using Action = umbraco.BusinessLogic.Actions.Action;
|
||||
using Node = umbraco.NodeFactory.Node;
|
||||
@@ -30,17 +30,17 @@ namespace umbraco
|
||||
#region Declarations
|
||||
|
||||
// Sync access to disk file
|
||||
private static readonly object _readerWriterSyncLock = new object();
|
||||
private static readonly object ReaderWriterSyncLock = new object();
|
||||
|
||||
// Sync access to internal cache
|
||||
private static readonly object _xmlContentInternalSyncLock = new object();
|
||||
private static readonly object XmlContentInternalSyncLock = new object();
|
||||
|
||||
// Sync access to timestamps
|
||||
private static readonly object _timestampSyncLock = new object();
|
||||
private static readonly object TimestampSyncLock = new object();
|
||||
|
||||
// Sync database access
|
||||
private static readonly object _dbReadSyncLock = new object();
|
||||
private readonly string XmlContextContentItemKey = "UmbracoXmlContextContent";
|
||||
private static readonly object DbReadSyncLock = new object();
|
||||
private const string XmlContextContentItemKey = "UmbracoXmlContextContent";
|
||||
private string _umbracoXmlDiskCacheFileName = string.Empty;
|
||||
private volatile XmlDocument _xmlContent;
|
||||
private DateTime _lastDiskCacheReadTime = DateTime.MinValue;
|
||||
@@ -65,46 +65,29 @@ namespace umbraco
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
static content()
|
||||
{
|
||||
//Trace.Write("Initializing content");
|
||||
//ThreadPool.QueueUserWorkItem(
|
||||
// delegate
|
||||
// {
|
||||
// XmlDocument xmlDoc = Instance.XmlContentInternal;
|
||||
// Trace.WriteLine("Content initialized");
|
||||
// });
|
||||
|
||||
Trace.WriteLine("Checking for xml content initialisation...");
|
||||
Instance.CheckXmlContentPopulation();
|
||||
}
|
||||
|
||||
public content()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Singleton
|
||||
|
||||
private static readonly Lazy<content> LazyInstance = new Lazy<content>(() => new content());
|
||||
|
||||
public static content Instance
|
||||
{
|
||||
get { return Singleton<content>.Instance; }
|
||||
get
|
||||
{
|
||||
return LazyInstance.Value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// <remarks>
|
||||
/// Get content. First call to this property will initialize xmldoc
|
||||
/// subsequent calls will be blocked until initialization is done
|
||||
/// Further we cache(in context) xmlContent for each request to ensure that
|
||||
/// we always have the same XmlDoc throughout the whole request.
|
||||
/// </summary>
|
||||
/// </remarks>
|
||||
public virtual XmlDocument XmlContent
|
||||
{
|
||||
get
|
||||
@@ -127,6 +110,9 @@ namespace umbraco
|
||||
get { return Instance.XmlContent; }
|
||||
}
|
||||
|
||||
//NOTE: We CANNOT use this for a double check lock because it is a property, not a field and to do double
|
||||
// check locking in c# you MUST have a volatile field. Even thoug this wraps a volatile field it will still
|
||||
// not work as expected for a double check lock because properties are treated differently in the clr.
|
||||
public virtual bool isInitializing
|
||||
{
|
||||
get { return _xmlContent == null; }
|
||||
@@ -135,6 +121,9 @@ namespace umbraco
|
||||
/// <summary>
|
||||
/// Internal reference to XmlContent
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Before returning we always check to ensure that the xml is loaded
|
||||
/// </remarks>
|
||||
protected virtual XmlDocument XmlContentInternal
|
||||
{
|
||||
get
|
||||
@@ -145,7 +134,7 @@ namespace umbraco
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_xmlContentInternalSyncLock)
|
||||
lock (XmlContentInternalSyncLock)
|
||||
{
|
||||
// Clear macro cache
|
||||
Cache.ClearCacheObjectTypes("umbraco.MacroCacheContent");
|
||||
@@ -179,14 +168,14 @@ namespace umbraco
|
||||
if (UmbracoSettings.isXmlContentCacheDisabled)
|
||||
return;
|
||||
|
||||
lock (_timestampSyncLock)
|
||||
lock (TimestampSyncLock)
|
||||
{
|
||||
if (_lastDiskCacheCheckTime > DateTime.UtcNow.AddSeconds(-1.0))
|
||||
return;
|
||||
|
||||
_lastDiskCacheCheckTime = DateTime.UtcNow;
|
||||
|
||||
lock (_xmlContentInternalSyncLock)
|
||||
lock (XmlContentInternalSyncLock)
|
||||
{
|
||||
|
||||
if (GetCacheFileUpdateTime() <= _lastDiskCacheReadTime)
|
||||
@@ -200,17 +189,17 @@ namespace umbraco
|
||||
/// <summary>
|
||||
/// Triggers the XML content population if necessary.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns>Returns true of the XML was not populated, returns false if it was already populated</returns>
|
||||
private bool CheckXmlContentPopulation()
|
||||
{
|
||||
if (UmbracoSettings.XmlContentCheckForDiskChanges)
|
||||
CheckDiskCacheForUpdate();
|
||||
|
||||
if (isInitializing)
|
||||
if (_xmlContent == null)
|
||||
{
|
||||
lock (_xmlContentInternalSyncLock)
|
||||
lock (XmlContentInternalSyncLock)
|
||||
{
|
||||
if (isInitializing)
|
||||
if (_xmlContent == null)
|
||||
{
|
||||
LogHelper.Debug<content>("Initializing content on thread '{0}' (Threadpool? {1})",
|
||||
true,
|
||||
@@ -504,7 +493,7 @@ namespace umbraco
|
||||
{
|
||||
// lock the xml cache so no other thread can write to it at the same time
|
||||
// note that some threads could read from it while we hold the lock, though
|
||||
lock (_xmlContentInternalSyncLock)
|
||||
lock (XmlContentInternalSyncLock)
|
||||
{
|
||||
// modify a clone of the cache because even though we're into the write-lock
|
||||
// we may have threads reading at the same time. why is this an option?
|
||||
@@ -551,7 +540,7 @@ namespace umbraco
|
||||
// making changes at the same time, they need to be queued
|
||||
int parentid = Documents[0].Id;
|
||||
|
||||
lock (_xmlContentInternalSyncLock)
|
||||
lock (XmlContentInternalSyncLock)
|
||||
{
|
||||
// Make copy of memory content, we cannot make changes to the same document
|
||||
// the is read from elsewhere
|
||||
@@ -621,7 +610,7 @@ namespace umbraco
|
||||
|
||||
// We need to lock content cache here, because we cannot allow other threads
|
||||
// making changes at the same time, they need to be queued
|
||||
lock (_xmlContentInternalSyncLock)
|
||||
lock (XmlContentInternalSyncLock)
|
||||
{
|
||||
// Make copy of memory content, we cannot make changes to the same document
|
||||
// the is read from elsewhere
|
||||
@@ -961,7 +950,7 @@ namespace umbraco
|
||||
/// </summary>
|
||||
private void DeleteXmlCache()
|
||||
{
|
||||
lock (_readerWriterSyncLock)
|
||||
lock (ReaderWriterSyncLock)
|
||||
{
|
||||
if (File.Exists(UmbracoXmlDiskCacheFileName))
|
||||
{
|
||||
@@ -1040,7 +1029,7 @@ namespace umbraco
|
||||
/// </summary>
|
||||
private XmlDocument LoadContentFromDiskCache()
|
||||
{
|
||||
lock (_readerWriterSyncLock)
|
||||
lock (ReaderWriterSyncLock)
|
||||
{
|
||||
var xmlDoc = new XmlDocument();
|
||||
LogHelper.Info<content>("Loading content from disk cache...");
|
||||
@@ -1106,7 +1095,7 @@ inner join cmsDocument on cmsDocument.nodeId = umbracoNode.id
|
||||
where cmsDocument.published = 1
|
||||
order by umbracoNode.level, umbracoNode.sortOrder";
|
||||
|
||||
lock (_dbReadSyncLock)
|
||||
lock (DbReadSyncLock)
|
||||
{
|
||||
using (
|
||||
IRecordsReader dr = SqlHelper.ExecuteReader(sql,
|
||||
@@ -1256,7 +1245,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
|
||||
/// <param name="xmlDoc"></param>
|
||||
internal void PersistXmlToFile(XmlDocument xmlDoc)
|
||||
{
|
||||
lock (_readerWriterSyncLock)
|
||||
lock (ReaderWriterSyncLock)
|
||||
{
|
||||
if (xmlDoc != null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user