If you're interested in functional programming, you might also want to checkout my second blog which i'm actively working on!!

Friday, January 3, 2014

Caching - the pitfall

At a current project we added some caches to our application to improve performance. However, we faced some issues I looked into and this article will demonstrate you should use caches with great precaution.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.pelssers</groupId>
<artifactId>cachingdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<spring.version>3.1.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.4.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>14.0.1</version>
</dependency>
</dependencies>
</project>
view raw gistfile1.xml hosted with ❤ by GitHub
package com.pelssers.services;
import java.util.List;
public interface PricingService {
List<Integer> getPriceHistory(Long productId);
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.services;
import java.util.List;
import org.springframework.cache.annotation.Cacheable;
import com.google.common.collect.Lists;
public class PricingServiceImpl implements PricingService {
@Cacheable(value = "services.pricing", key = "#productId")
public List<Integer> getPriceHistory(Long productId) {
System.out.println("Fetching prices from DB");
return Lists.newArrayList(2, 6, 8, 5, 9);
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.configuration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import com.pelssers.services.PricingService;
import com.pelssers.services.PricingServiceImpl;
@Configuration
@EnableCaching
public class DemoConfiguration {
@Bean
public PricingService pricingService() {
return new PricingServiceImpl();
}
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return ehCacheManagerFactoryBean;
}
@Bean
public CacheManager cacheManager() {
EhCacheCacheManager cacheManager = new EhCacheCacheManager();
cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject());
return cacheManager;
}
}
view raw gistfile1.java hosted with ❤ by GitHub
package com.pelssers.demo;
import java.util.Collections;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import com.pelssers.configuration.DemoConfiguration;
import com.pelssers.services.PricingService;
@Component
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(DemoConfiguration.class);
PricingService pricingService = context.getBean(PricingService.class);
List<Integer> prices1 = pricingService.getPriceHistory(1L);
System.out.println(prices1);
Collections.reverse(prices1);
List<Integer> prices2 = pricingService.getPriceHistory(1L);
System.out.println(prices2);
}
}
view raw gistfile1.java hosted with ❤ by GitHub
<ehcache>
<diskStore path="java.io.tmpdir" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="true" />
<cache
name="services.pricing"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off"
statistics="true" />
</ehcache>
<!-- running Main will result in following output.
Fetching prices from DB
[2, 6, 8, 5, 9]
[9, 5, 8, 6, 2]
What do we see here... we only fetch the list of prices the first time.
The second time we get back the cached value.
BUT we actually modified the list of prices (even in our cache). Potential PITFALL !!
-->
view raw gistfile1.xml hosted with ❤ by GitHub
<ehcache>
<diskStore path="java.io.tmpdir" />
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
statistics="true" />
<cache
name="services.pricing"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off"
statistics="true" copyOnWrite="true"
copyOnRead="true" />
</ehcache>
<!-- running Main will result in following output.
Fetching prices from DB
[2, 6, 8, 5, 9]
[2, 6, 8, 5, 9]
What do we see here... we only fetch the list of prices the first time.
The second time we get back the cached value. And even although we reversed
the prices the first time it has NO effect on our cached value.
This is in most situations what you want.
-->
view raw gistfile1.xml hosted with ❤ by GitHub

No comments:

Post a Comment