Enable and Configure HST Enterprise Caching
Introduction
Goal
Enable and configure enterprise caching features in Hippo's delivery tier.
Background
Enterprise Caching makes the community edition's page caching much more powerful and efficient, and makes a delivery tier cluster work more efficiently by reusing cached pages between cluster nodes. In addition, it allows for domain-specific optimization through cluster-wide caching.
This page describes how to enable and configure Enterprise Caching features.
To learn how Enterprise Caching works and what its added value is over the community edition's caching features, see Understand HST Enterprise Caching.
To configure logging and monitor performance using JMX, see Monitor HST Enterprise Caching.
HST Enterprise Caching Features
Enterprise Caching comes with three different caches for the delivery tier:
- Stale Page Cache
- Second Level Page Cache
- Cluster-Wide Generic Cache
All three caches work independently from each other. Depending on your exact requirements, you can enable only those caches that you need.
Requirements
For the Stale Page Cache you do not need anything other than adding the enterprise caching add-on and enable the Stale Page Cache. For the Second Level Cache and Cluster-Wide Generic Cache you need to setup Redis. Note that in case of high availability requirements at this moment we only support Redis Clustering and not (yet) Redis Sentinel. The Redis (cluster) needs to be compatible with the jedis 2.9.0 client.
Add HST Enterprise Caching
To add enterprise caching to your implementation project, add the following dependency to site/pom.xml:
<dependency> <groupId>com.onehippo.cms7</groupId> <artifactId>hippo-addon-caching-hst</artifactId> </dependency>
The above dependency pulls in all required jars. Note that including this dependency does not yet enable any of the caches. Below is described how to enable the different caches.
Enable Stale Page Caching
To enable Stale Page Caching, add to your HST webapp context.xml, typically located at:
/site/webapp/src/main/webapp/META-INF/context.xml
the following JNDI variable:
<Environment name="stalePageCache" type="java.lang.String" value=""/>
This enables Stale Page Caching with default configuration. If you'd like to tune some settings, the following attributes can be configured:
<Environment name="stalePageCache" type="java.lang.String" value="{'maxEntriesLocalHeap' : 10000, 'maxEntriesLocalDisk' : 0, 'eternal' : false, 'timeToLiveSeconds' : 86400, 'timeToIdleSeconds' : 86400, 'diskExpiryThreadIntervalSeconds' : 120, 'stats' : true}" />
The above values are the same as the defaults.
Enable Second Level Page Caching
To enable Second Level Page Caching, add to your HST webapp context.xml, typically located at:
site/webapp/src/main/webapp/META-INF/context.xml
the following JNDI variable:
<Environment name="secondLevelPageCache" type="java.lang.String" value=""/>
This enables Second Level Page Caching with default configuration. If you'd like to tune some settings, the following attributes can be configured:
<Environment name="secondLevelPageCache" type="java.lang.String" value="{ 'ttlSeconds':300, 'profiling' : true, 'stats' : true, 'quiet' : true, 'async' : true}" />
The above values are the same as the defaults. The attributes profiling, stats, quiet and async are optional and if not specified true by default. ttlSeconds is optional and, if not specified, 300 seconds.
Explanation of the attributes:
- profiling: If true, profiling information is exposed over JMX about how long puts and gets take
- stats: If true, statistics information is exposed over JMX of the cache like hits/misses/puts/ratios
- quiet: If true, you won't get an exception from the org.springframework.cache.Cache in case a put of get or any other method results in an exception (for example a timeout). If you have 'quiet' : false but 'async' : true (also the default), you won't get an exception thrown for puts because the put is done asynchronously. For a get you can get an exception from the backing cache. If you also have 'async' : false, then you're code will get the runtime exception from the backing cache if something fails for both put and get.
- async: If true, all void methods on org.springframework.cache.Cache are done asynchronously, most notably the #put method. The big advantage is that when the put is done async, request processing is not blocked during storing entries in the Second Level Page Cache.
Enable Cluster-Wide Generic Cache
To enable Cluster-Wide Generic Caching, you have to add to your HST webapp context.xml, for example in case your HST webapp is site at
site/webapp/src/main/webapp/META-INF/context.xml
the following JNDI variable
<Environment name="clusterCache" type="java.lang.String" value=""/>
This enables Cluster-Wide Generic Caching with default configuration. If you'd like to tune some settings, the following attributes can be configured
<Environment name="clusterCache" type="java.lang.String" value="{ 'ttlSeconds':300, 'profiling' : true, 'stats' : true, 'quiet' : true, 'async' : true}" />
The above values are the same as the defaults. The attributes mean the same as above for the secondLevelPageCache.
Use Cluster-Wide Generic Cache
When the 'clusterCache' is configured, retrieving and using it in HST code can be done as follows:
HippoRedisCacheManager cacheManager = HstServices .getComponentManager() .getComponent(HippoRedisCacheManager.class.getName(), "com.onehippo.cms.spring.cache"); Cache cache = cacheManager.getCache("clusterCache"); Visitor visitor = cache.get(visit_id, Visitor.class); if (visitor == null) { visitor = expensiveGet(visit_id); cache.put(visit_id, visitor); }
In the example above, the Visitor object must be serializable. If you have the cache configured as
'quiet' : true
(which is the default if not configured) you won't get an exception thrown but just a warning log from QuietCache. If you have 'quiet' : false but 'async' : true (also the default), you won't get an exception thrown for puts because the put is done asynchronously. For a get you can get an exception from the backing cache. If you also have 'async' : false, then you're code will get the runtime exception from the backing cache if something fails for both put and get.
Configure the Redis Connection Settings
By default, the connection factory tries to connect to a Redis instance with
hostname = localhost port 6379 connectionTimeoutMs = 1000 timeoutMs = 10
If you have configured Redis on a different server or port, or you want a different (connection) timeout, you can modify these defaults. Be careful not to make the timeoutMs much higher than 10 ms because it might result in taking too long lookups for cache key entries to be present (for storing it is less harmful since a Redis cache put happens asynchronously). Since latency needs to be very low, we recommend installing the redis nodes on the same servers as where the application runs so every container has local access to a Redis instance. If you'd like to change the defaults, you can do so as follows
In your local Cargo-based development environment, add a JNDI environment variable that contains configuration properties for connecting to Redis. Add the following JNDI environment variable to conf/context.xml:
<Environment name="redis/hippoJedisConnectionFactory" type="java.lang.String" value="{'hostname':'localhost', 'port':6379, 'connectionTimeoutMs' : 1000, 'timeoutMs' : 10}" />
Note that the <Environment /> variable above should go into the global conf/context.xml whereas the specific Second Level Page Cache, Stale Page Cache and Generic Cluster-Wide Cache context configuration should go into the site webapp context.xml.
Redis Cache Key Prefixing
If your Redis cache is being used my multiple applications, you might want to use cache key prefixing to avoid accidental cache key collisions. You can do so in the global container conf/context.xml by specifying the JNDI variables
<Environment name="env" type="java.lang.String" value="foo" /> <Environment name="stack" type="java.lang.String" value="bar"/>
Then the created Redis Cache Keys will be prefixed with <env>-<stack>:hippo:, thus for the example above, with 'foo-bar:hippo:'. This is useful if for example your redis instances are shared between different environments (test/acct/prod) or between different stacks (customers).