Publisher does not support the Fluid field type. Please do not contact asking when support will be available.
If you purchased an add-on from expressionengine.com, be sure to visit boldminded.com/claim to add the license to your account here on boldminded.com.
Ticket: Cannot escape caching or figure out cache breaking
Status | Resolved |
Add-on / Version | Speedy 1.0.1 |
Severity | |
EE Version | 5.3.0 |
Jennifer Martin
Dec 05, 2019Hi Brian, per our discussion on Slack, I’m pretty sure this is just setup/config related, but I’m having two major problems I can’t work out. I’m using Redis and fragment caching, but with layouts, so using the global fragment in the layout and the fragment with key=“home” on the homepage template per the docs. The issues are:
1) Cannot escape caching from within the homepage template. It’ll be way easier to connect to FTP and look at site.group/.layout_master and pages.group/homepage to see the code, but the issue is the 3x beer cans on the homepage should all be randomized on every page load, pulling from one of the selected relationships within a Grid field. So I’m trying to escape the entire {home_beers} (Grid field) loop from the pages/homepage template starting on line 63, but it’s not working.
2) I can’t work out how to setup Cache Breaking Rules so that the caching clears when the entry is updated. This one is likely just a setup thing as I don’t know what to put in the ‘Clear Items’ tab of the Homepage Channel Settings. I’ve tried local/home (as home is my key of the fragment), I’ve tried just local/, local/homepage (which is the name of hte channel), local/{channel_name}... but nothing seems to work. I’m using Structure though, so traditional EE template routing doesn’t apply - could that be part of it?
Any input would be appreciated.
Thanks,
Justin
BoldMinded (Brian)
Dec 05, 2019
Comment has been marked private.
BoldMinded (Brian)
Dec 05, 2019
1) I don’t login to sites or servers unless it is a last resort. So can you post those template examples here?
2) local/home implies there is a URL matching site.com/home. So if you want to break the cache when the home page entry is updated, tags may be better. So add tags=“home” to your fragment tags in the home page template. Then in the cache break settings for the Home channel, just add “home” as one of the Clear Tags options. There was some missing code in the original release so tag based breaking wasn’t working, but the build in the previous comment should work. I tested manually, and unit tests confirms it should work now.
Jennifer Martin
Dec 06, 2019
Thanks for the response - I’ll test 1.0.2 first and try tags given the homepage has no segments, then supply template code if still needed.
Side-note A), both times I’ve tried ‘upgrading’ Speedy, I’ve gotten an Exception. Uninstalling and re-installing fixes it, but figured you should know - here’s the exception from going from 1.0.1 to 1.0.2:
Exception Caught
SQLSTATE[42000]: Syntax error or access violation: 1091 Can’t DROP ‘refresh_time’; check that column/key exists:
ALTER TABLE `exp_speedy_breaking` DROP `refresh_time`
ee/legacy/database/drivers/mysqli/mysqli_connection.php:117
Stack Trace: Please include when reporting this error
#0 ee/legacy/database/drivers/mysqli/mysqli_driver.php(112): CI_DB_mysqli_connection->query(‘ALTER TABLE `ex…’)
#1 ee/legacy/database/DB_driver.php(270): CI_DB_mysqli_driver->_execute(‘ALTER TABLE `ex…’)
#2 ee/legacy/database/DB_driver.php(180): CI_DB_driver->simple_query(‘ALTER TABLE `ex…’)
#3 ee/legacy/database/DB_forge.php(293): CI_DB_driver->query(‘ALTER TABLE `ex…’)
#4 user/addons/speedy/upd.speedy.php(131): CI_DB_forge->drop_column(‘speedy_breaking’, ‘refresh_time’)
#5 ee/EllisLab/ExpressionEngine/Controller/Addons/Addons.php(533): Speedy_upd->update(‘1.0.1’)
#6 [internal function]: EllisLab\ExpressionEngine\Controller\Addons\Addons->update(Array)
#7 ee/EllisLab/ExpressionEngine/Core/Core.php(241): call_user_func_array(Array, Array)
#8 ee/EllisLab/ExpressionEngine/Core/Core.php(110): EllisLab\ExpressionEngine\Core\Core->runController(Array)
#9 ee/EllisLab/ExpressionEngine/Boot/boot.php(151): EllisLab\ExpressionEngine\Core\Core->run(Object(EllisLab\ExpressionEngine\Core\Request))
#10 admin.php(147): require_once(’...’)
#10 admin.php(147): require_once(’...’)
Side-note B) the ‘reply’ email address in tickets still doesn’t seem to be working for CCing notifications. We use one company account (design@) for buying licences so they’re all together, which isn’t my personal email. Again just figured you should know.
Jennifer Martin
Dec 06, 2019
So even with 1.0.2, I can’t get either to work - it’s not escaping the fragments and I can’t get cache busting working with tags. I created a test ‘homepage’ that actually has a segment and even a stripped down version of the template to hopefully make it easier to review:
URL: https://dev.marblebrewery.com/test (use same UN/PW as the CP access for the .htpasswd authentication)
Goal 1: Get the beer can images to stay random on each page load
Goal 2: Automatically clear cached content (eg heading/subheading) on entry save
Layout Template (not stripped on the site, but stripped here for legibility):
Stripped Homepage template being used at https://dev.marblebrewery.com/test
{home_beers} is a Grid field and {image_pool} is a Relationships field, with multiple selected from which one is chosen at random on each page load.
If I drill down into the Items, I can drill down as far as local/test/item which has the created/expires dates etc, and has output code, but the images are in there, so not being escaped. The ‘Content’ of that item is here:
Do you see anything wrong? Is there anything else you need?
Thanks,
Justin
BoldMinded (Brian)
Dec 07, 2019
The contents inside the entries tag is getting parsed before the escape can capture the content. Still investigating.
BoldMinded (Brian)
Dec 07, 2019
Ok, so I’m not sure what you’re trying to do will work, and after looking into it I’m wondering why you want to try to cache an entries tag, but escape part of the custom fields that require the entries tag to render? I setup a test case locally with the following template (the layout is exactly the same as your example above) using the pre-escape method (just adding a unique modifier to the tag, e.g. :status, :content etc)
And the cached item for this template looks like this:
As you can see, the non entries tag variables logged_in and the conditional are properly escaped, but the {bloqs} custom field tag pair is not parsed, so it is actually escaped, but not parsed because there is no more entries tag to parse the field. Without using the pre-escape tag, the {bloqs} field pair is parsed and cached with the proper output. So escaping fields that require the entries tag to be parsed won’t work b/c regardless of the escape type (normal or pre-escape).
Does that clear things up?
Jennifer Martin
Dec 09, 2019
Hi Brian,
Thanks for the explanation, that does help me see why it’s not parsing. So what’s the most efficient solution to overcome this? Would it be to use multiple fragments inside the exp:channel:entries loop just for the fields that *can* be cached? Like this?
This does work (and makes sense why), but I just don’t know if it’s the most efficient way to achieve what I’m trying to do?
Also any thoughts on getting cache to clear on entry-save?
Thanks,
Justin
BoldMinded (Brian)
Dec 09, 2019
The cache is clearing on save. Did you switch to using tags? It definitely worked for me locally when I tried it.
It looks like you’re caching two different fragments, you should probably give them unique names using the key=”” parameter.
Jennifer Martin
Dec 09, 2019
Is the code in comment #7 the right way to do this? Ie, setup fragments inside the exp:channel:entries loop rather than trying to cache the whole thing and set escaped sections?
Regarding clearing cache on save, it’s got to be something setup-wise I’m missing because nothing I do seems to work - I’ve tried using just tags, just keys and a combination of both and still nothing. See https://imagizer.imageshack.com/img921/8281/zVGLUX.jpg for my ‘cache breaking rules’ setup and below are the several attempts that don’t work :-
Using just tags: In this case, the cached code for the ‘heading’ is actually repeated down below, which makes sense because it’s the same tag on the same URL, but is clearly not the right setup:
Using just keys: The different fragments cache separately and the caching appears to work fine, but they don’t update when I edit the entry (my testing is just changing the numbers in heading/subheading back and forth between 1/2/3):
Using both unique keys and tags: acts the same as just with keys - renders and appears to cache correctly, but still doesn’t clear when I edit the entry:
BoldMinded (Brian)
Dec 09, 2019
In my test, here is my item based cache breaking rule. I had to add “en” b/c I have Publisher running too. https://d.pr/i/LzDNFe
Then in my site’s default/index template, which is loaded when I go to the home page at site.com/, I have the following:
When I save an entry in the Pages channel, the cached item is cleared.
BoldMinded (Brian)
Dec 09, 2019
What exactly are you trying to do with homepage1 and homepage2 in your examples? Are you trying to pass the cached contents up to the site/.layout_master template?
Jennifer Martin
Dec 10, 2019
I’m trying to cache the entire page contents except for the page-specific layout variables that get passed up to .layout_master and the {home_beers} Grid field because in each row, we’re wanting to load a non-cached random selection from the {image_pool} Relationship field (one of the Grid columns) on page-load. The only reason I tried splitting it into homepage1 and homepage2 was because I can’t wrap the channel entries tags with my fragment and just escape that one Grid field (you explained why in comment #6)... and I tried have one fragment that’s *inside* the channel entries loop but then escape the {home_beers} field, but that doesn’t work either using either the regular or pre-escape methods. But I’m still not clear if splitting the content into two fragments is the right way to do it, but that’s the only thing I can find that actually works - this is what’s working now at https://dev.marblebrewery.com/test - the heading/subheading pull from the cache and the beer images render from the Grid field each time. But is this the right/best way to do this?
Regarding clearing on save, I’ve tried so many things and nothing works. Assuming using multiple fragments instead of one fragment with an escape is the right way to do this, I’m using the same code as you just without the url_prefix… and of course I have two unique keys for my two fragments (homepage1/homepage2) and I end up with two cache items under local/test: local/test/homepage1 and local/test/homepage2 - each one showing what I’d expect in the ‘Content’ when I drill all the way down to ‘View Item’. In the Cache Breaking Rules for this channel, I’ve tried local/homepage1 & local/homepage2 and also local/test/homepage1 & local/test/homepage2 but neither set works.
I have to imagine the template group and template name come into this… in your example, you’re using the default group and the index template for the homepage (which has no segements). In my case, the template I’m using here is ‘pages.group/homepage_stripped.html’ but I’m using Structure to assign the template, so routing is not the standard EE template routing. In my head, this has to work into it somehow, but I can’t work out how. Using Structure, the same template could possibly be used with any URL. I just can’t figure out what to put in the Cache Breaking Rules for my setup to get that to work. Are we sure using Structure isn’t interfering here?
BoldMinded (Brian)
Dec 10, 2019
I’m not sure if passing things up to the parent layout will work. There is a template parse order thing that does not allow it to work. It’s working with the {layout:contents} b/c it doesn’t use a {layout:set} tag for the contents, it just captures everything, so there are no module tag parse order issues to deal with.
Template name shouldn’t matter. If it is creating a cache item at local/test/homepage1, and you add that as an item in your channel settings, saving just clears all items matching that pattern. Have you tried it without Structure just to be certain it isn’t a Structure issue? I don’t think it is, but if you can create another test case to take Structure out of the equation it’ll help.
BoldMinded (Brian)
Dec 11, 2019
I think we’ve both been thinking about this the wrong way. Speedy doesn’t pass around cache items like Stash does, it needs to render cached content in the same tag it was created in. In this case, we don’t want to cache a {layout:set name=“foo”} variable where it is created… we want to cache it where the contents are output. This works for me:
Then in the layout file:
BoldMinded (Brian)
Dec 11, 2019
Actually, this appears to be working for me:
Which results in a cache item looking like this.
Layout:
Jennifer Martin
Dec 11, 2019
Hi Brian, I agree your latest code (comment #15) works for escaping the layout variables and keeping them unique per page as they’re outside the speedy:fragment in the layout - and this does solve that part of the problem.
But regarding wanted to exclude a single EE field from being in the cache (in my example, the Grid field that pulls a random beer can image every page-load), do you agree that my method of having two separate fragments (key=“homepage1” and key=“homepage2” from the very bottom of comment #9) is the best way to do that? I thought from the docs having just one fragment (still inside the channels entry loop) with an escape around that one field would work, but it doesn’t.
Finally, regarding the cache breaking rules and clearing cache on entry-save, I’ll have to make time to test it outside of Structure and I might not get to that until the new year. But in the meantime, you implied in comment #13 that I had to add ‘local/test/homepage1’ to the Cache Breaking Rules, but in my case, ‘test’ is the name of a page that theoretically the content editors could create themselves… so needing to manually add rules for every page that gets added isn’t going to be viable anyway. Which presumably is where the reference to including {channel_name} in the instructions comes in, but with Structure, {channel_name} definitely isn’t in a segment. For example, I added another test page underneath my original, so now with my two-fragment setup I have cache items that look like:
So the question is, how do I setup cache breaking rules to not be URL specific so pages the user adds themselves still get cached?
Thanks,
Justin
BoldMinded (Brian)
Dec 12, 2019
Sounds like you do need to use more than 1 fragment tag if you want to cache everything except the beer can image, but at that point I wonder if that entries tag is even really benefiting from caching, b/c the heavy lifting is done by the entries tag itself, not the custom fields.
I understand now what you’re saying about the cache breaking, and based on the nature of dynamic urls it sounds like you need to rely on tags, not paths to break the cache. In the channel’s cache breaking settings page just add a tag for “homepage”, or whatever you name it. Then when you load the page on the front-end you should see an entry in the exp_speedy_tags table with the key column being the url, and the tag column being the tag you used on the fragment template tag.
Jennifer Martin
Dec 12, 2019
Thanks Brian, I’ll keep that in mind about using inside a channel:entries loop, but it seems like it would still reduce the number of queries so it’s worth testing. And what you said about using tags instead also makes sense, but I just still can’t get it to work.
If I only use tags on each of my two fragments, but use the same tag, then the first fragment is duplicated and rendered twice, eg:
In the above example, the <h1> is rendered twice. Even if I update the tags to be unique (eg tags=“hoempage1” and tags=“homepage2”), I still get duplicated output and drilling down to View Items, I only get one cache item with the key “local/test/item” (which has the cached content from the first fragment).
In both cases, the cache still isn’t cleared on entry-save with “homepage”, “homepage1” and “homepage2” rows setup under Clear Tags for this channel.
If I add unique keys to each fragment, then the two fragments do cache separately, we get two cache items with keys “local/test/homepage1” and “local/test/homepage2” and the <h1> and <h3> are both rendered correctly, eg:
But the cache still doesn’t clear when I edit the entry. Again, I tried making the tags unique, eg:
but this didn’t help to clear cache on entry-save either.
Even if I strip out the 2nd fragment altogether and just have one fragment with a single tag, that still does not refresh when the entry is edited, eg:
so something is very odd here.
Finally, going back to previous discussions, I tried removing this stripped homepage template out of Strucutre and accessing it directly via traditional EE template routing and that still doesn’t work. This fragment stays cached and doesn’t clear when I update the {page_heading} field and re-save the entry:
So I’m at a total loss with clearing the cache on entry-save. Any ideas? Would it be beneficial to login to the site yourself and see it with your own eyes? The creds I added to the first ticket should still be valid.
Thanks,
Justin
BoldMinded (Brian)
Dec 12, 2019
I think you always need to add a key parameter to the fragments, otherwise it won’t cache. The htaccess login isn’t working, so I can’t login to the CP.
Jennifer Martin
Dec 13, 2019
Ok, always adding a key is fine, so long as we can get the tag caching working. Sorry about the authentication, not sure what happened there - I’ve disabled .htpasswd authentication for now.
BoldMinded (Brian)
Dec 13, 2019
Which template file has your fragment tag in it for the home page?
BoldMinded (Brian)
Dec 13, 2019
Also, have you tried a similar caching scenario on a different page other than the home page to see if it works?
Jennifer Martin
Dec 13, 2019
I’m just using the homepage channel for now, and even created a stripped down version for testing so https://dev.marblebrewery.com/test (and https://dev.marblebrewery.com/test/level2) is using the pages.group/homepage_stripped.html template
BoldMinded (Brian)
Dec 13, 2019
It looks like it is creating keys incorrectly b/c there is no url segment. E.g. speedy/default_site/local//item and speedy/default_site/local//home - see the double slashes?
Try adding url_override=“home” to the home page fragment tags to see what happens.
BoldMinded (Brian)
Dec 13, 2019
Also try a similar setup on a non-home page entry that has a normal url segment, but don’t add the url_override parameter to that one… only add it on the home page template.
Jennifer Martin
Dec 13, 2019
The double-slashes appears to only be from the actual homepage (which has no URL segments), so adding a url_override there does make sense and I’ve done that now and am seeing the correct ‘speedy/default_site/local/home/item’ record in exp_speedy_tags… but the rest of the records in that table for the other pages I was focusing on testing with my homepage_stripped template (ie https://dev.marblebrewery.com/test and https://dev.marblebrewery.com/test/level2) do seem correct.
Should the contents of the exp_speedy_tags table persist forever? I tried removing all Cache Breaking Rules entries, clearing ‘All Drivers’ cache but that table remains with all records, including the erroneous ones, but I’m not sure if that’s an issue or not.
Jennifer Martin
Dec 13, 2019
I finally got it to work! I had basically already done what you suggested with a similar non-home setup using a totally different template (pages/homepage_stripped) on pages with URLs with segments (https://dev.marblebrewery.com/test and https://dev.marblebrewery.com/test/level2).
But it looks like the errors in the exp_speedy_tags table was the problem. I went ahead and manually deleted all the old rows from exp_speedy_tags and that seemed to be the difference maker. So now with the fresh setup, it is working as expected. So here’s what I now have:
1) my actual homepage (https://dev.marblebrewery.com) using the pages/homepage template with only one fragment for now, but it has a unique key (‘home’) and the url_override setup and working
2) the two inner pages above both setup at different levels using the homepage_stripped template which has two fragments, each with a unique key (‘homepage1’ and ‘homepage2’), without url_override and relies on the URL to make up part of the key.
In the DB, the exp_speedy_tags table looks correct now:
and as I drill down under Redis to view each individual cache item, they look correct as well and editing an entry DOES now clear the cache! Huzzah!
So my question above I guess is the pertinent one now - about how those records in exp_speedy_tags persist even if Cache Breaking Rules and fragments in template are changed/removed. In my case, setting up a homepage fragment without the url_override param created a broken tag with the double-slashes and that seemed to keep all other cache-breaking rules from working.
Jennifer Martin
Dec 13, 2019
Actually, after further playing around with this, I found that toggling on the “Refresh Items” keeps the cache from updating on entry-save. So maybe that was the problem instead of the broken tag items in exp_speedy_tags. I’ve kept the Refresh Items toggle to ‘on’ because I presumed that meant that as cache was deleted, Speedy would go re-render those pages in order to re-generate the cache (so the cache would be refreshed before the next user requested that page…), but this may well have been the issue all along.
Should the Refresh Items toggle work when utilizing dynamic URLs via tags?
Jennifer Martin
Dec 13, 2019
PS, I did just notice that exp_speedy_tags did get wiped and regenerated, but I can’t figure out what I did to cause that. At this point, I’m thinking it’s been the Refresh Items toggle being on all this time that kept the clear-on-entry-save from working.
BoldMinded (Brian)
Dec 14, 2019
Glad you figured it out. I’ll take another look at the refresh option. The next release will have a page to clear tags, so you don’t have to do it manually in the database.