Jump to content
u-nikos

MarkupCache improvements

Recommended Posts

Hey all,

I think it would be nice if the MarkupCache module could automatically expire specific caches when specific templates or pages are saved. This would allow us to cache heavy parts of a page for a very long time (or forever), and it'll only need to re-create the heavy part if its content has changed. It would also make sure no out-of-date content is shown.

Advantages over the normal page cache are:

- The normal page cache needs to re-render the whole page, this only needs to re-render the part that has changed (performance).
- The normal page cache does not work with CSRF forms.

I've coded a little proof-of-concept and I would like to hear if you guys have any suggestions or improvements. It works as following:

Say you want to cache a news slider on the homepage:
 


<?php if ( ! $data = $cache->get("HomeNewsSlider", 604800)) {

// ... generate markup which uses the news-item template in $data ...

$cache->save($data);

}

echo $data; ?>


Then u would somehow configure the MarkupCache to expire the "HomeNewsSlider" cache when a page has been saved with the "news-item" template:

fn7dp4.jpg

This is how I have implemented it at the moment, but maybe someone has a better idea?

I've also added a more convenient method (the begin() and end() methods) for caching markup:


<?php if ($cache->begin("HomeNewsSlider", 604800)): ?>

<h1><?=$page->title?></h1>

// ... some other markup ...

<?php $cache->end(); endif; ?>


The module will start buffering the output when calling begin() and it'll save the buffer to the cache when calling end(). If the cache is available the begin() method will output the cached data. This way you wouldn't need to render all output to a $data variable.

Hope u guys have some feedback or other/better ideas :)

MarkupCache.module

  • Like 3

Share this post


Link to post
Share on other sites

That's some nice ideas there. I'm sure this will come in handy and would make markup caching more powerful. I like the idea to have a module to expire them by rules. Though I don't understand those rules, are they somehow to define the cache and pages that when saved will expire something? Just to understand it correctly.

1name (cache)

1pages (pages)

2name

2pages

...

Also the alternative way to create the cache is nice idea. Keep going :)

Share this post


Link to post
Share on other sites

Yeah the configuration is a bit vague at the moment, but it works like this:

The first line (uniqueName:) specifies the markup cache that should be expired when a page is saved that matches the selector on the second line (pageSelector:).

Rule 1 (cache name or cache name regex)

Rule 1 (page selector)

Rule 2 (cache name or cache name regex)

Rule 2 (page selector)

...

If someone has a better idea then I would love to hear it. This is just one way to do it :)

Share this post


Link to post
Share on other sites

Nice job u-nikos, I like your updates. The begin/end stuff is particularly creative and cool. I would like to add this to MarkupCache. Personally, I am always populating variables anyway, so the current markupcache method works for me. But I know others prefer to have their base in HTML rather than PHP, and this will be a lot better for them in particular.

The idea of setting those rules is also great. Though I'm also a little confused by the definition, even after you've explained it further. I think having rules that span two lines may be in part what's confusing me. Is there a way to do it all on one line, maybe something like this?

what page is saved? name or regex of cache to expire

Example 1: When page's using basic-page as template are saved, clear the SubNavCache

template=basic-page? SubNavCache

Example 2: When a page with id or parent_id of 1 is saved, clear the TopNavCache

id|parent_id=1? TopNavCache

Example 3: When page with template section is saved, clear all the caches

template=section? *

Does this do the same thing that you were suggesting or is there more to it?

Share this post


Link to post
Share on other sites

I'm glad u like it! The begin/end concept is something I remembered from working with the Yii Framework (very nice framework btw, with lots of cool concepts) so I thought: let's integrate that ;)

For the second part:

