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

NestJs+TypeOrm+Mysql嵌套使用subscriber导致应用崩溃问题排查

问题触发原因
  • 订阅者重复注册:你在ORM配置中已经开启了subscribers自动扫描加载,同时又在每个订阅者的构造函数中手动将当前实例push到连接的订阅者列表中,导致每个订阅者被注册2次,事件触发2次。如果后续的Transaction实体插入又触发了前面实体的写入逻辑,会直接形成无限循环插入,快速占满数据库连接池,导致后续请求阻塞卡死。
  • 事务嵌套阻塞:TypeORM的afterInsert事件是在当前写入事务提交前触发的,外层save方法需要等待afterInsert的异步逻辑完全执行完成才会提交事务。而你在订阅者中使用注入的Repository执行写入操作时,会申请新的数据库连接开启独立事务,多层嵌套后所有外层事务都在等待内层逻辑完成再提交,再加上连接池资源被占,就会形成死锁式阻塞。从你的MySQL日志也能看出三个写入操作都执行了INSERT,但没有一个事务提交,就是因为外层事务一直在等待内层逻辑完成。
  • 上下文管理器使用错误:订阅者事件回调中没有复用当前事件上下文的entityManager,每次写入都申请新连接,进一步加剧连接池资源消耗。
解决方案

1. 修复订阅者重复注册问题

二选一即可,推荐使用Nest依赖注入管理方式:

  • 删掉ORM配置中的subscribers: ['dist/**/subscribers/*.subscriber{.ts,.js}']自动扫描配置
  • 删掉每个订阅者构造函数中的connection.subscribers.push(this)手动注册代码
  • 在对应业务模块的providers数组中加入所有订阅者,交给Nest统一实例化管理

2. 复用事件上下文的EntityManager

在订阅者的事件回调中,使用事件参数自带的entityManager执行所有数据库操作,复用当前事务的连接,不需要额外申请新连接,也避免嵌套事务问题,示例:

@Injectable()
export class BankActionsSubscriber implements EntitySubscriberInterface<BankAction> {
    constructor(connection: Connection) {}
    listenTo = () => BankAction;
    async afterInsert(event: InsertEvent<BankAction>) {
        // 直接用event.manager操作,复用当前事务连接
        const newOrder = event.manager.create(Order, example_data);
        await event.manager.save(newOrder);
    }
}

3. 非核心逻辑异步解耦(可选)

如果后续的Order、Transaction写入不需要和BankAction写入保证原子性,可以用事件驱动异步解耦,避免阻塞主事务:

// 订阅者中直接发事件,不等待执行完成
async afterInsert(event: InsertEvent<BankAction>) {
    this.eventEmitter.emit('bank.action.created', event.entity);
}

// 单独的事件监听者执行后续写入
@OnEvent('bank.action.created')
async handleBankActionCreated(data: BankAction) {
    await this.ordersService.create(example_data);
}

内容的提问来源于stack exchange,提问作者Mahdi Amiri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 08:15:10