以下是添加到my app的五个文件(没有数据库,不需要设置):
LIB /任务/ precomputation.rake
namespace :precomputation do
desc "This fetches data for precomputation"
task fetch_all: :environment do
Precomputation.precompute_all_data
# end
end
end
应用程序/模型/ precomputation.rb
class Precomputation
def self.precompute_all_data
ad_accounts = [1,2,3]
bgthread = BackgroundThread::BGThreadPool.new(1)
tasks = []
ad_accounts.each do |ad_account_id|
p = Proc.new do
begin
MongoPipeline::FbAdCampaignMongoPipeline.new(ad_account_id).fetch_all
false
ensure
GC.start
end
end
tasks << [p, "test #{ad_account_id}"]
end
bgthread.add_randomized_tasks(tasks)
bgthread.do_work
end
end
应用程序/模型/ mongo_pipeline.rb
module MongoPipeline
class Base
def initialize(ad_account_id)
end
def insert_data
puts 'inserting data'
end
def fetch_all
extract_data # Child Class defines this method
insert_data # Base class defines this method
end
end
end
应用程序/模型/ mongo_pipeline / fb_ad_campaign_mongo_pipeline.rb
module MongoPipeline
class FbAdCampaignMongoPipeline < MongoPipeline::Base
def extract_data
puts 'here is campaign data'
end
end
end
和app / models / background_thread.rb
(注意:使用并行gem并且没有后台线程的替代实现也会崩溃并出现类似错误 - :https://github.com/pratik60/circular_dependency_havoc/tree/parallel)
错误日志
rake aborted!
Circular dependency detected while autoloading constant MongoPipeline
/Users/pratikbothra/.rvm/gems/ruby-2.2.2/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:492:in `load_missing_constant'
/Users/pratikbothra/.rvm/gems/ruby-2.2.2/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:184:in `const_missing'
/Users/pratikbothra/.rvm/gems/ruby-2.2.2/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:526:in `load_missing_constant'
/Users/pratikbothra/.rvm/gems/ruby-2.2.2/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:184:in `const_missing'
/webapps/circular_dependency_havoc/app/models/precomputation.rb:9:in `block (2 levels) in precompute_all_data'
/webapps/circular_dependency_havoc/app/models/background_thread.rb:91:in `call'
/webapps/circular_dependency_havoc/app/models/background_thread.rb:91:in `block in background'
Tasks: TOP => precomputation:fetch_all
(See full trace by running task with --trace)
关于我做错的任何想法?后台线程库已克隆并刚刚修改。如果您认为这是问题,请随意更换它。任何建议,任何想法都非常欢迎。
答案 0 :(得分:3)
在rake任务中构建线程循环以轮询外部服务器时,我遇到了这个问题,得出的结论是Autoload&amp;不可预测的竞争条件相关。但是我不想简单地加载热切,所以有两种方法可以解决这个问题。一个是弄清楚我的任务中加载了哪些类,并在产生任何线程之前将它们加载到父级中:
# preload classes that showed circular dependencies just in this task
MediaFile
VideoPlatformIntegration
Tag
10.times.map{Thread.new{...}}
这就像一个魅力,但是放入生产代码有点奇怪,并且对任务的任何变化或它调用的任何方法都不健壮。
另一个更不完美,但实际上只是修改了开发环境中的行为,并且不依赖于了解可能导致这种情况的所有事情。由于它只是在开发中,我认为这是可以接受的:在每个初始线程开始时随机休息一段时间(0到1秒之间):
sleep(Random.rand) if Rails.env == "development"
此睡眠可防止线程同时启动,并大大降低加载类中任何竞争条件的可能性。我在这里使用一个简单的10个线程批次进行概念验证,运行脚本4次,没有睡眠4次,显示可靠的循环依赖性错误,没有睡眠:
[video_platform_integration] andrew@~/3p/app3$ rails runner '10.times.map{Thread.new{MediaFile.last rescue puts($!)}}'
Circular dependency detected while autoloading constant MediaFile
Circular dependency detected while autoloading constant MediaFile
[video_platform_integration] andrew@~/3p/app3$ rails runner '10.times.map{Thread.new{MediaFile.last rescue puts($!)}}'
Circular dependency detected while autoloading constant MediaFile
[video_platform_integration] andrew@~/3p/app3$ rails runner '10.times.map{Thread.new{MediaFile.last rescue puts($!)}}'
[video_platform_integration] andrew@~/3p/app3$ rails runner '10.times.map{Thread.new{MediaFile.last rescue puts($!)}}'
Circular dependency detected while autoloading constant MediaFile
Circular dependency detected while autoloading constant MediaFile
[video_platform_integration] andrew@~/3p/app3$ rails runner '10.times.map{Thread.new{sleep(Random.rand); MediaFile.last rescue puts($!)}}'
[video_platform_integration] andrew@~/3p/app3$ rails runner '10.times.map{Thread.new{sleep(Random.rand); MediaFile.last rescue puts($!)}}'
[video_platform_integration] andrew@~/3p/app3$ rails runner '10.times.map{Thread.new{sleep(Random.rand); MediaFile.last rescue puts($!)}}'
[video_platform_integration] andrew@~/3p/app3$ rails runner '10.times.map{Thread.new{sleep(Random.rand); MediaFile.last rescue puts($!)}}'
答案 1 :(得分:1)
我最初更改了此设置
config/environments/development.rb
config.eager_load = true
虽然这没有帮助!
我需要添加
config/initializers/eager_load.rb:
Rails.application.eager_load! unless Rails.env.test?
如果你正在使用一个文件夹,你还需要急切加载整个lib文件夹(自动加载不够)。
config/application.rb
config.eager_load_paths += Dir["#{Rails.root}/lib/**/"]
此外,请确保如果在初始化程序中使用了devise,则将其重命名为01_devise.rb或其他内容,因为初始化程序按字母顺序加载,而您的用户或管理员将引用它。
可能会建议在测试中跳过eager_load,因为它不是最理想的,如果你没有在内部使用线程,请完全跳过它!