0

I use JSR 107 JCache in GAE to temporarily store data captured from web and write it to datastore at certain interval (10 mins). My app only use one cache. Most of the time it works well. Once in a while (5-6 times out of 4,000 per day) one of the entries in the cache got missing due to unknown reason. I am not very faimilar with JCache but I somehow understand my app may run at different JVM instance and different JCache instance may be used by my app.

The problem can be found in the log below:

2013-12-31 09:58:14.229 /cron/<myservlet>?code=11 200 ........... app_engine_release=1.8.8 instance=00c61b117c1599a88baa456ae838cbfa9b0f28
2013-12-31 09:58:14.011 <myapp>.RealCache validate: realCache 29829270 retrieved
2013-12-31 09:58:14.117 <myapp>.RealCache validate: Cache contains 0, 1, 2, 3, 4, 5, 6, 8, 11, 12, 13,
2013-12-31 09:58:14.121 <myapp>.RealCache get: Cache 11 has ...... <expected result>......
2013-12-31 09:58:14.121 cron.<myservlet> doGet: Appended with <mydata> for 11
2013-12-31 09:58:14.121 <myapp>.RealCache validate: realCache 29829270 retrieved
2013-12-31 09:58:14.224 <myapp>.RealCache validate: Cache contains 0, 1, 2, 3, 4, 5, 6, 8, 11, 12, 13, 

2013-12-31 09:58:14.443 /cron/<myservlet>?code=6 200 ............ app_engine_release=1.8.8 instance=00c61b117c1599a88baa456ae838cbfa9b0f28
2013-12-31 09:58:14.227 <myapp>.RealCache validate: realCache 29829270 retrieved
2013-12-31 09:58:14.326 <myapp>.RealCache validate: Cache contains 0, 1, 2, 3, 4, 5, 6, 8, 11, 12, 13, 
2013-12-31 09:58:14.329 <myapp>.RealCache get: Cache 6 has ..... <expected result>......
2013-12-31 09:58:14.329 cron.<myservlet> doGet: Appended with <mydata> for 6
2013-12-31 09:58:14.329 <myapp>.RealCache validate: realCache 29829270 retrieved
2013-12-31 09:58:14.437 <myapp>.RealCache validate: Cache contains 0, 2, 3, 4, 5, 6, 8, 11, 12, 13, 

These are 2 executions of my servlet. As you can see, they run only 0.2 sec apart and on same GAE instance. "Cache contains 0-13" is the key of the Map in realCache. "realCache 29829270 retrieved" is the hashcode of the Cache I use. There are two of them, one is logged when I get() the cache, another one when I put(). You can see that in the second execution, the "Cache contains ..." are different in get() and put(), the key "1" is missing. These two executions were run ok because the missing key "1" is not involved. But problem appeared in a later execution when "code=1" where the previously accummulated data for "1" is missing. You can see what I want to do and my problem in the coding below.

Here are my coding (logging code skipped for simplicity):

public class RealCache {
    private static Cache realCache;
    public static synchronized void validate() {
        realCache = CacheManager.getInstance().getCache(Constants.Whale);
        if (realCache == null) {    //for first time of the day
             CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
             Map<String, Integer> props = new HashMap<>();
             props.put(GCacheFactory.EXPIRATION_DELTA, 28800);   //8 hrs is enough
             realCache = cacheFactory.createCache(Collections.emptyMap());  
             CacheManager.getInstance().registerCache(Constants.Whale, realCache);
         }
    }//end validate
    public static synchronized MyObj get(int code) {
        validate();
        MyObj myObj = (MyObj) realCache.get(code);
        return myObj;
    }//end get
    public static synchronized void put(int code, MyObj myObj) {
        validate();
        realCache.put(code, myObj);
    }//end put
}//end RealCache

Here is the code when I use RealCache:

synchronized ("RealCache") {
    MyObj myObj = RealCache.get(code);
    if (myObj != null) {
        myObj.setData(myObj.getData() + newData);
        log.info("Appended with "+ newData+ " for "+ code);
    } else {
        myObj = new MyObj();
        myObj.setData(newData);
        log.warning("Created with "+ newData+ " for "+ code);
    }
    RealCache.put(code, myObj);
}//end sync

Please advise what's wrong with my coding.

senderj
  • 400
  • 1
  • 9

1 Answers1

0

You should do some reading of various docs on appengine. JCache is a standard cache api implementation that on appengine utilises memcache. See the first line of the docs for JCache on appengine https://developers.google.com/appengine/docs/java/memcache/usingjcache .

Memcache on appengine has no guarantee of entries surviving in the cache. They can be evicted at any time and with not notice. Please read up on memcache behaviour on appengine.

Specifically google states - "In general, an application should not expect a cached value to always be available." https://developers.google.com/appengine/docs/python/memcache/

Tim Hoffman
  • 12,976
  • 1
  • 17
  • 29
  • Thanks for the reply. I did read the lines. But as an ordinary developer's preception, I interpreted "no guarantee ... surviving" is only for cache without activity for a while. In my case, if we are talking about Cache instance, it was accessed 0.2 sec before the problem occurs. If we are talking about the cache key in the Cache instance, it was used 1 min ago according to the design. Cache with such a short life span won't have any practical use at all. – senderj Jan 02 '14 at 02:43
  • cache life time varies for a lot of reasons. However it is just a cache with a LRU behaviour. But you cannot depend on anything being present. Even the paid exclusive memcache can have stuff evicted if they shift your instances around. You won't get any notice. You must write your application to be resilient in the face of cache eviction. – Tim Hoffman Jan 02 '14 at 05:07
  • I get around a 90% cache hit ratio. If you have lots of entities in the cache and they aren't being read frequently then they will get evicted. – Tim Hoffman Jan 02 '14 at 05:08
  • Thanks Tim for the reply. My cache was frequently get() and put() as can be seen in my log. That's why I expected it should still be here. Anyway, any technique about resilient in this regards. My app has 11 entries in the cache map and each key is accessed once every minute for a few hours each day. I need the cache to last at least 10 mins under this frequency of access. – senderj Jan 02 '14 at 07:01