4

Heyho,

currently using guava to cache users. So my question is whether it is possible with guava to rescrict the eviction of an entry if the cached object has a specific attribute value. For example if user.isOnline() returns true the user is not evicted even if he hasn't been accessed for a specific amount of time (.expireAfterAccess(KEEP_LOADED, TimeUnit.SECONDS)).

EDIT:

I found this in the javadoc of CacheBuilder.weighter(...):

When the weight of an entry is zero it will not be considered for size-based eviction (though it still may be evicted by other means).

But I don't want to evict my user in any way if he is online.

Basically I cache my users this way:

        this.cache = CacheBuilder.newBuilder()
            .maximumSize(MAX_CACHED_USERS)
            .expireAfterAccess(KEEP_LOADED, TimeUnit.SECONDS)
            .build(new UserLoader(core));

EDIT:

ok, I could use the weighter to limit the size of my cache. For every user who is online I could return 0 and for every else 1. This would cause the cache to keep online users. But if would be nice to use maximumSize and expireAfterAccess. But CacheBuilder.weighter(...) does not block expireAfterAccess(...) setting to evict old users.

EDIT:

Maybe it's possible to cancel the eviction in some way, but I'm not sure how :/

Related to: Is it safe to reinsert the entry from Guava RemovalListener?

Max

Community
  • 1
  • 1
maxammann
  • 1,018
  • 3
  • 11
  • 17
  • Can you show how you are using Guava to cache users so we have more context? – tuckermi Jul 23 '13 at 14:49
  • write a reload method on your CacheLoader implementation that returns the given "old" instance of user.isOnline() is true. – Ray Jul 23 '13 at 15:00
  • @Ray soo reload() is called when a cached object is evicted? – maxammann Jul 23 '13 at 15:04
  • Because there is no information in the javadocs: Computes or retrieves a replacement value corresponding to an already-cached key. This method is called when an existing cache entry is refreshed by CacheBuilder.refreshAfterWrite, or through a call to LoadingCache.refresh. – maxammann Jul 23 '13 at 15:05
  • 4
    No, there's not a way to do this like you're trying to do. It almost sounds like you should be doing an explicit `invalidate` when a user goes offline, though. – Louis Wasserman Jul 23 '13 at 16:22
  • @LouisWasserman Possible yeah but my goal was to keep the user a specific period of time after a disconnect, for example if the user just reconnects – maxammann Jul 23 '13 at 16:59
  • I think I'll just set a max weight of the cache and remove the time based eviction. Thanks although, if you wan't you can post your comment as answer and I give you a star. – maxammann Jul 23 '13 at 17:01

1 Answers1

1

The question you link to directly describes how to handle this pattern.

I'd use one map and one cache. Use the map for jobs still in progress, and perhaps use the cache's RemovalListener remove entries from the map?

Put simply, if there is data you don't want to be evicted, it doesn't belong in an evicting data structure.

For your use case you'd maintain a map of logged-in users and a cache of recently-logged-in users. While a user is logged in they're stored in a standard map. When they log out their object is moved to the cache, which will eventually evict them.

You can (and should) hide this behavior inside an object containing both the map and the cache. This might look something like:

public class UserStore {
  private final ConcurrentHashMap<Key, User> loggedIn;
  private final LoadingCache<Key, User> recentlyAccessed;

  ...

  public User getUser(Key userKey) {
    User user = loggedIn.get(userKey);
    if (user != null) {
      return user;
    }
    return recentlyAccessed.getUnchecked(userKey); // or .get() if necessary
  }
}
dimo414
  • 47,227
  • 18
  • 148
  • 244