在 Heroku 上预编译资产时如何普遍跳过数据库接触

Posted

技术标签:

【中文标题】在 Heroku 上预编译资产时如何普遍跳过数据库接触【英文标题】:How to universally skip database touches when precompiling assets on Heroku 【发布时间】:2011-11-17 20:05:56 【问题描述】:

我正在将 Rails 3.1 应用程序部署到 Heroku 的 Cedar 堆栈。使用Heroku Cedar and Rails 3.1,您可以自己在本地编译资产,让 Heroku 在您推送时编译它们(在“slug 编译”期间),或者让它们在应用程序运行时即时编译。我想做中间选项,让 Heroku 预编译资产。

当 Heroku 运行 assets:precompile 任务时,它会出现 "could not connect to server" 错误,因为应用程序正在尝试连接到数据库,但在 slug 编译的那个阶段没有可用的数据库。在这一点上,缺少数据库连接是意料之中的,也是不可避免的。我正在寻找一种绕过它的方法,因为数据库连接对于资产预编译并不重要。

我的应用程序中尝试连接到数据库的部分是 Devise。 routes.rb 中有一个devise_for :users 行,它想查看 User 模型。

我可以只写一个 rake 任务来存根 devise_for 并将其作为 assets:precompile 的先决条件。我认为这可以解决我的问题,但是我正在寻找一个更通用的解决方案,我可以在 任何 Rails 3.1 应用程序上使用该解决方案来解决 Heroku 上的这个问题。

那里有什么东西吗,或者你能想出什么东西可以消除数据库连接错误,同时仍然运行应用程序足以生成路由和资产路径?

很明显,如果一个应用程序需要在启动期间读取/写入数据,我们不能存根,但我们可以自动伪造每个 ActiveRecord 模型吗?

【问题讨论】:

【参考方案1】:

将此添加到 config/application.rb

config.assets.initialize_on_precompile=false                                                  

