2011-03-07 36 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()); 
    } 
}