Skip to content

Commit 859fbaa

Browse files
committed
Fixes: U4-5121 umbraco.content launches threadpool threads to reload the xml cache which causes lots of other issues
Conflicts: src/Umbraco.Web/umbraco.presentation/content.cs
1 parent 7299133 commit 859fbaa

File tree

2 files changed

+51
-164
lines changed

2 files changed

+51
-164
lines changed

src/Umbraco.Web/UmbracoModule.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,6 @@ public void Init(HttpApplication app)
498498
app.BeginRequest += (sender, e) =>
499499
{
500500
var httpContext = ((HttpApplication)sender).Context;
501-
httpContext.Trace.Write("UmbracoModule", "Umbraco request begins");
502501
LogHelper.Debug<UmbracoModule>("Begin request: {0}.", () => httpContext.Request.Url);
503502
BeginRequest(new HttpContextWrapper(httpContext));
504503
};
@@ -523,9 +522,8 @@ public void Init(HttpApplication app)
523522
var httpContext = ((HttpApplication)sender).Context;
524523
if (UmbracoContext.Current != null && UmbracoContext.Current.IsFrontEndUmbracoRequest)
525524
{
526-
//write the trace output for diagnostics at the end of the request
527-
httpContext.Trace.Write("UmbracoModule", "Umbraco request completed");
528-
LogHelper.Debug<UmbracoModule>("Total milliseconds for umbraco request to process: " + DateTime.Now.Subtract(UmbracoContext.Current.ObjectCreated).TotalMilliseconds);
525+
LogHelper.Debug<UmbracoModule>(
526+
"Total milliseconds for umbraco request to process: {0}", () => DateTime.Now.Subtract(UmbracoContext.Current.ObjectCreated).TotalMilliseconds);
529527
}
530528

531529
OnEndRequest(new EventArgs());

src/Umbraco.Web/umbraco.presentation/content.cs

Lines changed: 49 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ private bool CheckXmlContentPopulation()
206206
FireAfterRefreshContent(new RefreshContentEventArgs());
207207

208208
// Only save new XML cache to disk if we just repopulated it
209-
// TODO: Re-architect this so that a call to this method doesn't invoke a new thread for saving disk cache
210209
if (!UmbracoSettings.isXmlContentCacheDisabled && !IsValidDiskCachePresent())
211210
{
212211
QueueXmlForPersistence();
@@ -215,7 +214,9 @@ private bool CheckXmlContentPopulation()
215214
}
216215
}
217216
}
218-
Trace.WriteLine("Content initialized (was already in context)");
217+
218+
LogHelper.Debug<content>(() => "Content initialized (was already in context)");
219+
219220
return false;
220221
}
221222

@@ -288,32 +289,33 @@ public delegate void ContentCacheDatabaseLoadXmlStringEventHandler(
288289

289290
#endregion
290291

292+
[Obsolete("This is no longer used and will be removed in future versions, if you use this method it will not refresh 'async' it will perform the refresh on the current thread which is how it should be doing it")]
293+
public virtual void RefreshContentFromDatabaseAsync()
294+
{
295+
RefreshContentFromDatabase();
296+
}
297+
291298
/// <summary>
292-
/// Load content from database in a background thread
293-
/// Replaces active content when done.
299+
/// Load content from database and replaces active content when done.
294300
/// </summary>
295-
public virtual void RefreshContentFromDatabaseAsync()
301+
public virtual void RefreshContentFromDatabase()
296302
{
297303
var e = new RefreshContentEventArgs();
298304
FireBeforeRefreshContent(e);
299305

300306
if (!e.Cancel)
301307
{
302-
ThreadPool.QueueUserWorkItem(
303-
delegate
304-
{
305-
XmlDocument xmlDoc = LoadContentFromDatabase();
306-
XmlContentInternal = xmlDoc;
308+
XmlDocument xmlDoc = LoadContentFromDatabase();
309+
XmlContentInternal = xmlDoc;
307310

308-
// It is correct to manually call PersistXmlToFile here event though the setter of XmlContentInternal
309-
// queues this up, because this delegate is executing on a different thread and may complete
310-
// after the request which invoked it (which would normally persist the file on completion)
311-
// So we are responsible for ensuring the content is persisted in this case.
311+
// It is correct to manually call PersistXmlToFile here event though the setter of XmlContentInternal
312+
// queues this up, because this delegate is executing on a different thread and may complete
313+
// after the request which invoked it (which would normally persist the file on completion)
314+
// So we are responsible for ensuring the content is persisted in this case.
312315
if (!UmbracoSettings.isXmlContentCacheDisabled && UmbracoSettings.continouslyUpdateXmlDiskCache)
313-
PersistXmlToFile(xmlDoc);
314-
});
315-
316-
FireAfterRefreshContent(e);
316+
{
317+
PersistXmlToFile(xmlDoc);
318+
}
317319
}
318320
}
319321

@@ -551,33 +553,15 @@ public virtual void UpdateDocumentCache(List<Document> Documents)
551553
}
552554
}
553555

