Session 不能跨 goroutine 并发使用,必须每个 goroutine 独立调用 client.StartSession() 获取专属实例,并在结束前调用 session.EndSession();事务失败后需新建 Session 而非复用,且 Session 仅能由创建它的 Client 使用。

Session 不能跨 goroutine 并发使用
MongoDB Go Driver 的 Session 实例不是线程安全的,官方明确警告:**多个 goroutine 同时调用同一个 Session 的方法(如 StartTransaction()、CommitTransaction())会导致未定义行为或 panic**。这不是竞态检测问题,而是底层 session state 状态机被并发修改的硬性限制。
常见错误现象包括:panic: runtime error: invalid memory address、session is in use by another goroutine(某些驱动版本会显式报错)、事务提交静默失败但数据未写入。
- 每个 goroutine 必须独立调用
client.StartSession()获取专属Session,不能共享或池化Session实例 - 不要把
Session作为函数参数传给多个并发协程;若需复用逻辑,应把操作封装为闭包,在各自 goroutine 内部获取新 session - 务必在 goroutine 结束前调用
session.EndSession(),否则会泄漏 session 资源(尤其在高并发短生命周期任务中)
事务重试时别复用旧 Session
事务因 WriteConflict 或 TransactionTooOld 失败后,**原 Session 已进入终止状态,不能再用于新的 StartTransaction()**。强行复用会返回 session is in aborted state 错误。
正确做法是:每次重试都新建 Session,而不是在循环里反复调用 session.StartTransaction()。
- Go 驱动推荐用
session.WithTransaction()封装重试逻辑,它内部自动处理 session 创建与清理 - 手动重试时,必须在
defer session.EndSession()前完成整个事务流程;失败后立即结束当前 session,下一轮重试再client.StartSession() - 避免在
WithTransaction回调里捕获错误后继续用同一 session 执行其他操作——回调退出即 session 释放
Session 生命周期过长导致连接堆积
Session 在 MongoDB 服务端会占用资源,长期不关闭(比如全局缓存、长时间阻塞等待外部 API)会快速耗尽连接池,触发 connection refused 或 max number of concurrent sessions exceeded。
典型场景:HTTP handler 中启动 session 后调用第三方支付接口,超时未设或重试无界,session 持有数分钟。
- 所有
session.EndSession()必须配对defer,且放在最外层作用域(如 handler 函数开头) - 对耗时外部调用(HTTP、RPC),应在
context.WithTimeout()控制整体事务时限,超时直接 abort 并 end session - 不要依赖
transactionLifetimeLimitSeconds自动清理——那是兜底机制,不能替代主动管理
Client 和 Session 绑定关系不能错乱
Session 只能由创建它的 Client(或其衍生的 Database/Collection)使用。混用不同 client 实例的 session 会直接报错:session was created from a different client。
这在微服务或多数据源场景容易出错:比如用全局 client A 初始化 session,却传给依赖 client B 的 repository 方法执行写入。
- 确保 session 使用范围严格限定在创建它的 client 生态内;跨数据源操作必须拆成独立事务
- 避免在 repository 层接收 session 参数——它属于业务协调层职责;repository 应只接受
context.Context和必要参数 - 单元测试中 mock client 时,要同步 mock session 行为,否则易漏掉绑定校验


















