1

We're running Plone 4.1 with plone.app.caching behind Apache 2.2 with mod_cache and mod_disk_cache.

The pre-defined operations that are available with plone.app.caching aren't quite suitable for this configuration as Apache won't cache responses if max-age=0, no matter what values you have set for Expires and s-max-age (I think this is contrary to the HTTP 1.1 specification). With Plone 3.3 and CacheFu it was a straight forward configuration change to get round this: set max-age=1 for the relevant header set. See this CacheFu issue

I'm looking for some advice to achieve the same thing with plone.app.caching. What's the simplest way to override the plone.app.caching.moderateCaching operation such that its maxage is set to 1 rather than 0?

We're not considering adding Squid or Varnish to our stack at this moment in time.

scarba05
  • 2,943
  • 1
  • 27
  • 29

5 Answers5

2

What you are requesting is actually strongCaching (but with a short expire) so use that instead. By definition, moderateCaching is designed to "cache in browser but expire immediately" -- in other words, max-age=0.

The three default caching operations -- strongCaching, moderateCaching, and weakCaching -- are just useful abstractions designed to make the choices involved in assigning caching policies easier to understand. If the abstractions don't help you, you can pretty much do mostly anything you need by just using strongCaching and changing the settings.

This is discussed in more detail in the plone.app.caching docs, http://pypi.python.org/pypi/plone.app.caching

newbery
  • 433
  • 2
  • 4
  • Strong caching with a short expire here won't do. As soon as you set a non zero max-age `plone.app.caching.operations.utils.cacheInBrowserAndProxy` forces a "public" cache-control header. What I really need to be able to override is `cacheInBrowserAndProxy` and `cacheInProxy` with logic that if s-maxage is nonzero and maxage is zero then set maxage to 1 – scarba05 Sep 08 '11 at 12:19
  • Perhaps I'm misunderstanding something but this doesn't make sense to me. Pretty much by definition, anything set to a non-zero maxage is public. In any case, the public token is just a hint for caching heuristics -- I doubt it's going to change how caches will behave when they see maxage=1. – newbery Sep 08 '11 at 20:38
  • I was trying to figure out why you were concerned about the "public" token. Are you concerned about caching pages from logged-in users? There is already a mechanism in place to keep authenticated pages private -- just add an appropriate etag ('userid' or 'roles') to trigger a private response for logged-in users. Note that before you start trying to cache pages with this sort of split personality, make sure you read the section in the plone.app.caching docs about split view caching. – newbery Sep 08 '11 at 21:41
  • The reason being is to get round this Apache bug https://issues.apache.org/bugzilla/show_bug.cgi?id=35247 – scarba05 Sep 09 '11 at 08:26
  • No. That bug is the cause of your original problem but it's NOT a reason to be concerned about the "public" token issued by plone.app.caching. Again, for the sake of anyone else following this thread, you can get mostly any reasonable collection of cache headers with just the strongCaching operation and some suitable settings. – newbery Sep 09 '11 at 23:05
  • Agree that a positive max-age implies public. What I need to achieve is essentially trick apache into caching an object if the headers imply proxies should cache. i.e. if max-age=0 and s-maxage>0 then set max-age to 1. – scarba05 Sep 13 '11 at 12:28
0

That's all configurable policy. You can set it in the control panel, or (as I prefer) in a generic setup registry.xml. See the example policies in plone.app.caching.

Update:

The control panel attempts to simplify things by restricting what is set on the default caching policies. It's still possible to change these directly in the registry though, so you could use the following in your registry.xml:

<record name="plone.app.caching.moderateCaching.maxage">
    <field type="plone.registry.field.Int">
        <title>Maximum age</title>
        <description>Time (in seconds) to cache the response in the browser or caching proxy</description>
        <required>False</required>
    </field>
    <value>1</value>
</record>
Laurence Rowe
  • 2,909
  • 17
  • 20
  • 1
    For plone.app.caching.moderateCaching maxage is hard coded to 0. maxage is not included in plone.app.caching.operations.default.ModerateCaching.options so you get no option to set it in the control panel and trying to set it using registry.xml does nothing. – scarba05 Jul 06 '11 at 13:37
  • Laurence. Thanks for your help here but rather unusually I don't think you're right on this occasion. I can't get the configuration you provide to work. Inspecting plone.caching.utils.lookupOptions it appears that only options defined in the options attribute of the operation type get loaded from the registry. – scarba05 Jul 06 '11 at 20:49
  • Yes, looks like you're right. I don't really see any reason for that, or why it is hidden on that type. Perhaps worth filing a bug. – Laurence Rowe Jul 07 '11 at 13:35
  • No, this is not a bug. See my answer for details. – newbery Jul 27 '11 at 04:51
