Jump to content

MarkupCache improvements


u-nikos
 Share

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
Link to comment
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 :)

Link to comment
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 :)

Link to comment
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?

Link to comment
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.

Link to comment
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.

Link to comment
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
Link to comment
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
Link to comment
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.

Link to comment
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." 

Link to comment
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 :)

Link to comment
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. 

Link to comment
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. 

Link to comment
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'));
Link to comment
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
Link to comment
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.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...