2011-03-07 38 views
2

我们最近开始使用EHCache和Terracotta为应用程序数据运行分布式数据缓存。假设任何客户端节点都有大约2GB的堆,而服务器节点有8GB。我们产生了大量的数据,每天大约1.5GB。使用兵马俑的EHCache如何处理从分布式堆中驱逐?

一般来说,任何一个客户端都会使用特定日期的数据集(约1.5GB),但服务器显然必须持有所有这些数据集。

我希望到期工作的方式是在LRU基础上,当堆增长到很大时。因此,如果任何特定的L1客户端缓存变得太大(例如,从第1天切换到第2天),我预计它会从L1中逐出所有第1天的数据。如果L2在得到第6个数据集时变得太大,则最早的数据集会完全失效。对于生活时间或闲暇时间值应该如何,我并没有真正的意见,所以我不打算让它们休息。

看了几天后,我不认为这是按我的预期工作。例如,我运行了一个测试,其中L2 max的元素为4.我用四个元素填充它。然后我去了第五个元素。 Cache.put()返回时没有异常,但紧跟在后面的Cache.get()使用相同的键返回null!

所以我的问题是,我如何获得EHCache +兵马俑做我想做的事情,或者至少有什么东西靠近?

回答

1

经过大量调试后发现,神秘的行为取决于L1缓存是否知道L2缓存中的内容。附加的单元测试旨在成对执行,对中的每个元素都在单独的JVM中运行,以避免EHCache单例。

我认为一个行为良好的L1-> L2关系应该工作的方式是,如果你没有错误地执行.put(),你应该能够在没有错误的情况下立即执行get()一个问题(假设没有其他同时运行的线程正在搞东西)。然而,对于Terracotta EHCache,如果.put()需要某些东西被驱逐,驱逐不会发生,并且put()会被默默地忽略,除非L1客户端“知道”可以驱逐的密钥。在* JVM2测试中,它使用.getAllWithLoader()查找其他键,然后* JVM2测试按预期工作。

因此,我们所做的解决我们原来的问题的方法是使客户端定期执行.getAllWithLoader()。然后我们可以确定所有的驱逐规则都遵循了。

package com.mine; 

import java.io.Serializable; 

import junit.framework.TestCase; 
import net.sf.ehcache.Cache; 
import net.sf.ehcache.CacheManager; 
import net.sf.ehcache.Element; 
import net.sf.ehcache.config.CacheConfiguration; 
import net.sf.ehcache.config.Configuration; 
import net.sf.ehcache.config.TerracottaClientConfiguration; 
import net.sf.ehcache.config.TerracottaConfiguration; 

public class CacheEvictionTest extends TestCase { 

    private static final String SERVER = "localhost:9510"; 
    private CacheManager cacheManager; 
    private Cache cache; 
    private final Serializable keyA = "a"; 
    private final Serializable keyB = "b"; 
    private final Serializable keyC = "c"; 

    @Override 
    protected void setUp() throws Exception { 
     Configuration configuration = new Configuration(); 
     TerracottaClientConfiguration terracottaConfig = new TerracottaClientConfiguration(); 
     terracottaConfig.setUrl(SERVER); 
     configuration.addTerracottaConfig(terracottaConfig); 

     int maxElementsInMemory = 1; 
     int maxElementsOnDisk = 2; 
     long timeToIdleSeconds = 15; 
     long timeToLiveSeconds = 15; 
     String cacheName = "TEST_CACHE"; 
     CacheConfiguration amoebaCache = new CacheConfiguration(cacheName, maxElementsInMemory).statistics(true) 
         .terracotta(new TerracottaConfiguration()) 
         .logging(true) 
         .maxElementsOnDisk(maxElementsOnDisk) 
         .timeToIdleSeconds(timeToIdleSeconds) 
         .timeToLiveSeconds(timeToLiveSeconds); 
     configuration.addCache(amoebaCache); 
     configuration.addDefaultCache(new CacheConfiguration("default", 0)); 

     cacheManager = new CacheManager(configuration); 
     cache = cacheManager.getCache(cacheName); 
    } 

    @Override 
    protected void tearDown() throws Exception { 
     if (cache != null) { 
      cache.removeAll(); 
      cache.clearStatistics(); 
     } 
    } 

    public void testMaxElementOnDiskEvictionJVM1() throws Exception { 
     cache.clearStatistics(); 

     cache.put(new Element(keyA, keyA)); 
     cache.put(new Element(keyB, keyB)); 

     cache = null; 
    } 

    public void testMaxElementOnDiskEvictionJVM2() throws Exception { 
     assertEquals(2, cache.getSize()); 

     for (Object key : cache.getKeys()) { 
      cache.get(key; 
     } 

     cache.put(new Element(keyC, keyC)); 

     assertEquals(2, cache.getSize()); 
     assertNotNull(cache.get(keyC)); 
    } 

    public void testEvictsExpiredElementsFromDiskWhenNotInMemoryAndWeNeverKnewAboutItJVM1() throws Exception { 
     cache.clearStatistics(); 
     cache.put(new Element(keyA, keyA)); 

     cache = null; 
     cacheManager = null; 
    } 

    public void testEvictsExpiredElementsFromDiskWhenNotInMemoryAndWeNeverKnewAboutItJVM2() throws Exception { 
     cache.clearStatistics(); 

     for (Object key : cache.getKeys()) { 
      cache.get(key; 
     } 
     assertEquals(0, cache.getStatistics().getEvictionCount()); 

     Thread.sleep(20000); 

     assertEquals(1, cache.getStatistics().getEvictionCount()); 
    } 
}