19

我有一个 Rails 应用程序,其中有一个 Rake 任务,它使用 concurrent-ruby gem 提供的多线程函数。

我不时遇到Circular dependency detected while autoloading constant错误。

在谷歌搜索了一下之后,我发现这与使用线程结合加载 Rails 常量有关。

我偶然发现了以下 GitHub 问题:https ://github.com/ruby-concurrency/concurrent-ruby/issues/585和https://github.com/rails/rails/issues/26847

正如这里所解释的,您需要将从新线程调用的任何代码包装在一个Rails.application.reloader.wrap doRails.application.executor.wrap do块中,这就是我所做的。但是,这会导致死锁。

然后建议用于ActiveSupport::Dependencies.interlock.permit_concurrent_loads在主线程上包装另一个阻塞调用。但是,我不确定我应该用这个包装哪个代码。

这是我尝试过的,但这仍然会导致死锁:

@beanstalk = Beaneater.new("#{ENV.fetch("HOST", "host")}:#{ENV.fetch("BEANSTALK_PORT", "11300")}")
tube_name = ENV.fetch("BEANSTALK_QUEUE_NAME", "queue")

pool = Concurrent::FixedThreadPool.new(Concurrent.processor_count * 2)

# Process jobs from tube, the body of this block gets executed on each message received
@beanstalk.jobs.register(tube_name) do |job|
    ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
      @logger.info "Received job: #{job.id}"
      Concurrent::Future.execute(executor: pool) do
        Rails.application.reloader.wrap do
          # Stuff that references Rails constants etc
          process_beanstalk_message(job.body)
        end
      end
    end
end

@beanstalk.jobs.process!(reserve_timeout: 10)

谁能阐明我应该如何解决这个问题?奇怪的是我在生产中遇到了这个问题,而关于这个主题的其他信息似乎暗示它通常应该只发生在开发中。

在生产中,我使用以下设置:

config.eager_load = true

config.cache_classes = true.

所有环境的自动加载路径都是 Rails 默认的加上两个特定的文件夹(“models/validators”和“jobs/concerns”)。

eager_load_paths在我的任何配置中都没有修改或设置,因此必须等于 Rails 的默认值。

我正在使用 Rails 5,所以enable_dependency_loading应该等于false生产。

4

3 回答 3

8

您可能需要更改您eager_load_paths的路径以包含引发错误的类或模块的路径。eager_load_paths记录在 Rails 指南中。

您遇到的问题是Rails在应用程序启动时没有加载这些常量;当它们被其他一些代码调用时,它会自动加载它们。在多线程 Rails 应用程序中,两个线程在尝试加载这些常量时可能会出现竞争情况。

告诉 Rails 急切地加载这些常量意味着它们将在 Rails 应用程序启动时加载一次。光说是不够的eager_load = true;您还必须指定类或模块定义的路径。在 Rails 应用程序配置中,这是一个Array. eager_load_paths例如,急切加载ActiveJob类:

config.eager_load_paths += ["#{config.root}/app/jobs"]

或从以下位置加载自定义模块lib/

config.eager_load_paths += ["#{config.root}/lib/custom_module"]

更改您的急切加载设置将影响 Rails 的行为。例如,在 Railsdevelopment环境中,您可能习惯于运行rails server一次,并且每次重新加载某个端点时,它都会反映您对代码所做的任何更改。这不适用于config.eager_load = true,因为类在启动时加载一次。因此,您通常只会eager_load更改production.

更新

您可以eager_load_pathsrails console. 例如,这些是新 Rails 5 应用程序的默认值。如您所见,它没有加载app/**/*.rb;它加载 Rails 应该知道的特定路径。

Rails.application.config.eager_load_paths
=> ["/app/assets",
 "/app/channels",
 "/app/controllers",
 "/app/controllers/concerns",
 "/app/helpers",
 "/app/jobs",
 "/app/mailers",
 "/app/models",
 "/app/models/concerns"]
于 2017-07-27T17:23:11.227 回答
4

在我的 gems(即 inplezi和)中,我主要用语句iodine来解决这个问题。if

您会找到如下代码:

require 'uri' unless defined?(::URI)

或者

begin
  require 'rack/handler' unless defined?(Rack::Handler)
  Rack::Handler::WEBrick = ::Iodine::Rack # Rack::Handler.get(:iodine)
rescue Exception

end

由于Circular dependency detected警告和错误,我使用了这些片段。

我不知道这是否有帮助,但我想你可能想尝试一下。

于 2017-07-23T01:57:18.560 回答
4

我在尝试两个处理并行处理的 gem 时遇到了这个问题;

  1. pmap 宝石
  2. 平行宝石

对于 pmap,我不断收到与 Celluloid::TaskTerminated 相关的错误,对于并行,当我使用超过 1 个线程运行它时,我在自动加载常量时检测到循环依赖。我知道这个问题与我的类和模块如何急切地加载和竞相放置在线程上有关。我尝试在开发环境中启用这两个配置为真config.cache_classes = true,这对我有用config.eager_load = true

于 2018-05-31T16:04:33.943 回答