Hello,When I tested this method, I found that it was not the result I wanted anyway;
This is test case:
public Object manulOperator(String key) throws Exception{
AsyncLoadingCache<String, Object> cache1 = Caffeine.newBuilder()
.expireAfterWrite(10000, TimeUnit.SECONDS)
.expireAfterAccess(10000, TimeUnit.SECONDS)
.maximumSize(20)
.buildAsync(k -> setAsyncValue(k));
CompletableFuture cf = cache1.get("key");
System.out.println(cf.get());
System.out.println("complete");
return "true";
}
public CompletableFuture<Object> setAsyncValue(Object key) {
return CompletableFuture.supplyAsync(() -> {
return key + "value";
});
}
}
This is output:
java.util.concurrent.CompletableFuture@1f89ab83[Completed normally]
complete
But if I adopt the following use case:
```
public Object manulOperator(String key) throws Exception{
AsyncLoadingCache<String, Object> cache1 = Caffeine.newBuilder()
.expireAfterWrite(10000, TimeUnit.SECONDS)
.expireAfterAccess(10000, TimeUnit.SECONDS)
.maximumSize(20)
.buildAsync(k -> setAsyncValue(k));
CompletableFuture cf1 = cache1.get("key");
CompletableFuture cf2 = (CompletableFuture)cf1.get();
System.out.println(cf2.get());
System.out.println("complete");
return "true";
}
public CompletableFuture<Object> setAsyncValue(Object key) {
return CompletableFuture.supplyAsync(() -> {
return key + "value";
});
}
It can return the correct result:
keyvalue
complete
```
I did not find the exact cause of the problem by reading the code,Is my usage wrong? Looking forward to your reply
I think what you really want is to return the future back to the cache for it to manage. Currently your type is really AsyncLoadingCache<String, CompletableFuture<String>>. This results in the cache being backed by Map<String, CompletableFuture<CompletableFuture<String>>> which is awkward. If instead you declare it as AsyncLoadingCache<String, String> then it will manage a Map<String, CompletableFuture<String>> internally and give you the desired result.
public void manulOperator() throws Exception {
AsyncLoadingCache<String, String> cache1 = Caffeine.newBuilder()
.expireAfterWrite(10000, TimeUnit.SECONDS)
.expireAfterAccess(10000, TimeUnit.SECONDS)
.maximumSize(20)
.buildAsync((k, executor) -> setAsyncValue(k));
CompletableFuture<String> cf = cache1.get("key");
System.out.println(cf.get());
System.out.println("complete");
}
public CompletableFuture<String> setAsyncValue(Object key) {
return CompletableFuture.supplyAsync(() -> {
return key + "value";
});
}
The buildAsync(key -> value) is useful when computing the value can be expressed synchronously, so the cache automatically wraps it with a future. Otherwise the buildAsync((key, executor) -> future) lets the cache consume an existing future, e.g. one produced by another library. That seems to be your intent here.
I did not understand the third use case in the wiki at first:
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
// Either: Build with a synchronous computation that is wrapped as asynchronous
.buildAsync(key -> createExpensiveGraph(key));
// Or: Build with a asynchronous computation that returns a future
.buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
// Lookup and asynchronously compute an entry if absent
CompletableFuture<Graph> graph = cache.get(key);
// Lookup and asynchronously compute entries that are absent
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);
Now I seem to understand:
.buildAsync(key -> createExpensiveGraph(key));
Anonymous class that extends ‘CacheLoader’ will be created If initialized in this way,and the anonymous class will implements method of load,Because you have already done the asynchronousprocessing in the synchronized cacheloader, So when I call the asyncLoad method it will return CompletableFuture
Exactly. And the other method becomes an anonymous AsyncCacheLoader that must return the future. Glad it all makes sense now 🙂
Most helpful comment
I think what you really want is to return the future back to the cache for it to manage. Currently your type is really
AsyncLoadingCache<String, CompletableFuture<String>>. This results in the cache being backed byMap<String, CompletableFuture<CompletableFuture<String>>>which is awkward. If instead you declare it asAsyncLoadingCache<String, String>then it will manage aMap<String, CompletableFuture<String>>internally and give you the desired result.The
buildAsync(key -> value)is useful when computing the value can be expressed synchronously, so the cache automatically wraps it with a future. Otherwise thebuildAsync((key, executor) -> future)lets the cache consume an existing future, e.g. one produced by another library. That seems to be your intent here.