Re: [guava] Cache with CacheLoader for expiring data

675 views
Skip to first unread message

Louis Wasserman

unread,
Sep 11, 2012, 12:35:06 PM9/11/12
to Martin Schayna, guava-...@googlegroups.com
Use something like Cache<String, Optional<String>> instead of using null to indicate "there actually isn't any value anywhere, it's absent from Memcached."

Louis Wasserman
wasserm...@gmail.com
http://profiles.google.com/wasserman.louis


On Tue, Sep 11, 2012 at 9:20 AM, Martin Schayna <msch...@gmail.com> wrote:
Hi,

I want to implement something like 2nd level cache in front of Memcached server for caching security tokens in my API. I choose Cache<String, String> with small on-read expiration interval and appropriate CacheLoader<String, String> for loading values from Memcached server. When key expires in guava's Cache, CacheLoader tries to load it from Memcached, but when key expires there, whole cache must return null. This is regular use, token just expires. But CacheLoader.load(key) method cannot return null.

I can wrap key with something but I cannot cache nulls because expired tokens are pretty useless in 2nd level cache.

How can I use Cache with CacheLoader for expiring data like security tokens?

Thank you.

--
guava-...@googlegroups.com
Project site: http://guava-libraries.googlecode.com
This group: http://groups.google.com/group/guava-discuss
 
This list is for general discussion.
To report an issue: http://code.google.com/p/guava-libraries/issues/entry
To get help: http://stackoverflow.com/questions/ask (use the tag "guava")

Maaartin G

unread,
Sep 11, 2012, 12:50:11 PM9/11/12
to guava-...@googlegroups.com, Martin Schayna
On Tuesday, September 11, 2012 6:35:33 PM UTC+2, Louis Wasserman wrote:
Use something like Cache<String, Optional<String>> instead of using null to indicate "there actually isn't any value anywhere, it's absent from Memcached."

You seem to have ignored his sentence "expired tokens are pretty useless in 2nd level cache".

I'd say that they're not completely useless as they prevent the access to the next level, which may save no to a lot of time, depending on the access pattern.

What actually happens when load returns null?

Louis Wasserman

unread,
Sep 11, 2012, 12:55:24 PM9/11/12
to Maaartin G, guava-...@googlegroups.com, Martin Schayna
The relevant lines of the code are

if (value == null) {
    throw new InvalidCacheLoadException("CacheLoader returned null for key " + key + ".");

Louis Wasserman

unread,
Sep 11, 2012, 1:00:15 PM9/11/12
to Maaartin G, guava-...@googlegroups.com, Martin Schayna
Just to be clear though, it's not 100% clear to me what behavior you expect when cache.get is called on a key which was previously absent in Memcached.  Do you expect...

a) Query Memcached again to see if the value has been re-added
b) Never query Memcached again, and always return "absent"

If b), I'm wondering if maybe you could use refreshing instead of expiration; then you could specify in CacheLoader.reload that when the value was absent, that it should stay absent.

Martin Schayna

unread,
Sep 12, 2012, 4:03:20 AM9/12/12
to guava-...@googlegroups.com, Maaartin G, Martin Schayna
Thanks for valuable answers. 

I try to explain my motivation:

1) user calls login to API and obtain security token, token is written to Memcached server with user ID as value (expiration 1 hour) and Cache<String, User> instance (read-expiration 1 minute)
2) user calls API within 1 minute interval, passed token is in Cache instance and User object is known, ok
3) user calls API after 2 or more minutes, token is not found in Cache instance but CacheLoader loads user ID from Memcached and User object must be loaded from database, ok
4) from this point for 1 minute is User object held in Cache instance
5) user calls API after 1 hour, token is not found in Cache neither Memcached, error is returned to user and user has to relogin
6) from this point expired security token is not in use anymore

Sure, I can use your advice with Optional wrapper, it has only small footprint -- after returning error expired token will be placed for 1 minute in Cache instance. But would not, if CacheLoader supports something like "not found" in load(key) method.

Any idea?

Martin Schayna

Maaartin G

unread,
Sep 12, 2012, 6:39:11 AM9/12/12
to guava-...@googlegroups.com, Maaartin G, Martin Schayna
On Wednesday, September 12, 2012 10:03:20 AM UTC+2, Martin Schayna wrote:
Thanks for valuable answers. 

I try to explain my motivation:

1) user calls login to API and obtain security token, token is written to Memcached server with user ID as value (expiration 1 hour) and Cache<String, User> instance (read-expiration 1 minute)
2) user calls API within 1 minute interval, passed token is in Cache instance and User object is known, ok
3) user calls API after 2 or more minutes, token is not found in Cache instance but CacheLoader loads user ID from Memcached and User object must be loaded from database, ok
4) from this point for 1 minute is User object held in Cache instance
5) user calls API after 1 hour, token is not found in Cache neither Memcached, error is returned to user and user has to relogin

I could imagine returning `null` and wrapping the `InvalidCacheLoadException` into a `TokenExpiredException`... but this is just a hack.
 
6) from this point expired security token is not in use anymore

Doesn't it also mean that (assuming using `Optional`) the number of expired tokens in the cache is always quite low and thus the problem can be simply ignored?

Sure, I can use your advice with Optional wrapper, it has only small footprint

Using `Optional` makes all your cache entries bigger by a couple of bytes.. You could use the Null Object Pattern instead, but it's actually a sort of antipattern.
 
