2017-07-19 13 views
9

私はRailsタスクを持っており、Railsタスクはconcurrent-ruby gemによって提供されるマルチスレッド機能を使用しています。Railsでのマルチスレッド:自動ローディング中に循環依存関係が検出されました

時々私はCircular dependency detected while autoloading constantエラーが発生します。

ちょっとグーグルで調べたところ、これはRails定数の読み込みと組み合わせてスレッディングを使用することに関連していることがわかりました。私は、次のGitHubの問題につまずい

https://github.com/ruby-concurrency/concurrent-ruby/issues/585https://github.com/rails/rails/issues/26847

として、あなたは私が何をしたかであるRails.application.reloader.wrap doまたはRails.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のデフォルトと2つの特定のフォルダ( "モデル/バリデータ" & "jobs/concerns")です。

eager_load_pathsは、私の設定で変更または設定されていないので、Railsのデフォルトと同じでなければなりません。

私はRails 5を使用していますので、enable_dependency_loadingfalseと同じにする必要があります。

+0

どのRails環境でこれを試してもエラーが発生しましたか?これらの環境の 'eager_load_paths'と' autoload_paths'とは何ですか? (通常は 'config/application.rb'と' config/environments/*。rb'にあります) – anothermh

+0

ああ - 私は@anothermhと同じ質問をしようとしていました。あなたのapplication.rbでは、私はあなたがこれらの2つのセットのいずれかを持っていると思っています。 ' config.enable_dependency_loading = false'でオートローディングを無効にすると役立ちます。 – stef

+0

@anothermh修正回答をご覧ください。プロダクションのためにパスを自動ロードしないでください(たとえば、この設定をapplication.rbではなくdevelopment.rbに入れてください)。循環依存性エラーメッセージに含まれる定数は、それらの 'autoload_paths'で定義されていないことに注意してください。 – edwardmp

答えて

3

私の宝石では(すなわちpleziiodine)、私はifステートメントでこれを解決します。

次のようなコードを見つけることができます:私は理由Circular dependency detected警告やエラーのこれらのスニペットを使用

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 

を。

これが役立つかどうかわかりませんが、試してみるといいかもしれません。

+0

ありがとうございます。これは非Railsコンテキストではうまくいくかもしれませんが、Railsでは手動では何も必要ありませんが、すべてのRailsクラスは代わりに自動ロードされます。 – edwardmp

+0

@edwardmp私はあなたがあなたの 'rake'タスクに必要なものだと思っていたので、' bundle exec rake 'と呼んだとき、それはシンボルの自動読み込みと衝突していました。 – Myst

+0

問題ありません、あなたの入力をいただきありがとうございます。あなたの答えは、非Railsの文脈で同じ問題に遭遇するすべての人にとって有益です。 – edwardmp

4

eager_load_pathsには、エラーを発生させているクラスまたはモジュールへのパスを含める必要があります。 eager_load_pathsは、in the Rails Guidesと記載されています。

問題は、アプリが起動するときにRails is not loading these constantsです。他のコードによって呼び出されたときに自動的にロードされます。マルチスレッドのRailsアプリケーションでは、2つのスレッドがこれらの定数をロードしようとすると競合状態になることがあります。

これらの定数を熱心に読み込むように指示すると、Railsアプリケーションの起動時に一度読み込まれることを意味します。それは言うには不十分ですeager_load = true;クラス定義またはモジュール定義へのパスも指定する必要があります。 Railsアプリケーションの設定ではArrayeager_load_pathsの下にあります。たとえば、熱心な負荷にActiveJobクラス:

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

またはlib/からカスタムモジュールをロードする:

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

あなたの熱心な負荷設定を変更するには、レールの行動に影響を与えます。たとえば、Rails development環境では、おそらくrails serverの実行に慣れていて、エンドポイントの1つをリロードするたびに、変更したコードが反映されます。起動時にクラスが一度読み込まれるため、config.eager_load = trueでは動作しません。したがって、通常はproductioneager_loadの設定を変更するだけです。

更新

あなたはrails consoleから、既存のeager_load_pathsを確認することができます。たとえば、これらは新しい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"] 
+0

私は/ appのカスタムサブディレクトリをロードするだけなので、必要な場合でもロードしたいと思っています。私はちょうど彼らが/ app – edwardmp

+0

の子孫としてデフォルトで読み込まれて読んだと思う応答に応答します。短い答え:いいえ、デフォルトでは読み込まれません。 – anothermh

+0

悲しいことに、この問題は再浮上しました。私はこれが本当にこの場合の解決策だとは思わない。 – edwardmp

関連する問題