Application Caching using Ehcache
Ehcache is an open source caching system that handles application caching.
Advantages
Using Ehcache gives Self Managed Commerce the following benefits:
- JMX Support
- Unified run-time configuration for application and OpenJPA L2 caches
- Improved cache visibility and control
Application caching is enabled in Cortex, Search Server, Integration Server, and the Import/Export CLI tool.
Cache Behaviour
Most application caches are initialized using the EhcacheCacheFactory
class. This creates a new cache in the EhCache cache manager and then wraps the result using EhcacheCacheAdapter
which implements the com.elasticpath.cache.Cache
interface.
The com.elasticpath.cache.Cache
interface exposes several important methods which are described below.
CacheResult<V> get(K key)
This simple method retrieves the value from the cache for the passed key. The CacheResult
object allows the caller to determine if the object was present in the cache by calling the isPresent
method. It is possible to cache a null result, which will return true when isPresent
is invoked, but the get
method will return null.
It is usually recommended to use the get
method with the fallbackLoader
parameter instead of this method to ensure thread safety.
V get(K key, Function<K, V> fallbackLoader)
This method retrieves the value from the cache for the passed key and falls back to the fallbackLoader
function to retrieve the value if it isn't present. The result from the fallbackLoader
is inserted into the cache.
This method also uses XSync to ensure that if multiple threads request the same key value, only the first will invoke the fallbackLoader
and the other threads will block until the fallbackLoader
completes. After the value is loaded, all of the waiting threads will receive this value.
V get(K key, Function<K, V> fallbackLoader, BiFunction<K, V, V> populateCacheFunction)
This method retrieves the value from the cache for the passed key and falls back to the fallbackLoader
function to retrieve the value if it isn't present. The populateCacheFunction
is called after the fallbackLoader
function completes, and allows more complex cache population logic. It is the responsibility of the populateCacheFunction
to populate the cache, if necessary.
This method also uses XSync to ensure that if multiple threads request the same key value, only the first will invoke the fallbackLoader
and the other threads will block until the fallbackLoader
completes. After the value is loaded, all of the waiting threads will receive this value.
Map<K, V> getAll(Collection<? extends K> keyValues)
This method retrieves multiple value from the cache for the passed keys. Each result is returned in the map associated to it's corresponding key.
Map<K, V> getAll(Collection<K> keyValues, Function<Collection<K>, Map<K, V>> fallbackLoader)
This method retrieves multiple value from the cache for the passed keys. For any values not present in the cache, a single call to the fallbackLoader
function is made to retrieve the missing values. The results from the fallbackLoader
are inserted into the cache as separate entries.
V put(K key, V value)
This method inserts a single value into the cache corresponding to the passed key.
boolean remove(K key)
This method removes a single value from the cache for the passed key.
void removeAll()
This method removes all values from the cache.
boolean containsKey(K key)
This method returns true if the specified key exists in the cache.
Configuration
Elastic Path provides the following Ehcache configuration file in the extensions/database/ext-data
module:
src/main/resources/environments/default/files/conf/cache/ehcache-fast-updates.xml
for use in development environments
For development use, you need to copy configuration files to your ${user.home}/ep/conf
directory as described in Updating Configuration Files.
For deployed environments, the configuration file is specified using the ep.external.ehcache.xml.path
property, either as a JVM parameter or in the ep.properties
file.
The file path in ep.external.ehcache.xml.path
must be an absolute path. There are three supported formats as shown in the following examples:
${user.home}/ep/conf/cache/ehcache.xml
(Linux and Windows)/ep/conf/cache/ehcache.xml
(Linux) orc:/ep/conf/cache/ehcache.xml
(Windows)file:///ep/conf/cache/ehcache.xml
(Linux) orfile:///c:/ep/conf/cache/ehcache.xml
(Windows)
Additional Slash
The additional slash in file:///
is required by Spring.
If a particular cache is not specified in the file referenced by ep.external.ehcache.xml.path
(or if the ep.external.ehcache.xml.path
value is not set), then the cache will use the default values specified in the Spring bean. The Spring bean defaults represent the values that Elastic Path recommends for most production-like environments.
All Ehcaches can also be completely disabled by setting the -Dnet.sf.ehcache.disabled=true
JVM parameter.
An application restart is required for configuration changes to take effect.
Cortex and Integration Server Application Caches
The application level caches sit above the data access layer and are used by Elastic Path to cache longer lived application data.
Application cache configuration (time-to-live, time-to-idle, and max entries) can be defined in the external configuration file described above. If no external Ehcache file is defined, or if the file doesn’t contain an entry for a cache, then the fallback configuration is defined by the cache bean definition in Spring. See Application Data Caching a for a list of caches.
Search Server Application Caches
In the Search Server, many application caches have their configuration overridden to a shorter time-to-live. This is done to ensure that the search server doesn’t index out-of-date cached values.
By default, all caches, except domain (starting with com.elasticpath
) and openjpa-query
, have time-to-live and time-to-idle overridden to 5 seconds by the ehCacheConfigurationConfigurator
bean.
These values can be changed via system properties, if required:
-Dep.ehcache.ttl.seconds.override=<N>
-Dep.ehcache.tti.seconds.override=<N>
Additionally, which caches are overridden by the ehCacheConfigurationConfigurator
bean can be configured in its Spring properties:
cachesToOverride
: the list of caches for which time-to-live/time-to-idle will be overriddencachesToExclude
: the list of caches that will be excluded from overridingdomainCacheNamePrefixesToExclude
: the list of domain cache prefixes that will be excluded from overriding
Cache Invalidation
Objects in the Ehcache can be evicted for two reasons:
- The time-to-live (seconds since object was added to the cache) or time-to-idle (seconds since object was accessed in the cache) expires. This is managed internally by Ehcache.
- The object was modified by Commerce Manager, Import/Export, or Data Sync, and message-based cache invalidation is supported for the object type.
Message-based cache invalidation is currently only supported for product objects, but support for this will be expanded to other object types in upcoming versions. When an object is updated, a message is sent to the ep.domain
JMS queue. This is processed by the cacheEventMessageRouteBuilder
Camel route in the Integration Server, which then sends a message to the ep.cache
topic. All running services listen to this topic, and evict the object referenced in the message.
Messages on the ep.cache
topic are formatted as follows:
{
"eventType": {
"@class": "CacheEventType",
"name": "PRODUCT"
},
"guid": "{PRODUCT_CODE}",
"data": {}
}
The diagram below shows the interactions between JMS queues and the Self Managed Commerce services:
OpenJPA L2 Cache
OpenJPA uses ehCache as a backing store for its level 2 JPA cache.
The default Ehcache OpenJPA L2 cache configuration is defined in ehcache.xml
in the commerce-engine/core/openjpa-osgi-wrapper
module.
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="1"
timeToLiveSeconds="1"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="true"
/>
L2 Data Cache
The L2 (Level 2) data cache holds entity instances using their primary keys as identifiers. A separate cache is created for each domain entity with a cache name of the fully qualified class name (e.g. com.elasticpath.domain.catalog.impl.ProductImpl
).
Entities will not be stored in the data cache if they have the @DataCache(enabled = false)
annotation set. We recommend that the data cache be completely disabled for entities that are modified by shoppers, such as shopping cart entities.
The time-to-live for each data cache can be set in the following ways, evaluated from top to bottom:
- A timeout (in seconds) can be set in the data cache annotation as follows:
@DataCache(timeout = 3600)
- An explicit
cache
entry can be specified for the entity using its fully-qualified class name. - The
openjpa.DataCacheTimeout
JPA property can be set for all entities. - If none of the above overrides are set, the
timeToLiveSeconds
attribute of thedefaultCache
is used.
We recommend against using a data cache with more than a one-second timeout, as this can lead to dirty reads and inconsistent behaviour.
L2 Query Cache
The L2 (Level 2) query cache caches query results so that repeated queries with the same parameters can retrieve results directly from the cache rather than hitting the database again. The cache holds the list of primary keys for the entities that match the query criteria, not the actual entities themselves which are stored in the L2 data cache.
The OpenJPA query cache is configured by the following element:
<cache name="openjpa-querycache"
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
statistics="true"
/>
Configuration Example
Here is a sample configuration override file:
note
For information on Ehcache’s configurable properties, see Ehcache’s Cache Configuration guide.
<?xml version='1.0' encoding='UTF-8'?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"
name="ep-default-cache"
updateCheck="false">
<!--
Read the Ehcache documentation for help on configuring this file.
The <defaultCache> configuration is used as the default for programmatically created caches.
<cache> entries will not inherit configuration from <defaultCache>.
-->
<defaultCache
timeToIdleSeconds="1"
timeToLiveSeconds="1"
maxEntriesLocalHeap="10000"
eternal="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="true"
/>
<cache name="openjpa-querycache"
timeToIdleSeconds="5"
timeToLiveSeconds="5"
maxEntriesLocalHeap="10000"
eternal="false"
statistics="true"
/>
<!--
Override application cache configuration.
-->
<cache name="baseAmountCache"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
maxEntriesLocalHeap="10000"
statistics="true"
/>
</ehcache>
An example of overriding all caches can be found in the extensions/database/ext-data/src/main/resources/environments/default/files/conf/cache/ehcache-fast-updates.xml
file.
Recommended Practices
Every commerce implementation is unique. No one configuration fits all. You will need to discover the configurations that work for your implementation. Keep in mind:
- Configure lower cache timeouts in development, QA and authoring environments where users want to see changes quickly and performance isn’t a primary consideration.
- Configure much higher timeouts for production and performance test live environments.
- Stress your application while using JMX console to monitor the number of cache requests. This will help you determine if the DB entities needs to be cached and for how long.
- Be cautious about increasing L2 cache timeouts. Complex domain object graphs, such a products or categories, can get out of sync when L2 objects are refreshed independently of each other.
- Keep conducting performance tests until the optimal results are achieved.
- Ehcache documentation contains comprehensive information about sizing caches.