ehcache persist to disk issues
Asked Answered
F

8

28

I want to do something with ehcache in Java that I think should be extremely simple, but I've spent enough time frustrating myself with the docs...

  1. Write a value to a disk persistent cache. Shut down.

  2. Start up again and read that value.

Here is my Java function:

private static void testCacheWrite() {

  // create the cache manager from our configuration
  URL url = TestBed.class.getClass().getResource("/resource/ehcache.xml");
  CacheManager manager = CacheManager.create(url);
  // check to see if our cache exits, if it doesn't create it
  Cache testCache = null;
  if (!manager.cacheExists("test")) {
    System.out.println("No cache found. Creating cache...");
    int maxElements = 50000;
    testCache = new Cache("test", maxElements,
      MemoryStoreEvictionPolicy.LFU, true, null, true, 60, 30,
      true, Cache.DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null);
    manager.addCache(testCache);
    // add an element to persist
    Element el = new Element("key", "value");
    testCache.put(el);
    testCache.flush();
    System.out.println("Cache to disk. Cache size on disk: " +
      testCache.getDiskStoreSize());
  } else {
    // cache exists so load it
    testCache = manager.getCache("test");
    Element el = testCache.get("key");
    if (null == el) {
      System.out.print("Value was null");
      return;
    }
    String value = (String) el.getObjectValue();
    System.out.println("Value is: " + value);
  }
  manager.shutdown();
}

And here is my cache configuration (ehcache.xml):

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
  <diskStore path="C:/mycache"/><!-- java.io.tmpdir -->
  <defaultCache
    maxElementsInMemory="10000"
    eternal="true"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    overflowToDisk="true"
    maxElementsOnDisk="10000000"
    diskPersistent="true"
    diskExpiryThreadIntervalSeconds="120"
    memoryStoreEvictionPolicy="LRU" />
</ehcache>

Even though I see test.index and test.data files on disk after the first run, output from this function is always the following (it never seems to load the cache from disk):

No cache found. Creating cache...
Cache to disk. Cache size on disk: 2

I must be doing something dumb here, but I 'm not sure what!

Felizio answered 13/11, 2009 at 14:37 Comment(0)
F
18

Okay, well what I did to fix this was configure my cache using the configuration file. Here is the updated config:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="C:/mycache" />

    <defaultCache
        maxElementsInMemory="10000" 
        eternal="true"
        timeToIdleSeconds="120" 
        timeToLiveSeconds="120" 
        overflowToDisk="true"
        maxElementsOnDisk="10000000" 
        diskPersistent="true"
        diskExpiryThreadIntervalSeconds="120" 
        memoryStoreEvictionPolicy="LRU" />

    <cache 
        name="test" 
        maxElementsInMemory="500" 
        eternal="true"
        overflowToDisk="true" 
        timeToIdleSeconds="300" 
        timeToLiveSeconds="600"
        diskPersistent="true" 
        diskExpiryThreadIntervalSeconds="1"
        memoryStoreEvictionPolicy="LFU" />

</ehcache>

So basically I didn't use the constructor to define the cache.

I suppose this will work, but I still wonder why programatically defined caches can't persist on disk (especially since they are still written to disk!).

Thanks for the comments guys.

Felizio answered 13/11, 2009 at 16:38 Comment(1)
can you help me for this question. #45295779Ern
S
5

After spending some quality time with the debugger, I believe I have an answer for the OP.

The issue (at least from what I have seen) centers around the non-clustered disk cache files and how they get read back in. In the file net.sf.ehcache.store.compound.factories.DiskPersistentStorageFactory.java, the method:

public DiskPersistentStorageFactory(Ehcache cache, String diskPath) {
    super(getDataFile(diskPath, cache), cache.getCacheConfiguration().getDiskExpiryThreadIntervalSeconds(),
            cache.getCacheConfiguration().getDiskSpoolBufferSizeMB(), cache.getCacheEventNotificationService(), false);

    indexFile = new File(getDataFile().getParentFile(), getIndexFileName(cache));
    flushTask = new IndexWriteTask(indexFile, cache.getCacheConfiguration().isClearOnFlush());

    if (!getDataFile().exists() || (getDataFile().length() == 0)) {
        LOG.debug("Matching data file missing (or empty) for index file. Deleting index file " + indexFile);
        indexFile.delete();
    } else if (getDataFile().exists() && indexFile.exists()) {
        if (getDataFile().lastModified() > (indexFile.lastModified() + TimeUnit.SECONDS.toMillis(1))) {
            LOG.warn("The index for data file {} is out of date, probably due to an unclean shutdown. " 
                    + "Deleting index file {}", getDataFile(), indexFile);
            indexFile.delete();
        }
    }

    diskCapacity = cache.getCacheConfiguration().getMaxElementsOnDisk();
    memoryCapacity = cache.getCacheConfiguration().getMaxElementsInMemory();
    memoryPolicy = determineEvictionPolicy(cache.getCacheConfiguration());
}

