【问题标题】:Define ActiveRecord connection for given Resque queue为给定的 Resque 队列定义 ActiveRecord 连接
【发布时间】:2013-04-09 07:45:00
【问题描述】:

问题域

Heroku Cedar 堆栈,具有多个数据库。 RDS 用于主数据库,Postgres 用于第二个 Analytics 数据库。服务器使用读/写 RDS Postgres 数据库运行。 Nightly rake 任务,在不同的环境中运行,需要在 RDS 数据库的只读从属中运行特定的 Resque 队列。

Postgres 数据库连接

作为记录,Postgres 数据库中的所有模型包括:

module Trove::PostgresConnection
  def self.included(base)
    base.class_eval do
      …set up Postgres database
    end
  end
end

这很好用,并且作为一个注入到每个类中的模块,不会被 ActiveRecord::Base.connection 的任何更改压扁

MySQL 连接

使用 Heroku RDS 插件定义。连接到读/写生产数据库。不幸的是,无论环境如何都使用此连接。因此,使用 RAILS_ENV=analytics rake some:task 在 Heroku 上运行 rake 任务不会使用 ActiveRecord::Base 的此连接:

analytics:
  adapter: mysql2
  encoding: utf8
  database: dbase
  username: uname
  password: pword
  host: read-only.uri.on.amazonaws.com
  reconnect: true
  port: 3306

而是使用RDS连接中提供的连接字符串:

puts Rails.env
-> 'analytics'
puts SomeModel.connection_config[:host]
-> read-write.uri.on.amazonaws.com

我花了一段时间才弄明白。自我提醒:不要只看环境,还要看数据库主机。

当前的解决方法

# Perform an operation using another database connection in ActiveRecord
module SwapConnection

  def with_connection_to(config, &block)
    ActiveRecord::Base.connection.disconnect!
    ActiveRecord::Base.establish_connection(config)

    yield

  end
end

require 'swap_connection'
class TroveCalculations
  @queue = :trove_queue

  def self.perform(class_name, id)
    SwapConnection.with_connection_to(Rails.env) do
      Do something in a given queue
    end
  end
end

期望的能力

在 Procfile 中有这样的东西

troveworker:    env RAILS_ENV=analytics QUEUE=trove_queue bundle exec rake resque:work

实际上为该工作人员使用了分析 database.yml 配置。我们目前使用这个 Procfile 运行我们的服务器,但它仍然使用 RDS 数据库。

【问题讨论】:

  • 在黑暗中射击...您的应用正在从环境变量中获取 RDS 配置(耶 Heroku!),您可以这样使用不同的值吗?
  • 是的,就是这样。我的观点是,当 40 万个作业被放入队列时,我不想在每个作业的基础上更改它。我希望能够说“对于整个队列,为所有工作人员使用这个数据库字符串。”我好像不能。

标签: activerecord heroku resque


【解决方案1】:

为了扩展我对该问题的评论,我的意思是为您的数据库添加一个“Heroku 方式”配置,然后在您的 Procfile 中为将处理该队列上的作业的一个工作人员引用它。

使用新名称为您需要的数据库配置添加一个配置/环境变量:

heroku config:add ANALYTICS_DB=postgres://some_url

并在您的Procfile 中,根据您想要的示例:

troveworker:    env DATABASE_URL=$(ANALYTICS_DB) QUEUE=trove_queue \
                bundle exec rake resque:work

您必须通过这种方式为每个队列使用不同的配置,但配置至少在代码之外。

【讨论】:

  • 啊,是的。我假设这会被全局配置破坏,但我还没有真正尝试过。我会试试看。
【解决方案2】:

我只玩过 Heroku,但我认为 Heroku 工具会根据 Heroku 工具带指定的环境变量覆盖数据库连接信息。

【讨论】:

    【解决方案3】:

    这里的问题是 Heroku 生成了自己的 database.yml 文件:https://devcenter.heroku.com/articles/ruby-support#build-behavior

    通过使用 Amazon RDS 插件,Heroku 设置了一个 DATABASE_URL 环境变量。您可以通过从应用程序目录的根目录运行以下命令来查看其内容: heroku config

    此外,从 Rails 3.2 开始,它将使用 DATABASE_URL env var(如果已设置)而不是 database.yml 文件:

    https://github.com/mperham/sidekiq/issues/503#issuecomment-11862427

    最简单的解决方法可能是:

    1. 使用 Postgres 连接字符串创建一个名为 DATABASE_URL_ANALYTICS 的环境变量: heroku config:add DATABASE_URL_ANALYTICS=postgres://xxxxxxxxxxxx

    2. 在 rake 文件的开头(在任何 rails 初始化可能发生之前),添加: ENV['DATABASE_URL'] = ENV['DATABASE_URL_ANALYTICS'] if Rails.env.analytics?

    【讨论】:

    • 我们的 rake 任务启动多个作业,一些是只读的,一些是写的。 rake 文件的环境不会影响 Resque 作业的环境,因为它们是在自己的 Rails 初始化中分叉的。我们在每个作业的基础上有效地执行此显式连接定义,但在每个队列的基础上会更好,因此我们不必为每个只读作业切换。
    【解决方案4】:

    更新:不工作。 (原始答案留作文档)

    我们就是这样解决的:

    过程文件:

    troveworker:    env RAILS_ENV=analytics QUEUE=trove_queue rake trove:worker
    

    lib/tasks/trove.rake:

    desc 'Start the Resque workers in the proper environment'
    task :worker do
      SwapConnection.with_connection_to Rails.env do
        Rake::Task['resque:work'].invoke
      end
    end
    

    这个解决方案也为我们解决了一些其他问题,而且效果很好。谢谢大家。

    【讨论】:

      猜你喜欢
      • 2013-01-26
      • 1970-01-01
      • 2012-05-11
      • 2017-05-22
      • 1970-01-01
      • 2014-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多