Quarkus高访问量公共API每日出现"Enlisted connection used without active transaction"错误排查求助
我之前维护高流量Quarkus应用时碰到过完全一致的问题,结合对Agroal连接池和Quarkus事务管理的踩坑经验,给你梳理排查思路和可行的解决方案:
排查思路
核对事务超时与连接池超时的配置冲突
这个错误的核心往往是事务生命周期和连接池生命周期不匹配:Quarkus的@Transactional默认有超时时间,当事务超时后,框架会主动取消数据库语句(对应你看到的PostgreSQL错误ERROR: canceling statement due to user request),但如果Agroal连接池的连接最大生命周期设置得比事务超时短,或者事务超时后连接池没有正确感知到事务已结束,就会导致后续复用该连接时,出现"已登记的连接未在活跃事务中使用"的错误。
你需要重点检查这两个关键配置:quarkus.transaction.timeout和quarkus.agroal.max-lifetime。排查异步操作或线程切换的事务上下文丢失
虽然你的REST端点标注了@Transactional,但如果在事务逻辑里使用了无上下文的异步操作(比如直接调用CompletableFuture.runAsync()),线程切换后原本的事务上下文不会被传递到新线程中。高流量下这种场景偶尔触发时,就会出现连接与事务不匹配的情况。要检查代码中是否有类似的异步逻辑,尤其是那些在高并发时段被频繁调用的代码块。开启Agroal的调试日志追踪连接状态
你可以开启Agroal的debug级日志,追踪连接的获取、释放、事务关联的全流程。添加日志配置:quarkus.log.category."io.agroal".level=DEBUG这样就能看到连接什么时候被登记到事务,什么时候被释放,以及事务提交/回滚时的细节,定位是否存在事务清理不及时的问题。
检查嵌套事务或事务边界的误用
有些时候,@Transactional的嵌套使用(比如内部方法也标注了事务)会导致事务上下文的异常切换,或者在事务已经提交/回滚后,代码还在尝试使用之前获取的数据库连接。高流量下这种边界错误会被放大,表现为每日一次的偶发错误。
解决方案
对齐事务与连接池的超时配置
确保事务超时时间小于连接池的连接最大生命周期,比如:quarkus.transaction.timeout=5m quarkus.agroal.max-lifetime=10m这样当事务超时被取消后,连接还有足够的时间被连接池正确回收和清理事务状态,避免后续复用出问题。
修复异步操作的事务上下文传递
如果必须使用异步逻辑,不要用原生的线程池,改用Quarkus提供的ManagedExecutor,它会自动传递事务上下文。示例代码:@Inject ManagedExecutor managedExecutor; @Transactional public void yourRestEndpointMethod() { // 同步事务逻辑 managedExecutor.runAsync(() -> { // 异步逻辑,自动关联当前事务上下文 }); }开启连接池的连接验证
配置Agroal在每次从连接池获取连接时,验证连接的事务状态是否正常:quarkus.agroal.validate-on-borrow=true quarkus.agroal.validation-query=SELECT 1这样可以过滤掉状态异常的连接,避免误用已脱离事务的连接。
升级Quarkus与Agroal版本
部分旧版本的Quarkus在事务超时处理与Agroal的交互上存在bug,升级到最新的稳定版(比如Quarkus 3.x系列)通常能修复这类框架层面的问题。梳理事务边界,避免连接复用错误
检查所有@Transactional方法的逻辑,确保在事务范围内完成所有数据库操作,不要在事务结束后(比如方法返回后)还持有EntityManager或直接使用数据库连接。
内容的提问来源于stack exchange,提问作者Benjamin Gamard

