DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

MongoDB find 能读 secondary,db.command 却报 not primary:显式传递 readPreference

MongoDB Node 驱动连接 secondary 后,普通查询正常但 db.command 报 not primary 时,把 readPreference 放进第二参数,保留命令支持、权限和复制一致性条件。

Node.js 连接 MongoDB 副本集中的 secondary,普通 find 或 aggregate 能读取,但 db.command() 报 MongoServerError: not primary and secondaryOk=false。连接字符串已经写了 readPreference=secondary,仍应检查 command 调用本身的第二个参数。

MongoDB Node.js 驱动的低层 db.command() 不继承 MongoClient 的选项,也忽略 Db 对象上的读偏好,默认采用 primary。把 readPreference 塞进命令文档,不等于设置驱动用于选择节点和发送读取偏好的选项。

命令文档与驱动选项分别传递

下面是已有连接上的调用示例,假设 db 是目标数据库、items 是待检查的集合。示例读取传统索引过滤器,不修改集合:

const result = await db.command(
  { planCacheListFilters: 'items' },
  { readPreference: 'secondary' }
);

第一个对象描述 MongoDB 命令,第二个对象是驱动执行选项。这样才能明确把读偏好传给本次 command。

下面这种写法不能代替第二参数:

const result = await db.command({
  planCacheListFilters: 'items',
  readPreference: 'secondary'
});

服务器可能忽略不适用的字段,也可能报其他错误;没有立即出现参数语法错误,并不证明选项已经生效。不要以“调用没抛错”推断读取到了指定节点。

planCacheListFilters 属于旧的索引过滤器功能。MongoDB 8.0 起索引过滤器已弃用,新设计应评估 query settings;部分 Atlas 集群类型不支持这个命令。示例用于定位已有代码的选项传递问题,不能据此在所有版本或产品中启用相同功能。

secondary 读取仍有命令和一致性条件

显式设置读偏好不会让所有命令都能在 secondary 上运行。执行前确认该命令在所用服务器版本和部署类型中允许这样调用,并满足所需权限。写操作不能靠 readPreference=secondary 变成从备用节点写入。

如果本来想执行只能在 primary 上运行的管理动作,应按副本集正常发现机制连接并选择 primary,不能在直连 secondary 的配置上只更换读偏好就期待驱动切到另一个节点。

普通读取使用 secondary 还可能读到复制延迟的数据。业务要求刚写入内容立即可见时,需按一致性需求设计读取路径,不能为了消除报错把整个应用统一改成 secondary。

这里的 secondary 与 secondaryPreferred 也不同:前者要求符合条件的 secondary,后者在没有可用 secondary 时可回退到 primary。若排查必须读取备用节点,回退模式的“成功”不能证明它读的是备用节点。

修正后核对原命令,不只检查连接成功

记录原连接配置与调用方式,按最小范围修改失败的 command。连接成功只说明握手和认证等阶段已经通过,不能证明这个命令的执行路由正确。

使用原命令和原目标集合重新检查,确认报错消失、结果结构符合预期,并通过已有监控或受控日志核对目标节点。索引过滤器等状态可能在各节点之间不同,不应把“返回空列表”自动理解为主备配置相同。

若后续出现权限不足、命令不支持或找不到可用 secondary,应按新错误继续核对权限、产品限制或副本集拓扑,不能删除认证或修改复制角色来绕过。回退时恢复本次调用参数,再重新确认应用原有的读写及一致性要求。