Keycloak事务提交报错:当前线程无关联事务的原因排查求助
环境信息
- Keycloak版本:4.4.0.21-SNAPSHOT
- 模块POM配置:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <parent> <artifactId>keycloak-parent</artifactId> <groupId>org.keycloak</groupId> <version>4.4.0.21-SNAPSHOT</version> </parent> <name>Keycloak WildFly Integration</name> <description/> <modelVersion>4.0.0</modelVersion> <artifactId>keycloak-wildfly-parent</artifactId> <packaging>pom</packaging> <modules> <module>adduser</module> <module>extensions</module> <module>server-subsystem</module> </modules> </project>
参考实现(来自KeycloakApplications类)
KeycloakSession session = sessionFactory.create(); try { session.getTransactionManager().begin(); //do some work with db etc. session.getTransactionManager().commit(); ServicesLogger.LOGGER.addUserSuccess(userRep.getUsername(), realmRep.getRealm()); }
自定义模块代码
try { KeycloakTransactionManager transactionManager = session.getTransactionManager(); transactionManager.begin(); //do some work transactionManager.commit(); //error in this line }
报错信息
java.lang.IllegalStateException: No transaction associated with the current thread
完整堆栈跟踪
15:23:36,668 ERROR [com.mycompany.sso.events.listener.KafkaConsumerProvider] (keycloak-audit-akka.actor.default-dispatcher-5) No transaction associated with the current thread: java.lang.IllegalStateException: No transaction associated with the current thread at org.wildfly.transaction.client.ContextTransactionManager.commit(ContextTransactionManager.java:69) at org.keycloak.transaction.JtaTransactionWrapper.commit(JtaTransactionWrapper.java:92) at org.keycloak.services.DefaultKeycloakTransactionManager.commit(DefaultKeycloakTransactionManager.java:136) at com.mycompany.sso.events.listener.KafkaConsumerProvider.process(KafkaConsumerProvider.java:79) at akka.stream.javadsl.Source.$anonfun$map$1(Source.scala:1299) at akka.stream.impl.fusing.Map$$anon$1.onPush(Ops.scala:54) at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:523) at akka.stream.impl.fusing.GraphInterpreter.processEvent(GraphInterpreter.scala:480) at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:376) at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:606) at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:485) at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:581) at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:749) at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:764) at akka.actor.Actor.aroundReceive(Actor.scala:539) at akka.actor.Actor.aroundReceive$(Actor.scala:537) at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:671) at akka.actor.ActorCell.receiveMessage(ActorCell.scala:614) at akka.actor.ActorCell.invoke(ActorCell.scala:583) at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:268) at akka.dispatch.Mailbox.run(Mailbox.scala:229) at akka.dispatch.Mailbox.exec(Mailbox.scala:241) at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
调试情况
已调试org.keycloak.services.DefaultKeycloakTransactionManager类的begin()、commit()和rollback()方法,未定位到问题,同时保存了三个调试状态截图:
- begin()前的TransactionManager状态
- begin()后的TransactionManager状态
- 执行完业务逻辑、commit()前的TransactionManager状态
错误原因分析
从堆栈信息可以看到,代码运行在Akka的异步调度线程(keycloak-audit-akka.actor.default-dispatcher-5)中,结合Keycloak的事务实现逻辑,问题根源在于:
线程绑定的事务上下文丢失
Keycloak基于WildFly的ContextTransactionManager实现事务管理,该管理器依赖当前线程绑定的事务上下文。而Akka的线程池是异步非阻塞模型,若业务逻辑中存在异步操作、线程切换(比如Future回调、异步IO),会导致commit()时的线程和begin()时绑定事务的线程不一致,此时新线程无法找到关联的事务,触发报错。Keycloak Session的线程不安全特性
Keycloak的KeycloakSession设计为线程不安全,不能在多线程环境下共享或切换线程使用。如果业务逻辑中跨线程操作了同一个Session,会导致Session内部的事务上下文被破坏,进而丢失事务关联。隐式事务终止
业务逻辑中的某些操作可能隐式终止了事务,比如调用了rollback()、事务超时,或者Session被意外销毁/重置,也会导致commit()时找不到关联事务。
对比参考代码:KeycloakApplications中的代码是在同步线程中执行,没有线程切换,事务上下文可以一直绑定在当前线程,因此能正常提交。
内容的提问来源于stack exchange,提问作者Pavel Bukhalov

