Play-CMS supports powerful caching mechanisms at the block level. Caching is disabled by default, it can be enabled globally via configuration and refined per block class.

Caching uses Play's Ehcache plugin, but also accesses Ehcache's API via ch.insign.commons.CacheManager. The block's render() output (Html) is stored in-memory (currently no serialisation and disk storage).

How it works

Every block has a .render() method which tries to return a cached version first, and falls back to display if no cache is available.

Steps

  • block.render() executed
  • check if block is applicable for caching
    • (yes) check if block is already in the cache
      • (yes) return cache
      • (no) render block and store it into the cache
    • (no) render block

When are blocks flushed from cache?

  1. If blocks are modified, play-cms calls block.markModified(), which invlidates all existing cache for this block.
  2. When a block reaches its scheduled display from or display to date
  3. If the cache entry is older than the specified global time to life (cms.blockcache.ttl)
  4. ModifiedBlockCacheRemover.executeCacheRemove is called, and block is marked as applicable for beign lazily removed.
  5. By manually calling the .invalidate() method of BlockCache

Configuring the cache

The most crucial part in caching is creating a proper cache key which is unique for the different content variants while trying to keep the cache entries to a minimum. BlockCache offers various settings, and in addition the block class can overwrite the .getKey() method to further control the key generation.

Setting
Meaning
Default
setEnabled turn caching on/off false
ignoreGETParams List of get params which should be ignored, when deciding if block should be cached
If all params for a url are in this list - it would be cached even with @handleGET disabled.
[ ]
handlePOST Cache responses to POST requests false
handleGET Cache responses to requests that contain GET parameters. If enabled, separate cache entry would be created for each unique
parameters combination (excluding those listed in ignoreGETParams)
false
ignoreUser Do not create per- (authenticated) user cache entries false
ignoreGroups TODO: Do not create per set of user-group entries
(Warning: Do not ignore users and groups or everybody will see e.g. frontend edit controls if they happen to get cached) 
false
ignoreLanguage Do not create per-language cache entries false
ignoreUrl By default, caching is done on per-url basis. Enable this option to prevent it. false
ignoreModifiedUrls List of urls, which should be excluded from automatic block cache invalidation.
Such block caches would require manual flushing via ModifiedBlockCacheRemover
Use [ ] for disabled, use ["*"] for all urls
[ ]

 Global configuration

## Block cache settings
cms.blockcache.enabled = true
cms.blockcache.ignoreGETParams = []
cms.blockcache.size = 512 # MB (LRU strategy will evict least recently used entries when the size limit is reached)
cms.blockcache.ttl = 604800 # sec. (1 week)
cms.blockcache.handleGET = false
cms.blockcache.handlePOST = false
cms.blockcache.ignoreUser = false
cms.blockcache.ignoreGroups = false
cms.blockcache.ignoreLanguage = false
cms.blockcache.ignoreUrl = false
cms.blockcache.ignoreModifiedUrls = []

BlockCacheFactory class

BlockCacheFactory is responsible for BlockCache and BlockCacheKey management. If default implementation does not satisfy needed use case - it needs to be overriden.

Usage

In general, caching needs proper configuration, but not much implementation.

  • Controller level: The cms uses page.cached() from its FrontendController when a url is requested
  • Templates: Use @block.cached (and not @block.render) to ensure cached content can be used.
  • Test the caching behaviour with different users / roles! This is crucial - you want to be sure non-admins cannot see cached admin-content etc.
  • Hint: add <logger name="ch.insign.cms" level="DEBUG" /> to logger.xml to analyze the caching behaviour. Check entries like
    "cached: Using cached block-10802-1-de-page:mine"
  • Use the ?cache=false GET parameter to disable caching for this request
  • Use BlockCache.flush() to flush the entire cache (the cache is also flushed upon a play restart)

Live partials in otherwise cached pages

Often your main site layout contains dynamic per-user content, such as username, login state, cart content, feedback messages from flash scope etc. For these cases, you can define uncached live partials, which are not directly rendered when the page is rendered. Instead, during rendering, a live partial placeholder is placed in the template (Example: [uncached:miniCart]). Before output, these placeholders are replaced with the rendered content of these live partials, no matter whether the page's template was just rendered or comes from cache.

Example:

1. Define your uncached live partials in your CMSApiLifecycle implementation:

MyCMSApiLifecycle.java

@Override
public void registerUncachedPartials(CMSApi cmsApi) {
    cmsApi.getUncachedManager()
        .register("miniCart", () -> views.html.shared.miniCart.render())
        .register("myAccountBox", () -> views.html.shared.myAccountBox.render())
}

 

2. In your template (often the main layout):

@Template.addUncached("miniCart")

Note: If you use your main layout file for both cms and non-cms pages, then use:

@Template.addUncached("miniCart", page)

 

Where page is the instance of the Page interface. addUncached will then determine if the current page is a cms page (instace of PageBlock) and use the live partial mechanism. If it's not a cms page, then it will render and add the partial directly.