Fork me on GitHub

Caching guidelines  Bottom

  • I have over the last few years done some different work with caching in PostNuke/Smarty and would like to share some thoughts with you based on my own experiences (latest was the search cache bug mentioned in the previous topic).

    Lets take some basics first. Caching is something we do to save resources on the server. By caching content we save a local copy of the final HTML and serves this the next time there is a request for it - without the need to regenerating it all from scratch. In this way we save some valuable database calls.

    Now, if caching is such a good idea then why don't we just cache just about everything? Why have checkboxes on the admin pages for enabling caching? If you ask me then it's first of all because the developers don't comprehend the full consequences of caching everything - so they leave it up to the user to decide if they dare to enable caching - and suffer the possible consequences of modules that doesn't handle caching correct.

    But it is also because caching requires diskspace (or memory). The trade off from using caching is that you get a faster response time at the cost of more disk usage - under some circumstances you could easily get thousands of cached files (especially if access control calls for one cache per user).

    So we let the user decide if they want caching. In a perfect world with unlimited resources it would not be necessary. In a near-perfect world with foolproof caching but limited resources we still need that checkbox - because caching eats up diskspace. In the real world we cannot always make foolproof caching and must depend on cache expiration times - and so we let the user decide if this is acceptable.

    Caching is also inherently difficult - we must not let users see restricted content through other users cache result, and we must be able to show new content even when a cached result exists. The later can be very difficult, if not impossible, to achive if we combine content from various sources.

    This leads to my main point: if you don't do anything special to handle caching then it probably doesn't work and should be disabled! Unfortunately pnRender defaults to let the end user enable caching - even if the installed modules doesn't work with caching enabled.

    But, you could ask, if a module works with caching - why then be able to turn it off? Good point - in a near-perfect world it should also be so. If caching works perfectly then there is no need to turn it off. But as I have argued previously then it can be necessary under some circumstances.

    This leads me to the following guidelines for module devs:

    - If you don't care about caching then turn it off explicitly in your code(1). Don't rely on pnRender's default settings - chances are otherwise that the end user will enable caching for your module and it will fail, either by showing restricted content for the wrong users or by not showing new content in a timely way.

    - If you care about caching then go through all your code to see when and where you can allow caching. Do so by relying on pnRender's getInstance method's default parameters. In all other places caching should be explicitly disabled(1).

    - Think carefully about permissions: if you have any permission checks then either do them before the cache control or make sure you include the current user ID in the cache ID.

    - If possible then clear caches explicitly when content is added, edited, moved, deleted or otherwise modified. The easiest way is to have a common function "clearAllCaches" that you call after any changes are done to your content. Example: the Content module caches it's menu block and has clearAllCaches calls all over the code.

    - If you include content from sources that won't inform you of updates to their content then you cannot do anything else than depend on the cache expiration time. Example: Zikula's search module that caches search results where new content won't show up before the cache expires.

    /Jørn

    (1) $render = pnRender::getInstance('ModName', false)
  • I would also add:

    - Take care not to cache error results. This could make a one time problem (example: database timeout) persist for hours or days.

    Curt
  • Jorn,

    Thanks for the advice.

    As a hobby developer, caching is often an afterthought. I do try to create display templates (which have no uncache-able content like permission based output) and cache those.

    Any good Zikula references for caching strategies that are well implemented? Is the Content module, in your opinion, an example of good caching strategy?

    Anyone know of a good Smarty caching tutorial or manual? (I've looked at a few, but just wondering if anyone has good resources they use).

    NCM
    SwitchBit
    UHEweb
  • Quote

    Any good Zikula references for caching strategies that are well implemented?

    No.

    Quote

    Is the Content module, in your opinion, an example of good caching strategy?

    Only the menu block - there is no caching on the rest.

    Quote

    Anyone know of a good Smarty caching tutorial or manual?

    No, sorry.
  • Quote

    Take care not to cache error results

    Or plain and simple: don't cache anything that's inherently dynamic, be it banners, error messages, time of day, todays quote, random images, login formula etc.

  • Quote

    Or plain and simple: don't cache anything that's inherently dynamic, be it banners, error messages, time of day, todays quote, random images, login formula etc.

    Sometimes the error results are not obvious in testing.


    If the presentation is too dynamic for caching you might consider caching the data side. If the result set from a query is reasonably small and fairly static, this can be cached with Smarty.

    First:
    Create a Smarty template with only a single data element.

    In you module/block:
    Build an array of the complete result set from the data base query.
    Serialize the array and pass it to the Smarty template you created.

    While the template data has not expired, all you need to do is de-serialize the template output and use the resulting array to build your presentation.

    This could also be a simple way to cache RSS feeds so no matter how busy you site, the RSS data would only be refreshed every so often.

    Curt

This list is based on users active over the last 60 minutes.