-- after returning error expired token will be placed for 1 minute in Cache instance.

Wherever you get such a token from the cache, you could remove it manually. But I doubt it's worth it.
 
But would not, if CacheLoader supports something like "not found" in load(key) method.

But how?

- There's nothing it could return as the result must be of type `V` which is unknown to the cache / loader.
- It can't call a method working like `AbstractIterator.endOfData` as the loader has no reference to the cache.
- It could throw a `NoSuchElementCacheLoadException` which could be cleanly processed in the request. With this exception extending `InvalidCacheLoadException` it could make sense.

Martin Schayna

unread,
Sep 12, 2012, 8:38:41 AM9/12/12
to guava-...@googlegroups.com, Maaartin G, Martin Schayna
Thanks for inspiring post. Finally I have used something like suggested `Optional` pattern (in fact my cache is Cache<String, Principal> where Principal has method getUser() which can return null). I can live with couple of useless entries for couple of minutes in my cache :-)

But your last idea throwing `NoSuchElementCacheLoadException` in LoadCache.load(key) looks promising, however `InvalidCacheLoadException` is final class thus cannot be extended.

I can imagine that in some of future versions of guava will be possible to throw such (checked?) exception from CacheLoader.load(key) and Cache.get() returns null in that case.

Martin Schayna

Thomas O

unread,
Sep 12, 2012, 9:40:27 AM9/12/12
to guava-discuss
> You could use the Null Object Pattern instead, but it's actually a sort of
> antipattern.
Care to explain why the Null Object Pattern is sort of an antipattern?

Kevin Bourrillion

unread,
Sep 12, 2012, 9:57:36 AM9/12/12
to Martin Schayna, guava-...@googlegroups.com, Maaartin G
I don't think InvalidCacheLoadException has anything to do with anything here. Think of that class like Error. Just don't go there.

The loader can throw any Exception you want to, such as your TokenExpiredException or whatever, directly. That's the way to represent an "invalid cache load".





--
Kevin Bourrillion | Java Librarian | kev...@google.com | 650-450-7126

Maaartin G

unread,
Sep 12, 2012, 12:52:14 PM9/12/12
to guava-...@googlegroups.com, Martin Schayna, Maaartin G
On Wednesday, September 12, 2012 3:57:59 PM UTC+2, Kevin Bourrillion wrote:
I don't think InvalidCacheLoadException has anything to do with anything here. Think of that class like Error. Just don't go there.

Currently not - that's what I described as hack. What I've meant was an idea for you:

Create a `NoSuchElementCacheLoadException` and let the users throw it in cases like this (i.e., there's no value to be loaded and this information should not be stored). Handle it appropriately (which assuming that it extends `InvalidCacheLoadException` can mean just propagate when loaded synchronously). I don't claim the idea is any good -- just a possible solution for cases like this; probably not important enough.

On Wednesday, September 12, 2012 3:40:29 PM UTC+2, Thomas O wrote:
> You could use the Null Object Pattern instead, but it's actually a sort of 
> antipattern. 
Care to explain why the Null Object Pattern is sort of an antipattern?

Every piece of program must know about it. It's easy to forget to handle it and compute some nonsense with it. Sometimes the Null Object works nicely - when you ask it to do something it can throw an exception or do nothing, whatever is right. Sometimes you just add it to a collection as if it was a proper object and it has no change to protest.

Kevin Bourrillion

unread,
Sep 12, 2012, 2:04:38 PM9/12/12
to Maaartin G, guava-...@googlegroups.com, Martin Schayna
On Wed, Sep 12, 2012 at 9:52 AM, Maaartin G <graj...@seznam.cz> wrote:
On Wednesday, September 12, 2012 3:57:59 PM UTC+2, Kevin Bourrillion wrote:
I don't think InvalidCacheLoadException has anything to do with anything here. Think of that class like Error. Just don't go there.

Currently not - that's what I described as hack. What I've meant was an idea for you:

Create a `NoSuchElementCacheLoadException` and let the users throw it in cases like this (i.e., there's no value to be loaded and this information should not be stored).

Why would we need to create a special exception?  The user can already throw whatever they want.  They may even have an exception that can convey useful some information.


> You could use the Null Object Pattern instead, but it's actually a sort of 
> antipattern. 
Care to explain why the Null Object Pattern is sort of an antipattern?

Every piece of program must know about it. It's easy to forget to handle it and compute some nonsense with it. Sometimes the Null Object works nicely - when you ask it to do something it can throw an exception or do nothing, whatever is right. Sometimes you just add it to a collection as if it was a proper object and it has no change to protest.

I'd put this as: some types lend themselves naturally to a null object, and some don't. Shoehorning a null object into one of the latter kind is the anti-pattern.

Martin Schayna

unread,
Sep 13, 2012, 8:11:24 AM9/13/12
to guava-...@googlegroups.com, Maaartin G, Martin Schayna
I have tried to throw my own exception NoSuchToken in CacheLoader.load(key). This caused UncheckedExecutionException in Cache.get(key) with NoSuchToken exception in getCause(). 

Q1: Is this really intended use how to break loading data from CacheLoader?

Q2: Can I rely on throwing UncheckedExecutionException in this case? It seems that this exception is too general and related with asynchronous computing in guava.

Thanks again to guava devs and community!

M.
Reply all
Reply to author
Forward
0 new messages