Jump to content

API feature: page-level caching


Recommended Posts

I'd like to see a caching API added to Page objects.

This would differ from WireCache, in that this would enable caching in a Page-specific context, which would make it possible to provide some meaningful options for (among others) cache expiration in a Page-context.

The API might be something like a PageCache class, which is attached to Page objects as e.g. $page->cache.

It would work much like WireCache, but would write cache entries with the Page ID attached to each entry, allowing some new options for cache expiration:

  • Expires when the Page object is deleted
  • Expires when the Page object is changed
  • Expires when the Page object, or a Page in it's parent path, is changed

When, for example, you have a page/template that aggregates content from another page (fetching content server-side), or when your template needs to perform expensive calculations or database queries to produce the page content, the fetched/generated content could be written as cache entries that lives forever (until the Page object is deleted) or until the next change to that Page, or a parent Page.


  • Like 2
Link to comment
Share on other sites

I am quite new to caching and do not know, what do we have in PW right now, but what you are proposing makes a lot of sence.

If you could explain the difference to the curerent state of affairs it would be great.

How would pages with conditional output, url segments be treated?

Maybe we could even make the cache expire when some API call with PW selectors returns changed value (against the stered one)?

Link to comment
Share on other sites

Cool idea. The way I see many people storing objects in databases these days is JSON. There is some slight overhead in decoding big objects inside a database...if you are only grabbing a few fields from each $page object it's probably faster to use process wire's default approach to getting a field data. I'd image if you are getting lots of fields and need most of them to do a calculation then storing whole $pages as JSON inside a noSQL db like redis would give the largest speed boost. You will have fewer queries, you just spend your processor time decoding JSON objects (which should not be that bad). 

I image a simple module that did this could JSON encode the $page object on $page->save ($page->save is used by all things that save pages including the PW control panel) and store it in the DB of choice, under #page id.

if you wanted to pre-populate your current $page object with your cached version you could use.

$page->getCached(); or something like that. 

foreach( $veryComplexPages as $complexPage){
      $complexPage->getCached(); //would populate $complexPage->fields with the cached version if available.
      echo $complexPage->someField; //would use cached version gotten from the command above

As far as I know process wire by default will cache all fields that are set in memory, for example if $page->title is not NULL process wire will not try to search the database for it. This default behavior will make it easy to write a thrid party page cache module that gets the $page fields first.  Also process wire also only grabs the fields that you request, so If you don't request a lot of fields you will have minimum interaction with the database.


Caching the $page->find() style functions. 

The methods above do not seed up queries to the database that are string search related such as $pages->find(), which is probably the kind of queries that are taking up a the much of the overhead. if you wanted to store the results for those you would need another cache, maybe that stored page ID's returned from the $pages>find() method.


$pages->find('body=*"somthing to search for"')->cache('2 days');

One could use the API hook of the find() method, and use a hash of "body=*"somthing to search for" as a key to store the resulting page ID's in redis or MYSQL. That way you could save the results of huge and complex queries for a latter time as a list of page ID's.  This type of cache would also be harder to invalidate but it could still be done on save(). To do so you would have to search the results of all find queries that where ever cached looking for the a page ID that has changed. (that is going to be faster in MYSQL then redis), since you can't query redis. 


would love to hear if any of these solution might be terrible and not work as I will be needing to implement some variations of them in the future. 

  • Like 1
Link to comment
Share on other sites

mindplay.dk: the idea of caching I mentioned earlier was more about storing and fetching whole pages out of the database faster, i'm curios to learn more about how you envision a $page->cache to work? sounds like  you want to see a cache for storing calculated values associated with a page.

    echo $page->cache->averageRatting
     $page->setCache( 'averageRating',  caclulateAverageRating($page), '3 days' ); //no automatic clearing of cache...easy...

A possible way to store this kind of a cache and have it auto clear: (madeup code, to demo a possible way)

$page->setCache( 'averageRating',  caclulateAverageRating($page), '1 year'
      /* clear on page changes of: */ '$page->siblings(),$page->parent', 
      /* clear on count change of: */ 'siblingCount=$page->singling->count()' ); //clears cache when new review is added

only down side to having caches that automatically clear them selves is they are going to make your $pages->saves() more DB intensive & slower if you hook into that function to update the cache. Maybe even a whole lot slower if there are a lot of rules involved in when it should be cleared. Unless it is a cron job that runes ever hour or something, checking the DB: "did anything change, what changed? is it in my list of things to clear?" its hard to think of a one size fits all solution for clearing calculated caches automatically, since you are dealing with calculated values they could be coming from any combination of pages in the system. you would have to specify what what pages would need to be changed to clear your cache. it is going to be different for every kind of calculation. IE, what pages need to be cleared would be different for and "average rating"  then it would be for a cache of "top things voted by users."

i'm just throwing out idea of how it could work, would be curios to know in more detail how you see it working and, if you see a one-size-fits-all solution out there for clearing the calculated caches. 

Link to comment
Share on other sites

sounds like  you want to see a cache for storing calculated values associated with a page

More or less - not really associated with the page per se though, more like tagged as being related to a page ID. I envisioned $page->cache returning e.g. new PageCache($page->id) and any name/value you write to the cache would then get written to a table with the Page ID. So cache entries indexed by Page ID basically. And with cache entries related to a Page getting automatically cleared when you delete the Page.

Other than that, I picture this working just like any normal data cache - just a generic facility where you can store name/value pairs. I don't expect it to magically keep track of dependencies on other pages, although that is an interesting idea, if it could somehow be implemented without major performance drawbacks... probably not.

Link to comment
Share on other sites

Wouldn't this already possible with the WireCache? 

Not really. Each page should be able to have more than one cache entry - you can't enumrate cache entries for a page, since cache entries are not indexed by page number. For that, you would need a page-specific caching facility.

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

  • Recently Browsing   0 members

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