checks the timestamps on the data files. The problem I am seeing is that no matter how I end up shutting down the cache/manager, the files are never get synchronized properly. My quick and dirty workaround was to adjust the time of the data file to be just past the timestamp on the index file:

File index = new File( path, name + ".index" );
File data  = new File( path, name + ".data"  );

data.setLastModified( index.lastModified() + 1 );

Granted, this is not elegant, but it serves my needs, as our project uses clustered caches, and this allows me to debug standalone with a persistent cache...and without having to actually run Terracotta locally.

One caveat is that for non-clustered caches, I do have to flush() after every put() and remove() in order to keep the disk image fresh, especially when debugging due to the lack of shutdown support when you just "pull the plug".

Staford answered 10/9, 2010 at 21:51 Comment(1)
Nice find. At least I know why it was happening now.Felizio
E
4

This took me a while to figure out, but basically what needs to be done here is creating the CacheManager accordingly.

If you create the cache manager and the caches the same way how you created it in the xml it will work.

net.sf.ehcache.CacheManager manager = net.sf.ehcache.CacheManager
        .create(new Configuration().diskStore(
            new DiskStoreConfiguration().path("C:/mycache")
        )
        .cache(new CacheConfiguration()
            .name(testName)
            .eternal(true)
            .maxBytesLocalHeap(10000, MemoryUnit.BYTES)
            .maxBytesLocalDisk(1000000, MemoryUnit.BYTES)
            .diskExpiryThreadIntervalSeconds(0)
            .diskPersistent(true)));
Elison answered 4/11, 2014 at 15:56 Comment(1)
I do not think this answer applies. This issue is specifically about disk persistent caches that do not persist on the second startup after they have been shut down.Felizio
N
2

this might be a little late but i had the same problem: what helped was to shutdown the cache manager.

(from the docu: http://ehcache.org/documentation/code-samples#ways-of-loading-cache-configuration)

Shutdown the singleton CacheManager:

CacheManager.getInstance().shutdown();

Shutdown a CacheManager instance, assuming you have a reference to the CacheManager called :

manager.shutdown();
Nightcap answered 27/12, 2012 at 11:3 Comment(1)
Yes I second this. However it's not always possible to shutdown the cache manager if the application is terminated abruptly. What we ended up doing is launch a scheduled thread that calls the flush() method on the cache. Do not do it with non-persistent caches however because it will clear them.Schleswig
K
1

I think you should remove the manager.cacheExists(..) test and simply create your cache using testCache = manager.getCache("test"); instead of using new Cache(..). Even if your cache is diskPersistent, it won't exist until you get it the first time. (At least that's what I think as I'm only using getCache(..) and it does exactly what you are looking for)

Note:

You could also add something like this to make sure the cache exists:

Cache cache = manager.getCache(name);
if (cache == null) {
    throw new NullPointerException(String.format("no cache with name %s defined, please configure it in %s", name, url));
}

Note 2:

If your configuration file is called ehcache.xml, you shouldn't use CacheManager.create(url). Instead use the CacheManager singleton: I think I've confused using CacheManager.create(url) with and using new CacheManager(url). Still, you should use the singleton for ehcache.xml and new CacheManager(url) for anything else.

// ehcache.xml - shared between different invocations
CacheManager defaultManager = CacheManager.getInstance();
// others - avoid calling twice with same argument
CacheManager manager = CacheManager.create(url);

Using CacheManager.create(..) is problematic as it might completely ignore the passed URL if any of the create(..) methods or getInstance() have been called before:

public static CacheManager create(URL configurationFileURL) throws CacheException {
    synchronized (CacheManager.class) {
        if (singleton == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Creating new CacheManager with config URL: " + configurationFileURL);
            }
            singleton = new CacheManager(configurationFileURL);

        }
        return singleton;
    }
}

That's why I wouldn't recommend using any of the CacheManager.create(..) methods. Use CacheManager.getInstance() or new CacheManager(url).

