Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

General Questions /

General questions about getting started with SilverStripe that don't fit in any of the categories above.

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

Nested partial caching doesn't work


Go to End


9 Posts   2919 Views

Avatar
Rodskagg

Community Member, 26 Posts

7 April 2015 at 9:55pm

According to the documentation, it should be possible to have nested cache keys. Can't get this to work, however - the child caches are only updated when the parent cache is invalidated.

Example

This is my parent SS-file:

<% cached 'sections', ID, Sections.Max(LastEdited), Sections.count() %>
<% loop Sections %>
$Me
<% end_loop %>
<% end_cached %>

In Sections.ss I then have this (somewhat simplified)

<section>
<h1>$Title</h1>
$Now
<% cached 'blocks', ID, Blocks.Max(LastEdited), Blocks.count() %>
<% loop Blocks %>
<h2>$Title</h2>
$Now
<% end_loop %>
<% end_cached %>
</section>

I expect the $Now in the Blocks loop to be updated when a Block object is updated, but it isn't. It's only updated when the parent cache key is invalidated.

Avatar
HARVS1789UK

Community Member, 31 Posts

23 October 2015 at 4:00am

Edited: 23/10/2015 4:04am

I am also seeing this issue using SS 3.1.5

I have a cache block for my entire <header>, which is quite general, and a nested cache block for my navigation, which caches on a page by page basis, however the navigation remains the same across all pages (because the nested block is not causing the cache to regenerate on each page).

The documentation claims that nested cache blocks with higher specificity will be honoured (in fact I am sure I remember reading that this 'general' top level cache block with more specific nested blocks was the best practise and best performance)?

Avatar
martimiz

Forum Moderator, 1391 Posts

27 October 2015 at 11:19pm

@Rodskagg: The documentation HARVS1789UK is linking to, states the following

"Currently cached blocks can not be contained within if or loop blocks. The template engine will throw an error letting you know if you've done this. You can often get around this using aggregates."

It looks like your nested block is within the <% loop sections %> loop, so that may be the problem.

@HARVS1789UK: You might want to provide some code?

Avatar
HARVS1789UK

Community Member, 31 Posts

27 October 2015 at 11:33pm

@martimiz here is the contents of my Header.ss file:

<header class="row">
    <%--
        Regenerate cache whenever SiteConfig is updated and consider logged in vs
        logged out users
    --%>
    <% cached 'Header', List(SiteConfig).max(LastEdited), CurrentMember.exists() if useCache() %>
        <section id="header-ribbon" class="col-sm-12">
            <div class="container">
                <div class="row">  
                    <div class="col-xs-12">
                        <% if $SiteConfig.ConsumerURL || $SiteConfig.WholesaleURL %>
                            <ul>
                                <li>Switch To</li>
                                <% if $SiteConfig.ConsumerURL %>
                                    <li><a href="{$SiteConfig.ConsumerURL}" title="Go To Consumer Site" class="tab">Personal</a></li>
                                <% end_if %>
                                <% if $SiteConfig.WholesaleURL %>
                                    <li><a href="{$SiteConfig.WholesaleURL}" title="Go To Wholesale Site" class="tab">Business</a></li>
                                <% end_if %>
                            </ul>
                        <% end_if %>

                        <% if $SiteConfig.MyAccountPage %>
                            <span id="login-options">
                                <% if $CurrentMember %>
                                    <a href="Security/logout" title="Log Out" class="button">
                                        {$SVG('login-icon')}
                                        Log Out
                                    </a>
                                <% else %>
                                    <a href="{$SiteConfig.MyAccountPage.Link()}" title="Log In" class="button">
                                        {$SVG('login-icon')}
                                        Log In
                                    </a>  
                                <% end_if %>
                            </span>
                        <% end_if %>
                    </div>
                </div>

            </div>
        </section>
        <section id="header-top" class="col-xs-6 col-sm-12">
            <div class="container">
                <div class="row">
                    <div class="col-sm-3">
                        <div class="logo">
                            <a href="/" title="Home | {$SiteConfig.Title}">
                                <img src="/themes/2015-redesign/images/sure-international-logo.png" alt="{$SiteConfig.Title} Logo" />
                            </a>
                        </div>
                    </div>
                    <div class="col-sm-9 hidden-xs">
                        <%--
                            SecurityID fields in forms mean they should not be cached
                        --%>
                        <% uncached %>
                            $SearchForm
                        <% end_uncached %>
                    </div>
                </div>
            </div>
        </section>

        <% include Navigation %>

        <% include Carousel _Width=1280, _Height=313 %>

    <% end_cached %>

</header>

and here is the contents of my Navigation.ss include (which contains the nested cache block)

<%--
    Cache the entire navigation on an page by page basis and regenerate the cache
    whenever any page is added, deleted or edited in the CMS or the SiteConfig is updated