554-
/// <summary>
555-
/// Updates the document cache async.
556-
/// </summary>
557-
/// <param name="documentId">The document id.</param>
558556
[Obsolete("Method obsolete in version 4.1 and later, please use UpdateDocumentCache", true)]
559557
public virtual void UpdateDocumentCacheAsync(int documentId)
560558
{
561-
//SD: WE've obsoleted this but then didn't make it call the method it should! So we've just
562-
// left a bug behind...???? ARGH.
563-
//.... changed now.
564-
//ThreadPool.QueueUserWorkItem(delegate { UpdateDocumentCache(documentId); });
565-
566559
UpdateDocumentCache(documentId);
567560
}
568-
569-
/// <summary>
570-
/// Clears the document cache async.
571-
/// </summary>
572-
/// <param name="documentId">The document id.</param>
561+
573562
[Obsolete("Method obsolete in version 4.1 and later, please use ClearDocumentCache", true)]
574563
public virtual void ClearDocumentCacheAsync(int documentId)
575564
{
576-
//SD: WE've obsoleted this but then didn't make it call the method it should! So we've just
577-
// left a bug behind...???? ARGH.
578-
//.... changed now.
579-
//ThreadPool.QueueUserWorkItem(delegate { ClearDocumentCache(documentId); });
580-
581565
ClearDocumentCache(documentId);
582566
}
583567

@@ -652,69 +636,6 @@ public virtual void UnPublishNode(int documentId)
652636
ClearDocumentCache(documentId);
653637
}
654638