I tried to put the rules on one line but I wasn't sure if I could get the parsing (what is the selector part, what is the name part and is the name a regex pattern?) right. It would be possible to check if the name begins with a non-alphanumeric, non-backslash, non-whitespace character (aka delimiter) to determine if it's a regex or not (and I'm oké with that), but if someone decides to begin a cache name with a "!" character (or similar) the rule would break. I also don't know if selectors can contain question marks, but if that's not the case and you're okay with the delimiter thing, then one rule per line would definitely be better.

Share this post


Link to post
Share on other sites

I would be superhappy if there was possible to group multiple cache parts

You'd have something like:

  • nav.top.cache1
  • nav.top.cache2
  • nav.two.cache1
  • nav.two.cache2

And then you could delete nav.two.* and it would remove all related parts (in my example it's 3 and 4). That would be awesome.

Share this post


Link to post
Share on other sites

That's exactly what the regex functionality is for. The regex /^nav.two.(.*)/ would remove all related parts.

This can be useful (for example) when caching the top navigation per page. Because the markup of the the top navigation is different for each page (because of the current/parent classnames) you could cache it per page with $cache->get("TopNavigation_" . $page->id). Then you could expire all all TopNavigation caches with the /^TopNavigation_(.*)/ regex when needed. It would also allow us to cache things for specific users $cache->get("TopNavigation_" . $user->id) or even $cache->get("TopNavigation_" . $user->id . "_" . $page->id).

  • Like 2

Share this post


Link to post
Share on other sites
I tried to put the rules on one line but I wasn't sure if I could get the parsing (what is the selector part, what is the name part and is the name a regex pattern?) right.

I don't think there's any need for selectors to contain question marks here. While it's feasible someone may use one for performing an exact search on something like "title=why eat pastured eggs?", I really don't think that's a likely selector need in this context. I think we can safely not worry about question marks in selectors here.

For separating selector from the name/regex, I was initially thinking just an explode('?', $line); but that won't work since regex's often contain question marks. But something like this should work:

$pos = strpos($line, '?'); 
$selector = substr($line, 0, $pos); 
$line = trim(substr($line, $pos+1)); // reduce line to the part after the ?
if(substr($line, 0, 1) == '/') {
 // if it starts with a slash (delimiter) we assume regex
 $regex = $name; 
 $name = '';
} else {
 // otherwise we assume cache name
 $regex = '';
 $name = $line;
}

The test to determine whether it's a regex or a name would just be to see if the first character us a slash "/" (the most common PCRE delimiter). So the way for the user to say they want something to be a regex would be the same way they'd do it elsewhere, which is to start and end it with a delimiter. Though in this case, we'd limit them to "/" as the delimiter, which seems like a perfectly fine limitation here to me.

  • Like 1

Share this post


Link to post
Share on other sites

I'm fine with those limitations/requirements and would be more than happy if u could integrate this functionality into the MarkupCache module :)

Edit:

Maybe it's an idea to switch the name and the selector? Think it would be easier to identify a rule if the name comes first. Especially if you have a bunch of them.

Something like:

TopNavigation "template=basic-page"

HomeNewsSlider "template=news-item"

Or:

TopNavigation => template=basic-page

HomeNewsSlider => template=news-item

Some extra rows for the textarea field would also be nice. 5 rows seems to be a bit tiny.

Share this post


Link to post
Share on other sites
TopNavigation "template=basic-page"
HomeNewsSlider "template=news-item"

I think this seems like a nice format too. I guess what I was trying to get at with the format I recommended was the familiar "if => then" statement. "If the saved page matches this, then clear this cache." I start to have a little trouble when the logic gets to: "If the cache is this and if the saved page matches this selector, clear it." 

Share this post


Link to post
Share on other sites
I think this seems like a nice format too. I guess what I was trying to get at with the format I recommended was the familiar "if => then" statement. "If the saved page matches this, then clear this cache." I start to have a little trouble when the logic gets to: "If the cache is this and if the saved page matches this selector, clear it." 

Maybe u could say it like:

Clear this cache "if a page is saved that matches this selector"

But the main reason is that I think it would be easier to find a specific rule if all cache names are aligned at the left side :)

Share this post


Link to post
Share on other sites
But the main reason is that I think it would be easier to find a specific rule if all cache names are aligned at the left side

That makes sense. Perhaps what we really need are rows of two inputs each. Though that might be more trouble than it's worth in this case. 

Share this post


Link to post
Share on other sites

Ryan: any chance that this might get into 2.3? I'm kind of wrapping up one of my modules for 1.0, and this would make it super easy (well… easier) to implement waterfall partial caching for me (and speed stuff up).

Share this post


Link to post
Share on other sites

Adam, I'm working through one too many modules at once here so haven't had the chance to go through this one yet. I'm sure we'll get it into 2.3, but not positive we'll be able to get it into the first revision or not. But the version he linked above does work, if I understand correctly. So what you might want to do is download that and rename the file and class name in it, to be "MarkupCacheAdam" or something, and then use it as is, since it's ready to go. But good to rename it just so it doesn't get mixed up with the regular MarkupCache once updated. 

Share this post


Link to post
Share on other sites

@Ryan: I haven't realized that there was posted any code (I somehow thought this is just an idea), my bad!

@OP: Wouldn't be better if these settings were somewhere in config possibly, rather than in administration? This is obviously just my preference, have code related settings in code, but I thought I'll throw it out; You could have something like this:

$config->MarkupCache->rules = array(  array('cache'=>'MyCacheName', 'selector'=>'template=home'),  array('cache'=>'/^reg(.*?)exp$/', 'selector'=>'template=home|page|contact'));

Share this post


Link to post
Share on other sites
@OP: Wouldn't be better if these settings were somewhere in config possibly, rather than in administration? This is obviously just my preference, have code related settings in code, but I thought I'll throw it out; You could have something like this:

Well, all cache related settings are done through the admin UI at the moment, but maybe Ryan has a better opinion on this. I'm fine with both options.

I've also updated the MarkupCache module to support the CacheName "PageSelector" format.

MarkupCache.module

  • Like 1

Share this post


Link to post
Share on other sites
Well, all cache related settings are done through the admin UI at the moment, but maybe Ryan has a better opinion on this. I'm fine with both options.

This is one of those settings that most people probably won't take advantage of (and may not need), but the people that might benefit from it will really appreciate it. When settings like this are hidden in PHP code, it's a lot less likely people will ever know about them. You have to track down documentation to know that the setting even exists. Whereas, if it's a configuration field that's always visible with the module, it's a lot less likely to be missed. This is why most settings like this are done through the admin UI. Though admittedly, this is one case where I think it'd be perfectly fine for it to be in the static $config too (since 80% of people don't need it). I also wonder if it shouldn't just be defined when the cache is created, with a $cache->expireWhen(...); or something like it, before a $cache->save(). Ultimately there may be no right answer for every case, but the current UI module config seems like a good common denominator.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...