-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cache the item emitted by a Uni instead of the Uni itself #16608
Cache the item emitted by a Uni instead of the Uni itself #16608
Conversation
/** | ||
* Tests the {@link CacheResult} annotation on methods returning {@link Uni}. | ||
*/ | ||
public class UniValueTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the log output for this test:
[INFO] Running io.quarkus.cache.test.runtime.UniValueTest
2021-04-18 02:17:52,879 INFO [io.quarkus] (main) Quarkus 999-SNAPSHOT on JVM started in 0.486s. Listening on: http://localhost:8081
2021-04-18 02:17:52,879 INFO [io.quarkus] (main) Profile test activated.
2021-04-18 02:17:52,880 INFO [io.quarkus] (main) Installed features: [cache, cdi, mutiny, smallrye-context-propagation]
2021-04-18 02:17:53,156 DEBUG [io.qua.cac.run.CacheResultInterceptor] (main) Loading entry with key [key] from cache [test-cache]
2021-04-18 02:17:53,160 DEBUG [io.qua.cac.run.CacheResultInterceptor] (main) Adding UncomputedUniValue entry with key [key] into cache [test-cache]
2021-04-18 02:17:53,166 DEBUG [io.qua.cac.run.CacheResultInterceptor] (main) Loading entry with key [key] from cache [test-cache]
2021-04-18 02:17:53,169 DEBUG [io.qua.cac.run.caf.CaffeineCache] (main) Replacing Uni value entry with key [key] into cache [test-cache]
2021-04-18 02:17:53,169 DEBUG [io.qua.cac.run.caf.CaffeineCache] (main) Replacing Uni value entry with key [key] into cache [test-cache]
2021-04-18 02:17:53,170 DEBUG [io.qua.cac.run.CacheResultInterceptor] (main) Loading entry with key [key] from cache [test-cache]
2021-04-18 02:17:53,170 DEBUG [io.qua.cac.run.CacheResultInterceptor] (main) Loading entry with key [another-key] from cache [test-cache]
2021-04-18 02:17:53,170 DEBUG [io.qua.cac.run.CacheResultInterceptor] (main) Adding UncomputedUniValue entry with key [another-key] into cache [test-cache]
2021-04-18 02:17:53,170 DEBUG [io.qua.cac.run.caf.CaffeineCache] (main) Replacing Uni value entry with key [another-key] into cache [test-cache]
2021-04-18 02:17:53,215 INFO [io.quarkus] (main) Quarkus stopped in 0.044s
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.723 s - in io.quarkus.cache.test.runtime.UniValueTest
This workflow status is outdated as a new workflow run has been triggered. Failing Jobs - Building 311d99d
|
311d99d
to
2a577df
Compare
@CacheResult(cacheName = "test-cache") | ||
public Uni<String> cachedMethod(String key) { | ||
invocations++; | ||
return Uni.createFrom().item(() -> new String()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also check that the number of subscriptions is equal to the number of invocations?
return Uni.createFrom().item(() -> {
subscriptions++;
return "" + subscriptions;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review @cescoffier.
I added the subcriptions check. The number of subscriptions is not always equal to the number of invocations though. Step 2 uses a cached Uni
so it does not increment the invocations counter, but the subscriptions counter is still incremented when the Uni
is resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see! You cache the Uni
and resubscribe to it every time (well Quarkus does).
That's actually interesting, but I'm not sure it's what the user would expect.
Imagine:
@GET
public Uni<String> callMyRemoteService() {
return webClient.send().map(r -> r.bodyAsString());
}
Basically, it calls a remote service.
If you cache the result, what would/should happen?
- the response is cached - the users will get the same response, avoiding calls to the remote service
- the uni is cached - to every time there is a request, there is another subscription calling the remote service
I would have said 1, but it looks like 2 has been implemented. Can you confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2
happens until one of the subscribers gets the reponse, which will be cached because of the callback at CacheResultInterceptor:116
. From there, 1
will happen for all subsequent calls to the cached method. I don't think we can lower the number of remote service calls, but maybe I missed something.
There is one detail that could be changed though: the Uni
returned by the cached method does not have to be cached until a response is received. UnresolvedUniValue
could act as a placeholder in the cache and be replaced by the reponse when it's available. In the meantime, CacheResultInterceptor.intercept
would simply return the method invocation result followed by the CacheResultInterceptor:116
callback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I wrote it down, that placeholder idea sounds more elegant to me than wrapping and then caching the Uni
. 😄
If you want, I can add a commit to this PR to show you what I have in mind exactly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed the changes in 2daae56.
UnresolvedUniValue
no longer contains the Uni
but is still cached as a placeholder until the emitted item is available.
The number of subscriptions is now equal to the number of invocations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If that version is OK with you, I'll push one more commit to make UnresolvedUniValue
static instead of creating a new instance for each unresolved Uni
.
extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java
Outdated
Show resolved
Hide resolved
2a577df
to
0af0bf3
Compare
0af0bf3
to
85ac88b
Compare
35d60d7
to
62a28b8
Compare
This workflow status is outdated as a new workflow run has been triggered. 🚫 This workflow run has been cancelled. Failing Jobs - Building 35d60d7
|
62a28b8
to
2daae56
Compare
This workflow status is outdated as a new workflow run has been triggered. 🚫 This workflow run has been cancelled. Failing Jobs - Building 62a28b8
|
2daae56
to
015a3c6
Compare
015a3c6
to
7662427
Compare
I pushed the static |
Thank you all for your help with this! |
Fixes #16607.
cc @cescoffier @geoand