在服务账户密钥过期后,Atlas Search的 $vectorSearch会向所有用户返回“PlanExecutor :: Authentication failed”
在一个MongoDB Atlas M10集群(GCP,MongoDB 8.0.23)上,$vectorSearch 查询突然对所有用户返回 PlanExecutor error during aggregation :: caused by :: Authentication failed —— 无论是我的管理员用户(atlasAdmin 角色)还是按租户划分的 readWrite 用户。
奇怪的部分:使用 相同的凭据,在 相同的集群/集合 上,这些操作都正常工作:
client[db].embedded_movies.estimated_document_count() # → 3483 OK
list(client[db].embedded_movies.list_search_indexes()) # → vectorPlotIndex, READY, queryable=true
client.admin.command('connectionStatus') # → roles: atlasAdmin, readWriteAnyDatabase, ...
但我测试的每个用户都失败了:
list(client[db].embedded_movies.aggregate([
{"$vectorSearch": {
"index": "vectorPlotIndex",
"path": "plot_embedding",
"queryVector": [0.01] * 1536,
"numCandidates": 50, "limit": 3,
}},
]))
# pymongo.errors.OperationFailure:
# PlanExecutor error during aggregation ::
# caused by :: Authentication failed.
# {'code': 8, 'codeName': 'UnknownError'}
向量搜索索引在所有分片上通过Admin API显示 status: READY, queryable: true。我尝试过:
- 通过Atlas Admin API(非mongosh)删除并重新创建索引 → 同样的错误
- 具有不同角色级别的多名用户 → 同样的错误
- 从不同机器 / 不同的pymongo版本连接 → 同样的错误
- Atlas自动触发
CLUSTER_UPDATE_STARTED+ 3×HOST_MONGOT_RESTARTED事件,在服务账户密钥过期后5 分钟发生 → 未解决
在Atlas项目事件日志中我注意到的情况:
21:34Z (SA secret expires)
23:27Z Atlas sends alert "Organization Service Account Secrets have expired"
23:32Z CLUSTER_UPDATE_STARTED
23:34Z HOST_MONGOT_RESTARTED (shard 0)
23:36Z HOST_MONGOT_RESTARTED (shard 1)
23:37Z HOST_MONGOT_RESTARTED (shard 2)
23:37Z CLUSTER_UPDATE_COMPLETED
23:38Z+ $vectorSearch starts failing
时间点强烈表明mongot(Atlas Search进程)为某些内部认证缓存了旧的服务账户密钥,自动重启没有刷新它。
如何在不重启整个集群的情况下强制Mongo刷新其内部身份验证缓存?还是我对错误的理解有误?
解决方案
我发现这个根本原因:当Atlas Organization Service Account有 2个活跃密钥时(标准轮换模式),已过期的那个在过期后仍然会给Atlas Search带来问题。mongot(Atlas Search进程)似乎在内部缓存了该密钥以进行自我校验,而Atlas的自动 HOST_MONGOT_RESTARTED 流程并不能彻底刷新这个缓存。
修复办法:通过Atlas Admin API显式 DELETE 过期的密钥:
curl -X DELETE \
"https://cloud.mongodb.com/api/atlas/v2/orgs/${ORG_ID}/serviceAccounts/${SA_CLIENT_ID}/secrets/${EXPIRED_SECRET_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/vnd.atlas.2024-08-05+json"
→ 响应:HTTP 204 No Content
→ 紧接着,$vectorSearch 再次生效。无需重启集群。
如何识别受影响的密钥:
curl "https://cloud.mongodb.com/api/atlas/v2/orgs/${ORG_ID}/serviceAccounts/${SA_CLIENT_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/vnd.atlas.2024-08-05+json"
在过去的记录中寻找带有 expiresAt 的密钥——那就是需要删除的密钥。
教训总结:
- 错误信息
PlanExecutor :: Authentication failed具有误导性——它暗示被查询的集合存在权限问题,但实际根本原因是Atlas Search的内部认证状态。如果看到此问题,请检查你的服务账户密钥。 - Atlas自动因密钥过期触发的
HOST_MONGOT_RESTARTED事件并不能解决这个问题——需要手动DELETE。 - 将此步骤纳入你的SA轮换运行手册:创建新密钥 → 更新应用 → 等待旧密钥过期 → 显式删除旧密钥。
为持续监控,这段脚本用于检查仍然存在且已过期的密钥(你可以把它接到cron +警报系统):
# Pseudocode
sa = atlas_api_get(f"/orgs/{ORG_ID}/serviceAccounts/{SA_CLIENT_ID}")
for secret in sa["secrets"]:
if datetime.fromisoformat(secret["expiresAt"]) <= datetime.now(UTC):
alert(f"Expired SA secret still present: {secret['id']} — DELETE it")