Rails6多租户改造staging环境出现reading角色primary连接池不存在错误
报错原因
database.yml存在语法错误:primary_subdomain_2和replica_reader_subdomain_2的host配置行ERB标签缺失左尖括号,误写为%=,导致这两个租户的数据库配置无法被Rails正确解析加载,配置不存在时会自动 fallback 查找默认的primary连接池。- Rails启动时会默认初始化名为
primary的连接池,你的staging环境配置中没有定义默认的primary数据库项,而本地开发环境继承了default配置里的默认数据库,所以本地运行正常,staging启动后找不到默认连接池。一旦有请求在进入多租户中间件的connected_to块之前触发了数据库操作(比如健康检查路由、前置中间件的数据库查询逻辑),就会直接抛出找不到primary连接池的错误。 - 缺少子域名为空的兜底处理:如果请求没有子域名(比如直接访问根域名),
subdomain方法返回nil,拼接出来的数据库配置名是primary_、replica_reader_,这类配置不存在,也会触发 fallback 查找默认primary连接池的逻辑,导致报错。
修复方案
- 第一步修复
database.yml的语法错误,将两个错误的ERB标签补全:
staging: # 原有其他配置不变 primary_subdomain_2: <<: *default host: <%= ENV.fetch("MYSQL_HOST2") %> replica_reader_subdomain_2: <<: *default host: <%= ENV.fetch("MYSQL_HOST2_REPLICA") %> replica: true
- 第二步在staging配置中添加默认的
primary数据库作为兜底,不需要存储业务数据,仅用于Rails初始化连接池:
staging: primary: <<: *default database: <%= ENV.fetch("MYSQL_DEFAULT_DB") { 'staging_default' } %> # 原有其他租户配置不变
- 第三步添加子域名合法性校验,子域名不存在或者无对应配置时直接返回错误,避免查询无效配置:
def call(env) @env = env current_subdomain = subdomain # 校验子域名是否存在、是否有对应配置 unless current_subdomain.present? && ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: "primary_#{current_subdomain}").present? return [404, {'Content-Type' => 'text/plain'}, ['Invalid tenant']] end database = { writing: "primary_#{current_subdomain}".to_sym, reading: "replica_reader_#{current_subdomain}".to_sym } ActiveRecord::Base.connected_to(database: database) do @app.call(env) end end
- 第四步调整中间件加载顺序,将多租户中间件放在所有可能访问数据库的中间件之前,确保所有请求的数据库操作都被包裹在
connected_to块内,在config/application.rb中添加:
config.middleware.insert_before 0, Middlewares::Multitenancy
内容的提问来源于stack exchange,提问作者Taranjyot Singh Multani
相关产品推荐
相关产品推荐

