事务提交失败分三类:主节点不可写(NotWritablePrimary,code 10107)、服务端状态未知(UnknownTransactionCommitResult)、网络或心跳超时;需按 error.code 和 error.errorLabels 分层捕获并差异化处理,而非无差别重试。

事务提交失败不是单一错误,而是三类底层问题的共性表象:主节点不可写、服务端状态未知、网络或心跳超时。必须分层捕获错误码和 errorLabels,不能靠 try-catch 无差别重试。
怎么区分 NotWritablePrimary 和 UnknownTransactionCommitResult
这两个错误看起来都是“提交失败”,但处理方式完全相反:
-
NotWritablePrimary表示当前副本集没有可写的主节点(rs.status()中无stateStr === "PRIMARY"),驱动在发请求前就本地判定失败,毫秒级返回,retryWrites=true对它完全无效 -
UnknownTransactionCommitResult表示客户端发出了commitTransaction请求,但没收到明确响应(可能是主节点切换中、网络中断、连接被中间件断开),服务端事务状态未知——此时重试commitTransaction会报NoSuchTransaction,必须重建 session 并重放整个事务体 - 判断依据不是错误名,而是:
error.code === 10107(NotWritablePrimary)或error.errorLabels?.includes("UnknownTransactionCommitResult")
为什么重试 commitTransaction 会报 NoSuchTransaction
事务上下文由 txnNumber + session.id 唯一标识,服务端只接受一次 commit。一旦客户端未收到响应,这个 txnNumber 就失效了:
- 重试
commitTransaction时,驱动仍用原 session 和原txnNumber,服务端查不到对应事务,直接抛NoSuchTransaction(code 251) - 正确做法是:立刻调用
session.endSession(),新建ClientSession,显式调用session.startTransaction(),再执行全部读写逻辑 - 所有写操作必须幂等:
updateOne({ _id }, { $setOnInsert: {...} }, { upsert: true })替代insertOne,用条件更新替代无保护写入
哪些配置不匹配会导致高频提交失败
即使代码逻辑正确,以下服务端配置缺失或不一致,也会让事务在 prepare 或 commit 阶段静默失败:
-
enableMajorityReadConcern必须为true(所有分片/副本集成员),否则 snapshot 读关注无法生效,prepare 阶段失败不报错 - 各成员 MongoDB 小版本号(含补丁号,如
6.0.12vs6.0.15)和gitVersion必须完全一致,否则事务协议握手失败 -
writeConcern必须设为{ w: "majority", j: true };w: 1几乎必然触发UnknownTransactionCommitResult - 副本集投票节点数必须为奇数且 ≥3,否则
"majority"语义失效,写入无法满足确认要求
高延迟网络下事务卡住的真实原因
表象是 context deadline exceeded 或 Transaction is not in progress,但根源不在代码超时设置:
- 默认
heartbeatFrequencyMS=10000(10 秒)在跨 AZ 或跨 region 场景(RTT > 80ms)下太长,节点失联 20 秒后驱动才感知,期间事务已丢失上下文 - 必须同步调整:
heartbeatFrequencyMS=2000(RTT ≤100ms 时),并确保electionTimeoutMillis ≥ 5 × heartbeatFrequencyMS -
transactionLifetimeLimitSeconds(服务端参数)需调至 ≥180,否则事务在服务端被强制 abort,客户端收到TransactionTooOld - 客户端驱动的
socketTimeoutMS和serverSelectionTimeoutMS不建议设得太短,它们不是事务生命周期的控制点
最容易被忽略的是:错误分类必须依赖 error.code 和 error.errorLabels,而不是错误构造函数名;服务端配置不一致的问题,往往比业务代码更早、更稳定地触发失败。

















