"Exception thrown during asynchronous load" when using quarkus-cache and quarkus-hibernate-validator
See original GitHub issueDescribe the bug When using service validation on a method and also use caching on that service method an exception is written to the log.
Expected behavior No exception is written to the log.
Actual behavior Something like this exception is written to the log:
WARNING: Exception thrown during asynchronous load
java.util.concurrent.CompletionException: javax.validation.ConstraintViolationException: 1 constraint violation(s) occurred during method validation.
Constructor or Method: public java.lang.String ...Service.doService(ServiceRequest)
Argument values: [...ServiceRequest@7a0f43dc]
Constraint violations:
(1) Kind: PROPERTY
message: darf nicht null sein
root bean: ...Service_Subclass@15ec2ac4
property path: doService.arg0.text
constraint: @javax.validation.constraints.NotNull(message="{javax.validation.constraints.NotNull.message}", payload={}, groups={})
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1702)
at io.smallrye.context.SmallRyeThreadContext$ContextualRunnable.run(SmallRyeThreadContext.java:56)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at java.base/java.lang.Thread.run(Thread.java:834)
at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: javax.validation.ConstraintViolationException: 1 constraint violation(s) occurred during method validation.
Constructor or Method: public java.lang.String Service.doService(...ServiceRequest)
Argument values: [...ServiceRequest@7a0f43dc]
Constraint violations:
(1) Kind: PROPERTY
message: darf nicht null sein
root bean: Service_Subclass@15ec2ac4
property path: service.arg0.text
constraint: @javax.validation.constraints.NotNull(message="{javax.validation.constraints.NotNull.message}", payload={}, groups={})
at io.quarkus.hibernate.validator.runtime.interceptor.AbstractMethodValidationInterceptor.validateMethodInvocation(AbstractMethodValidationInterceptor.java:65)
at io.quarkus.hibernate.validator.runtime.interceptor.MethodValidationInterceptor.validateMethodInvocation(MethodValidationInterceptor.java:17)
at io.quarkus.hibernate.validator.runtime.interceptor.MethodValidationInterceptor_Bean.intercept(MethodValidationInterceptor_Bean.zig:340)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:50)
at io.quarkus.cache.runtime.CacheResultInterceptor.lambda$intercept$0(CacheResultInterceptor.java:27)
at io.quarkus.cache.runtime.caffeine.CaffeineCache$MappingSupplier.get(CaffeineCache.java:136)
at io.quarkus.cache.runtime.caffeine.CaffeineCache.lambda$get$0(CaffeineCache.java:58)
at com.github.benmanes.caffeine.cache.LocalAsyncCache.lambda$get$0(LocalAsyncCache.java:77)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
... 9 more
To Reproduce In the pom.xml add:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-cache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
Create a service like this:
@ApplicationScoped
public class Service {
@CacheResult(cacheName = "cache")
public String doService(@Valid ServiceRequest request) {
// do something important
}
}
Create a ServiceRequest POJO with validation annotations:
public class ServiceRequest{
@NotNull
private String text;
// getters / setters
}
Invoke the service method from a QuarkusTest or REST service (with text = null), then the error will appear. It doesn’t if I remove the cache annotation.
Environment (please complete the following information):
- Output of
uname -a
orver
: Microsoft Windows [Version 10.0.17763.1282] - Output of
java -version
: java version “11.0.7” 2020-04-14 LTS - GraalVM version (if different from Java):
- Quarkus version or git rev: 1.6.1.Final
- Build tool (ie. output of
mvnw --version
orgradlew --version
): Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Issue Analytics
- State:
- Created 3 years ago
- Comments:8 (5 by maintainers)
Top Results From Across the Web
Application Data Caching - Quarkus
When the call to the remote throws an exception, the cache does not store the result, so that a subsequent call to callRemote()...
Read more >Home of Quarkus Cheat-Sheet - GitHub Pages
Quarkus comes with a Maven archetype to scaffold a very simple starting project. ... overriding methods that override constraints should throw an exception....
Read more >javax.validation.constraints.notnull maven dependency
quarkusio/quarkus"Exception thrown during asynchronous load" when using quarkus-cache and quarkus-hibernate-validator#11315. Created over 2 years ago.
Read more >Using Quarkus Cache with Reactive and Mutiny correctly
The Subject is loaded from the cache, if available, by the cache key "subjectId". Migrating to Mutiny would look like this @CacheResult( ...
Read more >quarkusio/quarkus 1.8.0.CR1 on GitHub - NewReleases.io
... "Exception thrown during asynchronous load" when using quarkus-cache and quarkus-hibernate-validator; #11312 - Avoid generated-sources clash with ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
It looks like Quarkus has its own configuration file for logging (docs). You could simply specify a default log level in your templates that suppress Caffeine’s logging. If the majority of your users setup based on a common init script, it would become an implicit default.
A code workaround would be to translate the error into a success with an error’d value. Instead of the future being exceptional, it would hold a result object that was marked as a failure. The cache would consider it a successful load, while the handler could translate into a user-facing future. As the result would be cached, you would have to include an exceptional handler to remove the entry explicitly (safest by using
asMap.remove(key, oldValue)
).From Caffeine’s perspective it is designed as a data structure (like any other
Map
) so we log minimally. Yet application developers very frequently struggle with asynchronous logic and do not consider failures. Unlike synchronous code where uncaught exceptions arrive at the thread’sdefaultUncaughtExceptionHandler
to be logged, the failure of aCompletableFuture
is a stateful result. When chaining only on the happy path (then
without anexceptionally
block), the error is silently swallowed and in Caffeine that entry is removed. The lack of an exception handler may result in hard to find bugs as the developer was not guided by the programming model nor libraries to consider failures. That causes frustration, rightfully blames the tooling, so this logging helps uncover the mistake to provide a more pleasant experience. We responded to user complaints and, as logging is trivial to disable, I think it’s reasonable for Caffeine to warn on.It isn’t necessary, but a nice practice to avoid race conditions. If you use
AsyncCache.asMap()
then the value is the cached future. The reference equality check ensures that a concurrent removal & add doesn’t result in your callback accidentally removing the new entry. It’s a rare and minor race, but one you can squash if desired.