You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用IAM角色连接Mongo Server时出现NoServerAvailable错误

问题解决:Mongoid结合AWS IAM会话令牌连接Atlas时出现NoServerAvailable错误

问题背景

在Rails中使用Mongoid连接MongoDB Atlas集群,采用AWS IAM会话令牌认证(令牌1小时过期),实现了令牌过期时重新加载Mongoid配置、生成新连接URL的逻辑。初始运行正常,但一段时间后出现Mongo::Error::NoServerAvailable错误,提示集群无主可用服务器,存在死监控线程。

相关代码

def logs
  reload_mongoid_configuration
  @logs ||= Log.or(attributes_condition)
               .where(selector)
               .order_by(timestamp: :desc)
end

def reload_mongoid_configuration
  mongo_config = Mongoid::Config.clients[:default]
  old_connection_url = mongo_config[:uri]

  return unless mongo_token_expired?(old_connection_url)

  # Disconnect existing Mongoid clients
  Mongoid.disconnect_clients

  # Logic to get mongo URL with a new AWS session token
  Mongoid.load_configuration(
    clients: {
      default: {
        uri: MongoUriGenerator.audit_log_db_url(old_connection_url),
        options: {
          database: 'my_database'
        }
      }
    }
  )
end

错误信息

#<Mongo::Error::NoServerAvailable: No primary server is available in cluster ...
#<Server address=<someaddress>.mongodb.net:27017 UNKNOWN NO-MONITORING pool=#<ConnectionPool size=0 (0-20) used=0 avail=0 pending=0 paused>> #<Server address=<someaddress>.mongodb.net:27017 SECONDARY replica_set=atlas-ihuqqp-shard-0 NO-MONITORING>]> with timeout=30, LT=0.015. The following servers have dead monitor threads

连接URL格式

mongodb+srv://<temp_access key>:<temp_secret_key>@<mongourl>.mongodb.net/<db>?authMechanism=MONGODB-AWS&authSource=$external&authMechanismProperties=AWS_SESSION_TOKEN:IQo....

问题原因分析

  1. 查询对象缓存导致旧连接复用:@logs ||=会缓存查询对象,即使令牌过期并重新加载了配置,后续请求仍会使用旧的查询对象关联的无效连接。
  2. 旧连接资源未彻底清理:Mongoid.disconnect_clients可能没有完全终止旧的监控线程,导致这些线程残留并标记服务器为NO-MONITORING状态。
  3. 重载配置后未主动初始化新连接:重新加载配置后,Mongoid可能不会主动建立新连接,直到第一次查询,此时可能因网络或认证问题导致监控线程启动失败。

解决方案

1. 移除查询对象缓存

删除@logs ||=的缓存逻辑,确保每次查询都使用最新的连接:

def logs
  reload_mongoid_configuration
  Log.or(attributes_condition)
     .where(selector)
     .order_by(timestamp: :desc)
end

2. 改进配置重载逻辑,彻底清理旧资源并初始化新连接

修改reload_mongoid_configuration方法,确保旧客户端完全销毁,新客户端主动初始化:

def reload_mongoid_configuration
  mongo_config = Mongoid::Config.clients[:default]
  old_connection_url = mongo_config[:uri]

  return unless mongo_token_expired?(old_connection_url)

  # 彻底断开并销毁旧客户端
  Mongoid.disconnect_clients
  Mongoid::Config.clients.clear

  # 生成新的连接URL
  new_uri = MongoUriGenerator.audit_log_db_url(old_connection_url)

  # 加载新配置,添加监控与超时参数
  Mongoid.load_configuration(
    clients: {
      default: {
        uri: new_uri,
        options: {
          database: 'my_database',
          server_selection_timeout: 10,
          heartbeat_frequency: 5,
          connect_timeout: 5,
          retry_writes: true,
          retry_reads: true
        }
      }
    }
  )

  # 主动触发连接初始化,启动监控线程
  Mongoid::Clients.default.database_names
end

3. 优化令牌过期检查逻辑

调整mongo_token_expired?方法,提前5分钟刷新令牌,避免在令牌刚过期时才执行刷新,减少连接中断的概率。

4. 改用定时刷新逻辑

将令牌刷新逻辑从请求处理方法中移出,改用后台定时任务(比如每55分钟执行一次),确保应用始终持有有效的连接配置,避免在请求过程中频繁重载配置。

内容的提问来源于stack exchange,提问作者Hassan Anwer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 13:37:44