Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Joe 32 posts 136 karma points
    Dec 13, 2020 @ 14:35
    Joe
    0

    PageCacheRefresher missing from Cache instruction when Publishing on background job without HttpContext

    Hi, I have a difficulty about cache instruction (and HttpContext.Current) in Master-Slave distributed web-servers environment that needs help.

    I have a content which act as a template-like content, imagine it as a classroom content with a few demo device contents as its children. So it looks like:

    • Sample Room
      • Default device A
      • Default device B

    Everytime the user create a new room, they will copy from the Sample Room. By-default the copy function won't auto-publish the content and its decendents. So a custom eventHandler is added to automate the publish of whole tree. Originally it's fine with version below:

    public class RoomEventHandler : ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            Umbraco.Core.Services.ContentService.Copied += ContentService_Copied;
    
        }
    
        private void ContentService_Copied(IContentService contentService, CopyEventArgs<IContent> e)
        {
            var content = e.Copy;
            ApplicationContext.Current.Services.ContentService.Publish(content);
            if (content.ContentType.Alias == Room.ModelTypeAlias)
            {
                int contentId = content.Id;
                var publishStatus = ApplicationContext.Current.Services.ContentService.PublishWithChildrenWithStatus(content, userId: 0, includeUnpublished: true);
            }
        }
    }
    

    The version above handle everything within same thread / Http Request, so it's okay.

    However, after years, there're more contents in the database, making such publish process very slow. To avoid user from long waiting on screen, I splited the autp-publish process of the PublishWithChildrenWithStatus to another background thread by HostingEnvironment.QueueBackgroundWorkItem as below:

    public class RoomEventHandler : ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            Umbraco.Core.Services.ContentService.Copied += ContentService_Copied;
    
        }
    
        private void ContentService_Copied(IContentService contentService, CopyEventArgs<IContent> e)
        {
            var content = e.Copy;
            ApplicationContext.Current.Services.ContentService.Publish(content);
            if (content.ContentType.Alias == Room.ModelTypeAlias)
            {
                int contentId = content.Id;
                HostingEnvironment.QueueBackgroundWorkItem(ct =>
                {
                    var publishStatus = ApplicationContext.Current.Services.ContentService.PublishWithChildrenWithStatus(content, userId: 0, includeUnpublished: true);
                });
            }
        }
    }
    

    After encapsulating PublishWithChildrenWithStatus by HostingEnvironment.QueueBackgroundWorkItem , it's executed with another thread without HttpContext.Current (i.e. without a Http Request).

    Under this condition, although all contents are changed to Published status in Master server CMS, the XmlCache was not refreshed at Slave server. After tracing, it's found that the PageCacheRefresher instructions are not inserted to DB table [umbracoCacheInstruction]; while there should be a PageCacheRefresher entry of all published content IDs auto-inserted by Umbraco (It's the behaviour found by firing PublishWithChildren request via API). With same function PublishWithChildrenWithStatus called, the only difference is with vs without HttpContext.Current.

    The closest issue log (which is "fixed") is https://issues.umbraco.org/issue/U4-7129. But It seems not covering the case above.

    An alternative I imagined is moving such work to a cusotm API, and then in the background job, fire a http request to this API. But it looks like an indirect approach. I guess there may be more decent and appropriate way to do it?

    Can anyone help?

Please Sign in or register to post replies

Write your reply to:

Draft