Closed alexeyOnGitHub closed 6 years ago
In this case you would use a "capturing lambda expression". That would be like using Guava's where the anonymous class captures the surrounding environment. So you could do,
cache.get(userId, key -> {
return userService.get(userId, requestContext);
});
The benefit of non-capturing lamdas is that an instance can be cached by the JVM to avoid additional allocations. That limits its usefulness, so a capturing one is valid and the Function
is merely to help give you the choice. The only annoyance is checked exceptions, but utilities like jOOL's Unchecked.function
cover that pretty well.
You might be interested in https://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood
that would work, thank you!
I may have over-simplified the source code example to get my point across. we are actually using async operations to load values from external systems, and I am wondering if I can use this "get with loader" approach with Async Cache.
I see there is AsyncLoadingCache that works with java futures, but it requires declaring a loader when it is created. and regular Cache class only works with sync operations.
if there a way to have something like AsyncCache
with get(key, loader) operation?
I guess a workaround may be providing a dummy no-op AsyncCacheLoader
when creating an instance using Caffeince...buildAsync() and then use a real loader in get(key, loader)
operation.
the downside would be that all get(key)
calls will always return null
and you need to remember to only use get(key, loader)
for this cache.
Yeah, you can use a dummy loader and avoid using get(key)
. The get(key, func)
should do what you want.
I didn't provide an AsyncCache
initially so that I could understand why it might be helpful. The most common reason users use Cache
instead of LoadingCache
is poor - they do a racy getIfAbsent
=>compute=>put
instead of an atomic get
that computes for them. For async code this is perhaps more valuable, since the code is more explicitly concurrent than the synchronous case.
I did layout the interface to make it easy to add AsyncCache
later. I've seen a few integrations (e.g. Akka's) that would have wanted that, using a dummy loader as required, to bind to their caching APIs. There's been enough valid use-cases that I think it makes sense to add the interface now.
I see the code I am working with also uses .getIfPresent()
on that cache. with this workaround this method will always return false.
I can change .getIfPresent()
call to get(key, dummyLoader)
- the idea is to return the value if present, and return null if not found (since the loader will be a NO-OP thingy).
implementing AsyncCache
may be useful to skip these workarounds.
I think what you want is,
final AsyncLoadingCache<UUID, User> cache = Caffeine.newBuilder().buildAsync(key -> null);
public CompletableFuture<User> getUser(UUID userId, RequestContext requestContext) {
return cache.get(userId, key -> userService.get(userId, requestContext));
}
The dummy loader would be for the builder and you'd avoid calling cache.get(key)
which would invoke it.
this would simplify things indeed. the AsyncLoadingCache
constructed this way would return values if present and null
otherwise, without trying to load them from another source. good enough for now.
thanks a lot, Ben!
get(key)
just calls get(key, func)
with the attached loader, so it is mostly sugar. You only miss out on features like refresh and bulk loading, as would be expected.
I'll try to get around to AsyncCache
to avoid this hack. Just never have the time to get through my backlog.
my cache loader function needs to operate on a set of params that is different from the cache key. let's say the cache is defined as:
and my loading function for User (to use if there is a cache miss) is:
this prevents me from using this method in com.github.benmanes.caffeine.cache.Cache class:
since it requires
[K]
andFunction[K]
to be in sync. I can't userequestContext
as a part of the key since it is different on every request (for audit and other purposes).Guava's google cache supports a free-form value loader:
have you considered supporting this in Caffeine cache?