我花了一段时间才找到这个...将它添加到 config/environments/*.rb 没有工作

更新:它不适用于 rails 4

【讨论】:

看来,如果您在主 sass 文件中使用 @import('compass') (以获取所有 compass mixin),这将不起作用。它出现“找不到要导入的文件或无法读取:指南针。”的错误。 require 'compass' 包含在 application.rb 文件中,但如果未加载环境,则不会加载。有谁知道解决这个问题的方法吗? 这也被官方 Rails 指南中关于资产管道的引用:guides.rubyonrails.org/asset_pipeline.html#precompiling-assets 也为我工作 - 使用 Oracle。 这是正确的方法。只需确保未在清单中声明的​​任何资产都添加到预编译数组中,例如 this。使用heroku labs:enable user-env-compile 的答案也可以,但由于该功能是实验性的,我暂时不会使用它。 这对我也有用,关键是“将其添加到 config/environments/*.rb 不起作用”。而是将其添加到application.rb,如答案中所述。【参考方案2】:

Heroku 现在提供了一个 labs 标志,这将使运行时环境在编译期间可用,这意味着您的应用将能够成功连接到您的 DATABASE_URL 数据库。

首先你需要安装labs插件:

$ heroku plugins:install http://github.com/heroku/heroku-labs.git

然后启用user-env-compile labs feature:

$ heroku labs:enable user-env-compile --app your-app-name

【讨论】:

在此页面上的所有建议中,这是唯一对我有用的东西。仅供参考:Rails 3.2.0.rc2 最后,一个可行的解决方案。我希望 Heroku 能在他们的 Rails 故障排除页面上记录这个特性。 这就是答案!如果您有任何尝试无条件访问数据库的 gem(设计?active_admin?等),那么这是唯一有效的解决方案。非常感谢。【参考方案3】:

对我来说,问题是在lib/active_record/railtie.rb:92 中调用instantiate_observer 的activerecord。这将加载观察者和相应的模型。 has_and_belongs_to_many 然后连接到数据库。

我想我会在 ENV["RAILS_ASSETS_PRECOMPILE"] 存在时覆盖此方法,该方法由设计在 Bradley 链接到的修复程序中使用。

编辑:所以这个 sn-p 为我修复了它:

namespace :assets do
  # Prepend the assets:precompile_prepare task to assets:precompile.
  task :precompile => :precompile_prepare

  # This task will be called before assets:precompile to optimize the
  # compilation, i.e. to prevent any DB calls.
  task 'precompile_prepare' do
    # Without this assets:precompile will call itself again with this var set.
    # This basically speeds things up.
    ENV['RAILS_GROUPS'] = 'assets'

    # Devise uses this flag to prevent connecting to the db.
    ENV['RAILS_ASSETS_PRECOMPILE'] = 'true'

    # Prevent loading observers which will load the models which in turn may hit
    # the DB.
    module ActiveModel::Observing::ClassMethods
      def instantiate_observers; end
    end

    # Prevent route drawing because certain gems might get called which will hit
    # the DB.
    class ActionDispatch::Routing::RouteSet
      def draw; end
    end
  end
end

【讨论】:

谢谢,我整个上午都在为这个问题而烦恼! 对我也有帮助。谢谢。【参考方案4】:

Rails(4.2 边缘)的解决方法:

将以下内容添加为/config/initializers/precompile.rb

module Precompile

  # Public: ignore the following block during rake assets:precompile
  def self.ignore

    unless ARGV.any?  |e| e == 'assets:precompile' 
      yield
    else
      line = caller.first
      puts "Ignoring line '#line' during precompile"
    end

  end

end

并像这样在您的routes.rb 中使用它:

Precompile.ignore  ActiveAdmin.routes(self) 

【讨论】:

【参考方案5】:

编辑:此答案已过时且不再有效 - 请参阅 fringd 的答案。

不是一个通用的存根,但设计现在添加了一个检查来解决这个特殊问题。请参阅 Github 上的 issue 和 fix。通过提供 RAILS_ASSETS_PRECOMPILE 环境配置设计应该跳过构建路线

【讨论】:

太棒了!感谢您提醒我这一点。我已经在 Heroku 的工单中添加了如果他们还没有设置这个环境变量的话,他们应该设置这个环境变量。 抱歉,我认为这个修复实际上已经从 Devise 和 Rails 中撤出,Jose Valim 做了一些花哨的步法来尝试解决这个问题,但我认为他认为这不是问题他们在这个时间点上可以做的太多了。如果它仍然被 Devise HEAD 和 Rails 3-1-stable 分支所破坏,恐怕我认为除了研究你的设计模型并试图查明导致问题的具体方法之外,别无他法。 感谢您的更新。 Heroku 告诉我他们也在从不同的角度解决这个问题。猜猜我们只需要等待它摆脱。我有针对我自己情况的解决方法。【参考方案6】:

我将它保存在“lib/tasks/assets.rake”中,并且能够让 assets:precompile 真正成功。只要您由于需要您的环境而没有实际访问数据库,这应该可以工作。它显然对 ActiveRecord 没有帮助,但它应该适用于所有基于 mongoid 的应用程序。

task 'assets:precompile' => 'assets:stub_mongoid'

task 'assets:stub_mongoid' do
  def Mongoid.load!(*args)
    true
  end
end

【讨论】:

【参考方案7】:

Heroku 添加了一个非官方标志,以使环境(即数据库)在预编译期间可访问。只需要求他们打开它,资产预编译期间的数据库依赖关系就不再是问题。不确定,这个标志是否/何时正式可用,或者它是否只是新的默认值。

【讨论】:

谢谢,尼科。我昨天在帮一个朋友做这个,我们当然可以用它!我认为这将帮助很多人。干杯! 您现在可以自己启用此标志。我会发布一个关于它的答案。【参考方案8】:

Spork.trap_method 也是一个有趣的解决方案,用于解决 Devise 的 routes_for 在加载过程早期调用模型的问题。解决方案不能直接应用 AFAIK,但它解决的是同类问题,因此它可能会为某人提供灵感。

Spork.trap_method

【讨论】:

【参考方案9】:

我缺乏足够的声誉来发表评论,所以这是另一个答案。

@fringd 的***答案确实不适用于 Rails 4。但是,我发现这种技术有效:

https://iprog.com/posting/2013/07/errors-when-precompiling-assets-in-rails-4-0

不过,我重新排列了 BASH 变量,如下所示:

~$ RAILS_ENV=production DATABASE_URL=postgresql://user:pass@127.0.0.1/dbname bundle exec rake assets:precompile

顺便说一句,如果您需要构建 Docker 映像,这是一个极好的帮助。将该行放入您的 Dockerfile 中,以便您的数据库可以存在于不同的容器中,并且您的应用程序容器不需要在每次启动时预编译资产!

【讨论】:

【参考方案10】:

禁用 AR:

config = Rails.application.config
def config.database_configuration
  
end

ar = ActiveRecord::Base
def ar.establish_connection
end

【讨论】:

以上是关于在 Heroku 上预编译资产时如何普遍跳过数据库接触的主要内容,如果未能解决你的问题,请参考以下文章

是否可以跳过 Heroku 上的单个 git push 的资产预编译步骤?

带有 Puma 和 Nginx 服务页面的 Elastic Beanstalk 上预编译资产的 Rails 4 应用程序以及旧资产链接

Rails 4 - 在生产服务器上预编译资产后没有 manifest.json

使用 webpack 部署到 Heroku 时预编译资产时出错

导致资产预编译在heroku部署上失败的代码片段

我可以在资产编译后删除 Heroku 上的应用程序/资产以减少 slug 大小吗