Caching Strategy

Mezzanine takes great care to appropriately minimize database queries. This strategy enables Mezzanine to perform well without a caching configuration. However, caching is also well-supported in the event that you wish to implement customized caching for your Mezzanine site. Mezzanine is preconfigured to cache aggressively when deployed to a production site with a cache backend installed.

Note

By using Mezzanine’s bundled deployment tools, Mezzanine’s caching will be properly configured and in use for your production site. Consult the Deployment section for more information. If you would like to have a cache backend configured but to use a different caching strategy, simply remove the cache middleware described in the next section.

Cache Middleware

Mezzanine’s caching system employs a hybrid approach which draws from several popular caching techniques and combines them into one overall implementation. Mezzanine provides its own implementation of Django’s page-level cache middleware, and behaves in a similar way.

Pages are fetched from cache by mezzanine.core.middleware.FetchFromCacheMiddleware, which should appear at the end of the MIDDLEWARE_CLASSES setting and therefore be activated at the end of the request phase. If a cache miss occurs, the request is marked as requiring a cache update, which is handled by mezzanine.core.middleware.UpdateCacheMiddleware, which in turn should appear at the start of MIDDLEWARE_CLASSES and therefore be activated at the end of the response phase.

Mezzanine’s cache middleware differs from its Django counterpart in a few subtle yet significant ways:

  • Setting CACHE_ANONYMOUS_ONLY to False will have no effect, so authenticated users will never use the cache system.
  • Cache keys include the ID for the current Django Site object.
  • Cache keys do not take Vary headers into account, so all unauthenticated visitors will receive the same page content per URL.

Two-Phased Rendering

One approach to caching Django sites is to use template fragment caching, which defines the areas of templates to be cached. Another approach is two-phased rendering, which is the opposite. Using this method, all content is cached by default. We then define the sections of a template that should not be cached. These sections might be anything that makes use of the current request object, including session-specific data.

Accordingly, Mezzanine provides the start and end template tags nevercache() and endnevercache. Content wrapped in these tags will not be cached. With two-phased rendering, the page is cached without any of the template code inside nevercache() and endnevercache executed for the first phase. The second phase then occurs after the page is retrieved from cache (or not), and any template code inside nevercache() and endnevercache is then executed.

Mezzanine’s two-phased rendering is based on Cody Soyland’s django-phased and Adrian Holovaty’s blog post which originally described the technique.

Note

The template code inside nevercache() and endnevercache will only have access to template tags and variables provided by a normal request context, with the exception of any variables passed to the template from a view function. Variables added via context processors such as the current request and via Mezzanine’s settings will be available. Template tag libraries should be loaded inside these areas of content so as to make use of their template tags.

Mint Cache

The final step in Mezzanine’s caching strategy involves a technique known as mint caching, in which the expiry value for any cache entry is stored in cache along with the cache entry itself. The real expiry value used is the given expiry plus the value defined by Mezzanine’s CACHE_SET_DELAY_SECONDS setting. Each time a cache entry is requested, the original expiry time is checked, and, if the expiry time has passed, the stale cache entry is placed back into the cache along with a new expiry time using the value of CACHE_SET_DELAY_SECONDS. In this case, no cache entry is returned, which has the effect of essentially faking a cache miss, so that the caller can know to regenerate the cache entry. This approach ensures that cache misses never actually occur and that (almost) only one client will ever perform regeneration of a cache entry.

Mezzanine’s mint cache is based on this snippet created by Disqus.