Kluge answered 13/11, 2009 at 14:56 Comment(10)
If manager.getCache("test") doesn't return null, then manager.cacheExists(..) should be true.Plasticity
@Pascal I've just looked at the code and you're right. Nevertheless, the check shouldn't be necessary. So @skaffman might be right and ehcache.xml wasn't located correctly.Kluge
The OP is creating the cache programmatically so, to me, the check is perfectly fine (the first time, the cache doesn't exist). Then, regarding your Note 2, what's the problem with using CacheManager.create(url) even if the configuration file is called ehcache.xml (btw, ehcache.xml isn't located at the root of the classpath here)?Plasticity
When I change the location of the configuration file to something invalid, I get warnings on my console output, so I am pretty sure everything is kosher that way. When I leave out the check to see if the cache exists, I get a null pointer exception when I try loading a value from it (the manager says there are 0 caches on the second go-around). Maybe I need to create the cache in the config file instead of programatically?Felizio
@Pascal As long as ehache is used with a configuration file, creating the Cache programmatically is the problem itself. This should be left to the CacheManager (and the config it reads from the given file instead of having different configuration in code and ehcache.xml). So the OP should either use a CacheManager or create Caches programmatically. @Felizio see my edits above: try using new CacheManager(url) instead. Maybe the singleton CacheManager has already been created with another URL.Kluge
Sorry, I should have said: either use a configuration file or create Caches programmaticallyKluge
@Kluge I simply do not agree. To me, there is always a ehcache.xml, be it the failsafe one, and the OP might want to configure the DiskStore path. So no, creating a Cache programmatically doesn't exclude using the a configuration file for the CacheManager.Plasticity
@Pascal I think I get what you mean: You say it's okay to create a Cache programmatically as a) a fallback if no cache with this name is configured or b) to override (ignore) the configuration from ehcache.xml completely. That's what you meant, right? In this case, I do agree with you. However, I think what the OP was expecting is that a disk persistent cache has to be created the first time but already exists on consecutive runs. At least that's how I understood it. Otherwise, posting new Cache(..) together with an ehcache.xml wouldn't make much sense. @Felizio Did I get you wrong there?Kluge
damn ... on second (or third?) read, I think I really messed this one up :) Sorry ...Kluge
@Kluge Yes, that's why I meant.Plasticity
G
1

Small hint if your cache on disk stays empty: Be sure your elements in the cache are serializable. ehcache does log if thats not the case but my log settings did not print out these log entries.

Guienne answered 14/6, 2012 at 10:12 Comment(0)
S
1

I had and resolved a similar issue.

I want to configure ehcache to have a given cache persisting elements on disk. But I want to do it only in local environment (production environment works with a distributed persistence) so I switch the configuration programmatically when the application starts (a web application in my case)

File configurationFile = new File(event.getServletContext().getRealPath(EHCACHE_CONFIG_PATH));    
Configuration configuration = ConfigurationFactory.parseConfiguration(configurationFile);

//...doing other stuff here...

CacheConfiguration cacheConfiguration = configuration.getCacheConfigurations().get("mycachename");
if(localEnvironment){    
    cacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.DISTRIBUTED));
}else{
    //siteCacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.LOCALRESTARTABLE));
    //deprecated lines..
    siteCacheConfiguration.setDiskPersistent(true);
    siteCacheConfiguration.setOverflowToDisk(true);
}

I had problem with the commented line siteCacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.LOCALRESTARTABLE)), in fact Ehcache code (I'm using ehcache-2.6.11) throws an Exception if you use Strategy.LOCALRESTARTABLE without an enterprise version of the jar:

CacheException: You must use an enterprise version of Ehcache to successfully enable enterprise persistence.

Digging into the code I realized that these two (deprecated) lines do the same eluding the entreprise version Exception

siteCacheConfiguration.setDiskPersistent(true);
siteCacheConfiguration.setOverflowToDisk(true);

Remember to add CacheManager.getInstance().shutdown() on the shutdown of the application!

Hope this helps.

Shae answered 9/2, 2017 at 15:31 Comment(1)
This works normally fine except when your application/process is killed by the OS with a SIGKILL. In this case, the shutdown hook will not be executed and it is very likely that your persisted cache files (foo.data and foo.index) will get corrupted and will not populate the cache again on startup. Instead, they will be discarded and deleted and will lose the persisted info. Calling explicitly CacheManager.getCache("foo").flush() can help mitigate this issue somehow.Pantaloons
P
0

I suppose this will work, but I still wonder why programatically defined caches can't persist on disk (especially since they are still written to disk!)

My understanding is that a programmatically created cache (i.e. not declared in ehcache.xml) can use a DiskStore that can itself be persistent but this doesn't mean that this cache will be loaded automatically by the CacheManager uppon restart. Actually, I don't think the previously mentioned files do contain the cache parameters.

But, if you "recreate" the cache programmatically with the same parameters, you'll find the previously cached entries back from the DiskStore.

Plasticity answered 13/11, 2009 at 16:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.