0

Laurence located the relevant Apache bug for me https://issues.apache.org/bugzilla/show_bug.cgi?id=35247

As we build Apache ourselves we've decided to patch Apache instead and leave Plone alone although the patch is for trunk and so applying to the 2.2 code is a little tricky.

scarba05
  • 2,943
  • 1
  • 27
  • 29
0

After much deliberation we've solved this with some Apache configuration. We modified the response headers to set max-age to be 1 if max-age = 0 and s-maxage is positive and then remove the expires header:

Header edit Cache-Control max-age=0(.*s-maxage=[1-9].*) max-age=1$1
Header unset Expires

This does the trick although HTTP 1.0 clients now won't know have Expires to base their caching on.

scarba05
  • 2,943
  • 1
  • 27
  • 29
-1

I guess the real answer here is probably that there's no easy way to do this at the moment. I've raised a Plone bug to allow max-age to be overriden with moderateCaching

So here's my solution, that works, but seems a lot of work compared to being able to override the default max age on moderate caching. I define my own caching operation, that extends plone.app.caching.operations.default.moderateCaching:

class CacheInApache(ModerateCaching):
    """ Apache won't cache a response if max-age cache control is 0.  Override ModerateCaching
        and set it to 1 second.
    """
    classProvides(ICachingOperationType)

    title = _(u"Cache in Apache")
    description = _(u"Moderate caching for Plone behind Apache. max-age set to 1")
    prefix = 'cacheInApache'

    maxage = 1  

Register this in configure.zcml

<adapter factory=".operation.CacheInApache" 
        name="cacheInApache" />
<utility component=".operation.CacheInApache" 
        name="cacheInApache" />

Then in my generic setup profile registry.xml I configure my types to use it and define its fields.

<record name="plone.caching.interfaces.ICacheSettings.operationMapping">
    <value purge="False">
        <element key="plone.resource">plone.app.caching.strongCaching</element>
        <element key="plone.stableResource">plone.app.caching.strongCaching</element>
        <element key="plone.content.itemView">cacheInApache</element>
        <element key="plone.content.feed">cacheInApache</element>
        <element key="plone.content.folderView">cacheInApache</element>
        <element key="plone.content.file">cacheInApache</element>
    </value>
</record>
<record name="cacheInApache.smaxage">
    <field type="plone.registry.field.Int">
        <title>Shared maximum age</title>
        <description>Time (in seconds) to cache the response in the caching proxy</description>
        <required>False</required>
    </field>
    <value>86400</value>
</record>
<record name="cacheInApache.etags">
    <field type="plone.registry.field.Tuple">
        <title>ETags</title>
        <description>A list of ETag component names to include</description>
        <value_type type="plone.registry.field.ASCIILine" />
        <required>False</required>
    </field>
    <value>
    </value>
</record>
<record name="cacheInApache.lastModified">
    <field type="plone.registry.field.Bool">
        <title>Last-modified validation</title>
        <description>Turn on Last-Modified headers</description>
        <required>False</required>
    </field>
    <value>False</value>
</record>
<record name="cacheInApache.ramCache">
    <field type="plone.registry.field.Bool">
        <title>RAM cache</title>
        <description>Turn on caching in Zope memory</description>
        <required>False</required>
    </field>
    <value>False</value>
</record>
<record name="cacheInApache.vary">
    <field type="plone.registry.field.ASCIILine">
        <title>Vary</title>
        <description>Name(s) of HTTP headers that must match for the caching proxy to return a cached response</description>
        <required>False</required>
    </field>
    <value></value>
</record>
<record name="cacheInApache.anonOnly">
    <field type="plone.registry.field.Bool">
        <title>Only cache for anonymous users</title>
        <description>Ensure logging users always get a fresh page. Note that if you are caching pages in a proxy cache, you'll still need to use a Vary response header to keep anonymous and authenticated content separate.</description>
        <required>False</required>
    </field>
    <value>False</value>
</record>          

You can also define some type specific overrides.

If anyone can suggest a simpler way to achieve this then please let me know

scarba05
  • 2,943
  • 1
  • 27
  • 29
  • This is much more complicated than necessary. Just use strongCaching and set max-age to whatever you like. See my answer for details. – newbery Jul 27 '11 at 22:17
  • Actually this doesn't work for logged in users as `cacheInBrowserAndProxy` forces a public cache-control header. – scarba05 Sep 08 '11 at 12:20
  • Just as it should. Anything with a non-zero maxage is by definition public. – newbery Sep 08 '11 at 20:43