NotWritablePrimary是拓扑状态确认“无主可写”,驱动在本地缓存中未查到PRIMARY节点便毫秒级抛错,不发请求至服务器;与网络超时等无关,retryWrites=true对事务无效,需业务层捕获重试。

NotWritablePrimary不是连接失败,而是拓扑状态确认“无主可写”
这个错误出现时,驱动根本没发请求到服务器——它在本地缓存的副本集拓扑中已查不到状态为 PRIMARY 的节点,于是直接抛出 NotWritablePrimary。它和网络超时、DNS 解析失败完全不同,毫秒级返回,且不依赖 timeoutMS 设置。
常见误判是以为“刚连上就报错=配置没生效”,其实此时 rs.status() 在 shell 里可能仍显示有 primary,但那是你手动连的那个节点的视角;客户端驱动看到的拓扑快照可能已过期或压根没建立起来。
- 检查
mongosh连接串是否含?replicaSet=rs0,缺这个参数就退化为单节点模式,无法感知主从变化 - 用
db.runCommand({ isMaster: 1 })在应用连接的 session 中执行,看返回里的ismaster字段是否为true - 如果返回
"ismaster": false, "secondary": true,说明驱动连到了 secondary 且没启用 replicaSet 发现机制
添加新节点期间必然触发NotWritablePrimary
执行 rs.add("new-node:27017") 后,选举过程会短暂导致集群无主。这不是异常,是协议设计:MongoDB 不允许在无主状态下接受任何写操作(包括事务、普通 insert、甚至 db.getCollectionNames() 这类元数据读——某些驱动会把它转成带写上下文的操作)。
此时所有写请求都会立即失败,retryWrites=true 完全无效,因为事务内操作禁止重试,普通写操作即使开启该选项,也会在首次失败后终止,不会跨选举窗口重放。
- 避免在业务高峰期执行
rs.add或rs.reconfig - 添加节点前,先用
rs.status().members.map(m => m.stateStr)确认当前无STARTUP2或RECOVERING节点,否则选举概率陡增 - 新节点加入后,等待其状态变为
SECONDARY且optimeDate与主节点差距
驱动初始化时必须显式声明副本集上下文
只靠连接字符串里的 replicaSet 参数还不够。Node.js 驱动等现代 SDK 要求在 MongoClient 构造时传入拓扑发现开关,否则即使 URI 正确,也可能降级为单节点行为。
例如 Node.js 中:new MongoClient("mongodb://n1:27017,n2:27017/?replicaSet=rs0", { directConnection: false }) ——漏掉 directConnection: false(默认值其实是 false,但显式写出可防误覆盖)或写成 true,都会跳过副本集发现流程。
- Python PyMongo 同理:必须确保
client = MongoClient(uri, serverSelectionTimeoutMS=3000)中未设directConnection=True - Kubernetes 环境下,若用 Headless Service + DNS SRV,连接串还得加
&srvServiceName=mongodb,否则驱动无法解析完整成员列表 - Java 驱动需确认
com.mongodb.ConnectionString构造的 URI 包含replicaSet,且未被ClusterSettings.Builder.applyToClusterSettings强制覆盖
事务场景下必须由业务层接管重试逻辑
一旦收到 NotWritablePrimary,当前 ClientSession 已失效,commitTransaction() 必然失败,且无法恢复。你不能指望驱动自动重建 session 或重放命令。
真正可行的做法是捕获该错误,在业务代码中实现带退避的幂等重试:
- 立即 sleep(200 + jitter),再调用
db.runCommand({ isMaster: 1 })检查是否已有ismaster: true - 最多重试 3 次,超过则降级:比如把关键订单写入 Kafka,后续异步补偿
- 重放前校验幂等 key(如订单号),防止前一次事务已在新主选出后部分写入 oplog
- 不要设过长的
timeoutMS(建议 ≤ 1500),否则会掩盖真实的拓扑问题
host.docker.internal 直连某 Pod IP,或监控脚本硬编码 --host sec-node-01,这些都让驱动彻底失去 topology discovery 能力。


















