Introduction
It might be painful to maintain an application specific cache when we are working on a distributed environment mainly because of the synchronization lag and data inconsistency issue. Best solution for this is to separate the cache to a different server and may be distribute it depending on the storage requirements.
Ehcache integration
Caches can be configured in Ehcache either declaratively, XML or programatically.
Integration with ehcache.
Required dependencies :
[group: 'net.sf.ehcache', name: 'ehcache', version: '2.7.2']
Spring cacheManager configuration.
Out of the box, spring provides integration with ehcache.
<cache:annotation-driven /> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cacheManager-ref="ehcache"/> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:/META-INF/spring/ehcache.xml" p:shared="true"/>
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <defaultCache eternal="true" maxEntriesLocalHeap="100" overflowToDisk="false" /> <cache name="users" maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> <cache name="tenants" maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> <cache name="offices" maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> <cache name="office_template" maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> <cache name="charges" maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> <cache name="funds" maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> <cache name="code_values" maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> <cache name="codes" maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> <cache name="roles " maxEntriesLocalHeap="10000" eternal="true" overflowToDisk="false" /> </ehcache>
Here I've configured seven inmemory caches. More configuration details can be found in the ehcache configuration documentation.
Configuration parameter details
timeToLive
The maximum number of seconds an element can exist in the cache regardless of use. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTL eviction takes place (infinite lifetime).
timeToIdle
The maximum number of seconds an element can exist in the cache without being accessed. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTI eviction takes place (infinite lifetime).
- eternal
when set to "true", overridestimeToLive
andtimeToIdle
so that no expiration can take place. - maxEntriesLocalHeap
Sets the maximum number of objects that will be held on heap memory. 0 = no limit.
What happens when you store more data in ehache than there is memory available?
- <answer here>
Note:
If we need to switch to a disk based cache storage then it is mandatory to implement java.io.serializable interface by the candidate cache objects.
Integration with ehcache enterprise
Ehcache enterprise is a solution to distributed cache storage which is not provided in the general ehcache distribution. Now enterprise version is bundled with BigMemomry MAX - an in memory data management platform.
We can download a free copy of enterprise ehcache from here : http://terracotta.org/downloads/bigmemorymax/free
Required dependencies :
repositories{ maven { url "http://www.terracotta.org/download/reflector/releases" } } dependencies{ compile( [group: 'net.sf.ehcache', name: 'ehcache-ee', version: '2.7.3'], [group: 'org.terracotta', name: 'terracotta-toolkit-runtime-ee', version: '4.0.3'] ) }
It is important to pay attention to dependency versions. According to ehcache documentation all the enterprise dependency versions should matched with the enterprise bundle versions. In case the above configurations failed to resolve dependencies we can host the libraries on a private server.
Spring cacheManager configuration.
CacheManager configurations are same as the previous integration.
<cache:annotation-driven /> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cacheManager-ref="ehcache"/> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:/META-INF/spring/ehcache.xml" p:shared="true"/>
ehcache.xml
In the enterprice edition we can use few other features like server clusters.
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <diskStore path="C://data/" /> <terracottaConfig url="localhost:9510" /> <cache name="users" maxEntriesLocalHeap="1000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="1800"> <terracotta clustered="true"/> </cache> <cache name="tenants" maxElementsInMemory="10000" eternal="true" overflowToDisk="false" /> <cache name="offices" maxElementsInMemory="10000" eternal="true" overflowToDisk="false" /> <cache name="office_template" maxElementsInMemory="10000" eternal="true" overflowToDisk="false" /> <cache name="charges" maxElementsInMemory="10000" eternal="true" overflowToDisk="false" /> <cache name="funds" maxElementsInMemory="10000" eternal="true" overflowToDisk="false" /> <cache name="code_values" maxElementsInMemory="10000" eternal="true" overflowToDisk="false" /> <cache name="codes" maxElementsInMemory="10000" eternal="true" overflowToDisk="false" /> </ehcache>
More configuration details can be found on the distributed ehcache configuration documentation.
Screen shots of Terracotta Management console
Memcached integration
Memcached is a widely used, FOS, high performance, in memory object caching framework. I had to change the previously chosen key values a little bit to suit memcached framework requirements.
We can consider memcached as a giant hash map which stores key value pairs.
There are several memcached clients available for java ; xmemcached , spymemcached , Memcached-java-client . A comparison of these three clients can be found in here. After going through the comparison and little research on the internet I've choose xmemcached client .
Required dependencies :
[group: 'com.google.code.simple-spring-memcached', name: 'xmemcached-provider', version: '3.2.0'], [group: 'com.google.code.simple-spring-memcached', name: 'spring-cache', version: '3.2.0']
Memcached by default does not provide support for cache zones as spring does. However 'Simple Spring Memcached' provides the solution for it.
memcached.xml for user cache :
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- Cache manager --> <bean name="cacheManager" class="com.google.code.ssm.spring.ExtendedSSMCacheManager"> <property name="caches"> <set> <bean class="com.google.code.ssm.spring.SSMCache"> <constructor-arg name="cache" index="0" ref="users" /> <constructor-arg name="expiration" index="1" value="0" /> <constructor-arg name="allowClear" index="2" value="true" /> </bean> </set> </property> </bean> <bean name="users" class="com.google.code.ssm.CacheFactory"> <property name="cacheName" value="users" /> <property name="cacheClientFactory"> <bean name="cacheClientFactory" class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl" /> </property> <property name="addressProvider"> <bean class="com.google.code.ssm.config.DefaultAddressProvider"> <property name="address" value="127.0.0.1:11211" /> </bean> </property> <property name="configuration"> <bean class="com.google.code.ssm.providers.CacheConfiguration"> <property name="consistentHashing" value="true" /> </bean> </property> </bean> </beans>
Since memcached doesn't have cache zone concept, a cache zone specific identifier was appended to the key values to avoid possible conflicts on a single memcached instance:
Cache Zone | Identifer |
---|---|
codes | cd |
funds | fn |
users | un |
charges | ch |
offices | of |
office_tempates | oft |
code_values | cdv |
roles | rl |
Charges cache configuration
@Cacheable(value = "charges", key = "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('ch')") public Collection<ChargeData> retrieveAllCharges() {
What happens when you store more data in memcached than there is memory available?
- memcached throws away data that is least recently used. (LRU)
Memcached management console
PhpmemcachedAdmin is a handy tool to monitor memcached instances it is still in the beta version.
Cached API collection - POSTMAN
I've created a postman collection for cached API end points. One can easily import them to POSTMAN and check the performance improvement by observing the request time.
Additional resources