ExpireAfterWrite似乎不起作用

时间:2014-11-13 13:11:22

标签: java caching guava cache-expiration evict

我正在尝试使用Google的Guava Cache,我只是用expireAfterWrite以1分钟的间隔测试它。在我正在开发的应用程序中,有数百个用户。所以我的理解是,当第一个人访问缓存时,我们从数据库填充它,然后一分钟计时器应该开始倒计时。现在缓存已填满。我们总是会在一分钟内从缓存中获取它。一分钟后,它将过期,我们再次从数据库中获取它。

但是,似乎每次我在一分钟内访问缓存时,计时器都会重置。这是为什么?如果我在一分钟内没有访问缓存,则缓存过期并正常工作。

代码:

 Cache<Long, List<someObj>> cacheMap = CacheBuilder.newBuilder().maximumSize(maxSize).expireAfterWrite(maxTime, TimeUnit.MINUTES).removalListener(new RemovalListener<Long, List<someObj>>() {
    public void onRemoval(RemovalNotification<Long, List<someObj>> notification) {
        if (notification.getCause() == RemovalCause.EXPIRED) {
            logger.info("Value " + notification.getValue() + " has been expired");
        } else {
            logger.info("Just removed for some reason");
        }
    }
}).build();

...
//Convert to ConcurrentHashMap
cacheMap = someCache.asMap();

...


public List<someObj> getAllData(final Long orgId, final User user) {
    // Get requests from cache
    List<Request> cacheList = overdueSlaMap.get(orgId);

    // Fill cached requests if null
    cacheList = fillCacheIfNecessary(orgId, cacheList, overdueSlaMap);  

    return cacheList;
}

//Strategy for reading and writing to cache
//This method is fine. On first load, cache is empty so we fill it from database. Wait one minute, value expires and cache is empty again so we get from cache. 
//However, everytime I refresh the page the one minute timer starts again. Surely, the one minute timer within the Guava Cache should not refresh regardless of how many times I access the cache within that one minute period. 
 private List<someObj> fillCacheIfNecessary(List<someObj> cacheList, ConcurrentMap<Long, List<someObj>> cacheMap) {

        // If the cache is empty then we will try to fill it from the database.
        if (cacheList == null) {
            logger.info("populating cache");
            List<someObj> objList = new ArrayList<someObj>();

            // if bla bla
            if (such and such) {
                objList = service.getFromDatabase(someArg);
            } else {
                objList = service.getFromDatabase(anotherarg);
            }

            // Concurrently lock the new cacheList retrieved from the database.
            List<someObj> newValue = Collections.unmodifiableList(objList);

            // Only insert if new value does not exist in cache map
            List<someObj> oldValue = cacheMap.putIfAbsent(orgId, newValue);

            // If old value already exists then we use the old value otherwise we use the new value
            cacheList = ((oldValue != null && !oldValue.isEmpty()) ? oldValue : newValue);
        }
        return cacheList;

    }

修改

我从控制器调用缓存:

public ModelAndView getNotifications(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    User user = getCurrentUser();
    CacheManager cacheManager = new CacheManager();
    List<someObj> objList = new ArrayList<Request>();

    // Get from database
    if (bla bla) {
        objList = getFromDatabase(user);
    }
    // Get from cache
    else {
        Long orgId = user.getUserOrganisation().getId();
        objList = cacheManager.getAllData(orgId, user);      
    }

    return new ModelAndView(SOMEVIEW);
}

传递方法所需的有用数据,该方法将该方法传递给调用数据库的方法。但是,我现在试着重构我的代码以使用LoadingCache,但这要求我覆盖load()方法,该方法只使用一个参数,这是关键。我需要的不仅仅是密钥,我需要在控制器中调用cacheManager,因此它调用适当的方法。什么是加载方法?什么时候使用?什么时候打电话,多久一次?它会使我的其他方法无效吗?

1 个答案:

答案 0 :(得分:1)

我认为您的问题是您使用Map返回的asMap作为缓存。 asMap被记录为Returns a view of the entries stored in this cache as a thread-safe map。此处没有任何内容表明您可以使用Map作为缓存。我建议您更新代码以使用Cache API并直接在缓存上调用putget(key, Callable)

您可能需要考虑使用LoadingCache,因为我发现API更简单。

修改

首先,只有当缓存中不存在密钥时才会调用load方法。缓存调用load方法以获取应为其键返回的值,然后缓存该值以供下次使用。使用CacheLoaderCallable的一个好处是,检索数据的调用是线程安全的,即每个键只调用一次,即使多次调用同一个键也是如此在同一时间。

如果您需要传递更多信息,请创建一个包装器对象:

 public class MyKey{
       final Long orgId; // true key value
       final User user; // other values needed

       // implement equals and hashCode to ONLY use orgId. This will ensure that
       // the same cached value is returned for all requests with the same
       // orgId which is the ACTUAL key.
  }

您也可以使用常规Cache执行相同操作,并创建包含所需值的Callable

我建议CacheLoader使fillCacheIfNecessary无效。对cache.get()的简单调用将在内部确定是否必须检索值或缓存中是否已存在该值。它正在为你做get, check if null, retrieve工作。