You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单元测试链式调用2个Uni使用Panache.withTransaction()报TimeoutException

问题根因

抛出TimeoutException和事务执行耗时无关,是响应式上下文用法错误导致事件循环线程被阻塞,事务任务永远得不到调度:

  • Hibernate Reactive Panache的所有数据库操作必须运行在Vert.x事件循环线程上,测试中频繁调用.await().indefinitely()会直接阻塞当前线程,如果当前线程恰好是事件循环线程,提交到事件循环的事务任务会一直排队,直到触发默认5秒的超时阈值抛出异常
  • 你在Panache.withTransaction()外部提前初始化了insertKline、getOhlcList这类Uni实例,响应式流创建时就绑定了当前的非事务上下文,后续即使放到事务Supplier里执行,也无法加入当前开启的事务,会因为找不到活动事务会话一直挂起
  • 你提前调用.await().indefinitely()查询currencyPairEntity的操作,已经在事务开启前阻塞了执行线程,后续手动调用runOnContext()切换上下文也无法解决线程被占住的问题
解决方案

优先使用Quarkus官方提供的测试响应式事务注解,无需手动管理事务状态、无需手动标记回滚,也不需要自行阻塞等待结果,框架会自动处理上下文调度和测试完成后的事务回滚:

@QuarkusTest
@Slf4j
class KlineServiceTest {
    @Inject
    KlineRepository klineRepository;

    @Inject
    CurrencyPairRepository currencyPairRepository;

    @Inject
    KlineService service;

    @Test
    @DisplayName("ohlc matches inserted kline")
    @TestReactiveTransaction(rollback = true) // 测试方法自动包裹在回滚事务中,测试结束自动清理数据
    Uni<Void> ohlcMatchesInsertedKline() { // 测试方法直接返回响应式Uni,Quarkus测试框架会自动订阅执行,无需手动阻塞
        // GIVEN
        val volume       = BigDecimal.valueOf(1d);
        val closeTime    = LocalDateTime.now().withSecond(0).withNano(0);
        val currencyPair = new CurrencyPair("BTC", "USDT");

        // 链式拼接所有数据库操作,全程不提前阻塞
        return currencyPairRepository.findOrCreate(currencyPair)
                .chain(currencyPairEntity -> {
                    val kline = KlineEntity.builder()
                            .id(new KlineId(currencyPairEntity, closeTime))
                            .volume(volume)
                            .build();
                    return klineRepository.persistAndFlush(kline);
                })
                .chain(() -> service.listOhlcByCurrencyPairAndTimeWindow(currencyPair, ofMinutes(5)))
                // 断言直接写在响应式链路中
                .invoke(ohlcList -> {
                    assertThat(ohlcList).hasSize(1);
                    val ohlc = ohlcList.get(0);
                    assertThat(ohlc).extracting(Ohlc::getCloseTime, Ohlc::getVolume)
                            .containsExactly(closeTime, volume);
                });
    }
}

如果必须手动控制事务逻辑,需遵守两个规则:

  • 所有需要加入事务的响应式Uni,必须在Panache.withTransaction()的Supplier内部创建,禁止提前在事务外初始化绑定非事务上下文
  • 禁止在Vert.x事件循环线程上调用.await().indefinitely()这类阻塞方法,阻塞操作需调度到Quarkus的阻塞工作线程池执行

内容的提问来源于stack exchange,提问作者Frédéric Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:09:18