Regression 2.x - Injecting reactive producer in an transactional bean loses the transaction
See original GitHub issueDescribe the bug
A regression bug was introduced with the 2.x release of Quarkus, when injecting an reactive producer in an transactional bean, the transaction gets lost.
A sample-application (based on the hibernate-orm-quickstart) has been created in a fork of the quickstarts.. The sample adds an EventPublisher for Kafka after persisting a new entity.
The bug appeared after upgrading to Quakrus 2.x
Expected behavior
Transaction finished successfully and sends event to Kafka.
Actual behavior
Exception occurs
ERROR [org.acm.hib.orm.FruitResource] (executor-thread-0) Failed to handle request: io.quarkus.arc.ArcUndeclaredThrowableException: Error invoking subclass method
at org.acme.hibernate.orm.FruitResource_Subclass.create(FruitResource_Subclass.zig:518)
at org.acme.hibernate.orm.FruitResource_ClientProxy.create(FruitResource_ClientProxy.zig:248)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:138)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:93)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:503)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2442)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1476)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:831)
Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1307)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:128)
at io.quarkus.narayana.jta.runtime.CDIDelegatingTransactionManager.commit(CDIDelegatingTransactionManager.java:102)
at io.quarkus.narayana.jta.runtime.CDIDelegatingTransactionManager_Subclass.commit$$superforward1(CDIDelegatingTransactionManager_Subclass.zig:386)
at io.quarkus.narayana.jta.runtime.CDIDelegatingTransactionManager_Subclass$$function$$6.apply(CDIDelegatingTransactionManager_Subclass$$function$$6.zig:24)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(InvocationInterceptor_Bean.zig:521)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
at io.quarkus.narayana.jta.runtime.CDIDelegatingTransactionManager_Subclass.commit(CDIDelegatingTransactionManager_Subclass.zig:983)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.endTransaction(TransactionalInterceptorBase.java:365)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:165)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:103)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:38)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:57)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:32)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_Bean.intercept(TransactionalInterceptorRequired_Bean.zig:340)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
at org.acme.hibernate.orm.FruitResource_Subclass.create(FruitResource_Subclass.zig:496)
... 31 more
Suppressed: javax.transaction.xa.XAException: Error trying to transactionCommit local transaction: Enlisted connection used without active transaction
at io.agroal.narayana.LocalXAResource.xaException(LocalXAResource.java:140)
at io.agroal.narayana.LocalXAResource.xaException(LocalXAResource.java:134)
at io.agroal.narayana.LocalXAResource.commit(LocalXAResource.java:72)
at com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord.topLevelOnePhaseCommit(XAResourceRecord.java:702)
at com.arjuna.ats.arjuna.coordinator.BasicAction.onePhaseCommit(BasicAction.java:2400)
at com.arjuna.ats.arjuna.coordinator.BasicAction.End(BasicAction.java:1502)
at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:96)
at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162)
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1295)
... 54 more
Caused by: java.sql.SQLException: Enlisted connection used without active transaction
at io.agroal.pool.ConnectionHandler.verifyEnlistment(ConnectionHandler.java:362)
at io.agroal.pool.ConnectionHandler.transactionCommit(ConnectionHandler.java:331)
at io.agroal.narayana.LocalXAResource.commit(LocalXAResource.java:69)
... 60 more
How to Reproduce?
Steps to reproduce.
Checkout Commit https://github.com/lostiniceland/quarkus-quickstarts/commit/f9300b1effa8fc61fe1a14ccfba343761619862a
go to hibernate-orm-quickstarts
docker-compose up
./mvnw quarkus:dev
curl -X 'POST' \
'http://localhost:8080/fruits' \
-H 'accept: */*' \
-H 'Content-Type: application/json' \
-d '{
"name": "foo"
}'
Check Log
Output of uname -a
or ver
No response
Output of java -version
16
GraalVM version (if different from Java)
No response
Quarkus version or git rev
2.1.0.Final, 2.0.3.Final
Build tool (ie. output of mvnw --version
or gradlew --version
)
Maven 3.8.1
Additional information
Changing the version (runtime and plugin) to 1.13.7, no error occurs.
Issue Analytics
- State:
- Created 2 years ago
- Comments:10 (8 by maintainers)
I do not agree that the issue is solved with the remarks done in #18450 First, there is an SQLException thrown at the end of the exception (not a Kafka-Exception), which doesnt show up when using Quarkus 1.3.x. So there is something wrong with 2.x Sure, the example is plain wrong in general for non-reactive api, because Kafka does not take part in a JTA transaction, so you should not do this dual-write. You might end up with a written Kafka-Message and a failed transaction afterwards due to a db constraint for instance because JTA finishes when the transactional-method is exited. Wrapping non-JTA in a Future and blocking will not solve this issue. Or is the exception an improvement over 1.3.x, meaning that it protects from something that shouldnt be done (what 1.3.x did not cover)
I find that in general, using reactive messaging streams in integration with other reactive APIs is not that obvious. We need a place to document these “common integration patterns”. Here’s a couple that comes to my mind :
I used Kafka as an example but it goes for all reactive messaging connectors.