--%>
<% cached 'Nav', PageCacheKey, List(Page).max(LastEdited), List(Page).count(), List(SiteConfig).max(LastEdited) if useCache() %>
    <nav id="header-nav" class="col-xs-6 col-sm-12">
        <div class="container">
            <div class="row">
                <div class="col-sm-12">
                    <div class="visible-xs">
                        <button id="navbar-toggle" toggle-status="closed">
                        <span toggle-status="closed">
                            {$SVG('menu-dropdown')}
                        </span>
                        <span toggle-status="open">
                            {$SVG('cross')}
                        </span>
                        </button>
                    </div>
                    <div id="primary-navigation">
                        <ul class="clearfix">
                            <% loop $Menu(1) %>
                                <% if $Children() %>
                                    <li class="{$LinkingMode} parent">
                                        <a href="#" title="{$Title} | {$SiteConfig.Title}">
                                            $MenuTitle 
                                            <span class="visible-xs">{$SVG('arrow-dropdown')}</span>
                                        </a>
                                        <ul>
                                            <% loop $Children() %>
                                                <li class="{$LinkingMode}">
                                                    <a href="{$Link}" title="{$Title} | {$SiteConfig.Title}">$MenuTitle</a>
                                                </li>
                                            <% end_loop %>
                                        </ul>
                                    </li>
                                <% else %>
                                    <li class="{$LinkingMode}">
                                        <a href="{$Link}" title="{$Title} | {$SiteConfig.Title}">$MenuTitle</a>
                                        <ul class="empty-list">
                                            <li>
                                                <a></a>
                                            </li>
                                        </ul>
                                    </li>
                                <% end_if %>
                            <% end_loop %>
                        </ul>
                    </div>
                </div>
            </div>
        </div>
    </nav>
<% end_cached %>

Unless im mistaken the nested block is not within an if or a loop? Are there also restrictions on using nested cache blocks within includes which is not documented?

Avatar
HARVS1789UK

Community Member, 31 Posts

3 March 2016 at 9:59am

Bump

@martimiz I have just gone to use Silverstripe's partial caching again in another project running v3.1.18 and I am still getting this issue. I seem to be able to replicate it without the use of any <% include %> logic as well.

From what I am seeing it is like nested caching is not possible at all?

Avatar
HARVS1789UK

Community Member, 31 Posts

7 March 2016 at 10:38pm

Edited: 07/03/2016 10:45pm

For the benefit of anyone else struggling ith the same issue who come across this thread, I have done a little more research and identified two seperate issues which I needed to resolve in order to get partial caching working as I expected.

1) The cache block parser considers <% base_tag %> to be an open control block (even though it has no closing block) therefore if nest <% base_tag %> within a <% cache %> block then any additional nested <% cache %> or <% uncached %> blocks will return errors. The solution is either A) Never wrap <% base_tag %> in a <% cache %> block B) Continue to include the <% base_tag %> within a larger <% cache %> block BUT ensure it is directly wrapped in an <% uncached %> block C) If you still want to cache the <base> tag then do not use <% base_tag %> but instead use you own markup i.e.

<base href="{$Director.absoluteBaseURL()}"><!--[if lte IE 6]></base><![endif]-->

2) Nested <% cache %> blocks, with higher specificity, within <% include %> (or $Layout) will no be honoured. <% include %> (and $Layout) blocks are parsed/rendered using a new instance of SS_Viewer which has no knowledge of the existing SS_Vierwer instance (i.e. the one being used to parse/render Page.ss) and therefore, no knowledge of the fact it is within a parent <% cache %> block. The solution is to wrap all of you <% include %> blocks in <% uncached %> and then resume your caching within the <% include %> itself. This will not work:

templates/Page.ss

<% cached 'Page', $getPageCacheKey() if $useCache() %>
<body>
    
    <h1>My Page</h1>

    <% include Header %>

</body>
<% end_cached %>

templates/Includes/Header.ss

<header>
    <h2>$Subtitle</h2>
    <div class="usp-bar">
        <div class="container">
            <div class="row">
                <% cached 'Header', $gerMoreSpecificCacheKey() if $useCache() %>
                <div class="col-sm-4"><p>$someMethodA()</p></div>
                <div class="col-sm-4"><p>$someMethodB()</p></div>
                <div class="col-sm-4"><p>$someMethodC()</p></div>
                <% end_cached %>
            </div>
        </div>
    </div>
</header>

As the cache key in Header.ss with higher specificity than the generic cache key in Page.ss will not be honoured and the Header.ss cache block will become stale. However this will work:

templates/Page.ss

<% cached 'Page', $getPageCacheKey() if $useCache() %>

<body>
    
    <h1>My Page</h1>

    <% include Header %>

</body>

<% end_cached %>

templates/Includes/Header.ss

<% cached 'Page', $getPageCacheKey() if $useCache() %>
<header>
    <h2>$Subtitle</h2>
    <div class="usp-bar">
        <div class="container">
            <div class="row">
                <% cached 'Header', $gerMoreSpecificCacheKey() if $useCache() %>
                <div class="col-sm-4"><p>$someMethodA()</p></div>
                <div class="col-sm-4"><p>$someMethodB()</p></div>
                <div class="col-sm-4"><p>$someMethodC()</p></div>
                <% end_cached %>
            </div>
        </div>
    </div>
</header>
<% end_cached %>

Avatar
martimiz

Forum Moderator, 1391 Posts

7 March 2016 at 11:54pm

Thanks for delving so deep and sharing this!

I know I had looked into the problem with caching include blocks once, and posted about it somewhere, searched again on friday, but just can't find it :(

But it looks like you found out even more! To me the thing with the base tag not being closed, would border on being a bug - maybe it should be reported as an issue on github, do you think?

Avatar
HARVS1789UK

Community Member, 31 Posts

8 March 2016 at 12:31am

I would agree, so I have replicated it in a fresh install of SS v3.3.1 and raised an issue in GitHub - https://github.com/silverstripe/silverstripe-framework/issues/5150

Go to Top