655-
///// <summary>
656-
///// Uns the publish node async.
657-
///// </summary>
658-
///// <param name="documentId">The document id.</param>
659-
//[Obsolete("Please use: umbraco.content.ClearDocumentCacheAsync", true)]
660-
//public virtual void UnPublishNodeAsync(int documentId)
661-
//{
662-
663-
// ThreadPool.QueueUserWorkItem(delegate { ClearDocumentCache(documentId); });
664-
//}
665-
666-
667-
///// <summary>
668-
///// Legacy method - you should use the overloaded publishnode(document d) method whenever possible
669-
///// </summary>
670-
///// <param name="documentId"></param>
671-
//[Obsolete("Please use: umbraco.content.UpdateDocumentCache", true)]
672-
//public virtual void PublishNode(int documentId)
673-
//{
674-
675-
// // Get the document
676-
// var d = new Document(documentId);
677-
// PublishNode(d);
678-
//}
679-
680-
681-
///// <summary>
682-
///// Publishes the node async.
683-
///// </summary>
684-
///// <param name="documentId">The document id.</param>
685-
//[Obsolete("Please use: umbraco.content.UpdateDocumentCacheAsync", true)]
686-
//public virtual void PublishNodeAsync(int documentId)
687-
//{
688-
689-
// UpdateDocumentCacheAsync(documentId);
690-
//}
691-
692-
693-
///// <summary>
694-
///// Publishes the node.
695-
///// </summary>
696-
///// <param name="Documents">The documents.</param>
697-
//[Obsolete("Please use: umbraco.content.UpdateDocumentCache", true)]
698-
//public virtual void PublishNode(List<Document> Documents)
699-
//{
700-
701-
// UpdateDocumentCache(Documents);
702-
//}
703-
704-
705-
706-
///// <summary>
707-
///// Publishes the node.
708-
///// </summary>
709-
///// <param name="d">The document.</param>
710-
//[Obsolete("Please use: umbraco.content.UpdateDocumentCache", true)]
711-
//public virtual void PublishNode(Document d)
712-
//{
713-
714-
// UpdateDocumentCache(d);
715-
//}
716-
717-
718639
/// <summary>
719640
/// Occurs when [before document cache update].
720641
/// </summary>
@@ -932,9 +853,10 @@ internal bool IsXmlQueuedForPersistenceToFile
932853
RemoveXmlFilePersistenceQueue();
933854
}
934855
}
935-
catch
856+
catch (Exception ex)
936857
{
937858
// Nothing to catch here - we'll just persist
859+
LogHelper.Error<content>("An error occurred checking if xml file is queued for persistence", ex);
938860
}
939861
}
940862
}
@@ -1036,22 +958,8 @@ private static void InitContentDocument(XmlDocument xmlDoc, string dtd)
1036958
/// </summary>
1037959
private XmlDocument LoadContentFromDatabase()
1038960
{
1039-
// Alex N - 2010 06 - Very generic try-catch simply because at the moment, unfortunately, this method gets called inside a ThreadPool thread
1040-
// and we need to guarantee it won't tear down the app pool by throwing an unhandled exception
1041961
try
1042962
{
1043-
// Moved User to a local variable - why are we causing user 0 to load from the DB though?
1044-
// Alex N 20100212
1045-
User staticUser = null;
1046-
try
1047-
{
1048-
staticUser = User.GetCurrent(); //User.GetUser(0);
1049-
}
1050-
catch
1051-
{
1052-
/* We don't care later if the staticUser is null */
1053-
}
1054-
1055963
// Try to log to the DB
1056964
LogHelper.Info<content>("Loading content from database...");
1057965

@@ -1235,19 +1143,10 @@ internal void PersistXmlToFile(XmlDocument xmlDoc)
12351143
{
12361144
if (xmlDoc != null)
12371145
{
1238-
Trace.Write(string.Format("Saving content to disk on thread '{0}' (Threadpool? {1})",
1239-
Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread));
1240-
1241-
// Moved the user into a variable and avoided it throwing an error if one can't be loaded (e.g. empty / corrupt db on initial install)
1242-
User staticUser = null;
1243-
try
1244-
{
1245-
staticUser = User.GetCurrent();
1246-
}
1247-
catch
1248-
{
1249-
}
1250-
1146+
LogHelper.Debug<content>("Saving content to disk on thread '{0}' (Threadpool? {1})",
1147+
() => Thread.CurrentThread.Name,
1148+
() => Thread.CurrentThread.IsThreadPoolThread);
1149+
12511150
try
12521151
{
12531152
Stopwatch stopWatch = Stopwatch.StartNew();
@@ -1263,23 +1162,20 @@ internal void PersistXmlToFile(XmlDocument xmlDoc)
12631162
}
12641163

12651164
xmlDoc.Save(UmbracoXmlDiskCacheFileName);
1266-
1267-
Trace.Write(string.Format("Saved content on thread '{0}' in {1} (Threadpool? {2})",
1268-
Thread.CurrentThread.Name, stopWatch.Elapsed,
1269-
Thread.CurrentThread.IsThreadPoolThread));
1270-
1271-
LogHelper.Debug<content>(string.Format("Xml saved in {0}", stopWatch.Elapsed));
1165+
1166+
LogHelper.Debug<content>("Saved content on thread '{0}' in {1} (Threadpool? {2})",
1167+
() => Thread.CurrentThread.Name,
1168+
() => stopWatch.Elapsed,
1169+
() => Thread.CurrentThread.IsThreadPoolThread);
12721170
}
12731171
catch (Exception ee)
12741172
{
12751173
// If for whatever reason something goes wrong here, invalidate disk cache
12761174
DeleteXmlCache();
1277-
1278-
Trace.Write(string.Format(
1175+
1176+
LogHelper.Error<content>(string.Format(
12791177
"Error saving content on thread '{0}' due to '{1}' (Threadpool? {2})",
1280-
Thread.CurrentThread.Name, ee.Message, Thread.CurrentThread.IsThreadPoolThread));
1281-
1282-
LogHelper.Error<content>("Xml wasn't saved", ee);
1178+
Thread.CurrentThread.Name, ee.Message, Thread.CurrentThread.IsThreadPoolThread), ee);
12831179
}
12841180
}
12851181
}
@@ -1288,36 +1184,29 @@ internal void PersistXmlToFile(XmlDocument xmlDoc)
12881184
/// <summary>
12891185
/// Marks a flag in the HttpContext so that, upon page execution completion, the Xml cache will
12901186
/// get persisted to disk. Ensure this method is only called from a thread executing a page request
1291-
/// since umbraco.presentation.requestModule is the only monitor of this flag and is responsible
1187+
/// since UmbracoModule is the only monitor of this flag and is responsible
12921188
/// for enacting the persistence at the PostRequestHandlerExecute stage of the page lifecycle.
12931189
/// </summary>
12941190
private void QueueXmlForPersistence()
12951191
{
1296-
/* Alex Norcliffe 2010 06 03 - removing all launching of ThreadPool threads, instead we just
1297-
* flag on the context that the Xml should be saved and an event in the requestModule
1298-
* will check for this and call PersistXmlToFile() if necessary */
1192+
//if this is called outside a web request we cannot queue it.
1193+
12991194
if (HttpContext.Current != null)
13001195
{
13011196
HttpContext.Current.Application.Lock();
1302-
if (HttpContext.Current.Application[PersistenceFlagContextKey] != null)
1303-
HttpContext.Current.Application.Add(PersistenceFlagContextKey, null);
1304-
HttpContext.Current.Application[PersistenceFlagContextKey] = DateTime.UtcNow;
1305-
HttpContext.Current.Application.UnLock();
1306-
}
1307-
else
1308-
{
1309-
//// Save copy of content
1310-
if (UmbracoSettings.CloneXmlCacheOnPublish)
1197+
try
13111198
{
1312-
XmlDocument xmlContentCopy = CloneXmlDoc(_xmlContent);
1313-
1314-
ThreadPool.QueueUserWorkItem(
1315-
delegate { PersistXmlToFile(xmlContentCopy); });
1199+
if (HttpContext.Current.Application[PersistenceFlagContextKey] != null)
1200+
{
1201+
HttpContext.Current.Application.Add(PersistenceFlagContextKey, null);
1202+
}
1203+
HttpContext.Current.Application[PersistenceFlagContextKey] = DateTime.UtcNow;
13161204
}
1317-
else
1318-
ThreadPool.QueueUserWorkItem(
1319-
delegate { PersistXmlToFile(); });
1320-
}
1205+
finally
1206+
{
1207+
HttpContext.Current.Application.UnLock();
1208+
}
1209+
}
13211210
}
13221211

13231212
internal DateTime GetCacheFileUpdateTime()

0 commit comments

Comments
 (0)