Retrying after database error causes Hibernate exception
See original GitHub issueDescribe the bug
My project has a JPA entity annotated with @UniqueConstraint
to guarantee the application won’t persist duplicate data. One of its endpoints checks the presence of a key in the database and, if missing, inserts a new record. Under highly concurrent circumstances, the application may trigger unique constraints while saving new data. I added the @Retry
annotation to the resource method to make REST API invocations more fault-tolerant. However, Hibernate throws an exception when Quarkus attempts to retry the request.
Expected behavior
Quarkus should attempt to execute the same method again and return the previously saved data.
Actual behavior
Quarkus attempts to execute the same method but throws an org.hibernate.AssertionFailure
.
ERROR [org.hib.AssertionFailure] (executor-thread-1) HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in org.acme.Car entry (don't flush the Session after an exception occurs)
To Reproduce
Clone this repository and run the tests.
Environment:
Output of uname -a
or ver
Darwin Kernel Version 20.3.0:root:xnu-7195.81.3~1/RELEASE_X86_64 x86_64
Output of java -version
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.10+9, mixed mode)
Quarkus version or git rev
1.13.0.FINAL
Build tool (ie. output of mvnw --version
or gradlew --version
)
Apache Maven 3.6.3
Issue Analytics
- State:
- Created 2 years ago
- Comments:12 (6 by maintainers)
There is no specification on how
@Retry
should interact with JPA or transactions. MicroProfile Fault Tolerance is mainly designed for guarding straightforward request/response network interactions.I also realized another thing. If I understand correctly, Hibernate sessions are managed by Quarkus and are assumed to span the whole request. If we were to discard them in the middle of request processing and replace with fresh ones, you would easily end up with detached entities (and perhaps more troubles? I don’t really understand Hibernate).
I guess this could all be solved in a straightforward way if, as @Sanne points out, we were to retry the entire request processing pipeline. That is currently probably equivalent to putting
@Retry
on the outermost resource method, but I recall there were ideas about injecting entities into resource methods to simplify some common tasks, Panache-style. In such case, putting@Retry
on such method, even if it were the outermost resource method, would not be equivalent to retrying the entire request processing pipeline. Which leads me to think this should perhaps be an entirely separate feature?Thinking about it again, I guess we could only possibly guarantee “reprocessability” for what the HTTP specification calls “safe methods” (
GET
andHEAD
, mostly). Anything else is hit or miss due to reasons you mention (do we wanna buffer the HTTP entity body, for example?), including the original issue here (which I guess is aPUT
orPOST
).