New to Ruby trying to learn how to debug - ruby-on-rails

I am migrating data from a database and getting an error I cannot understand. I am new to Ruby and am looking for both what is wrong with my code and also the most effective commands for debugging. I cannot even really read my error.
Here is my error:
/Users/skline/.rvm/gems/ruby-1.9.2-p136#rails3tutorial/gems/activemodel-3.0.6/lib/active_model/attribute_methods.rb:367:in `method_missing': undefined method `answer=' for #<Question:0x00000102d59758> (NoMethodError)
from /Users/skline/.rvm/gems/ruby-1.9.2-p136#rails3tutorial/gems/activerecord-3.0.6/lib/active_record/attribute_methods.rb:46:in `method_missing'
from ./script/migrate.rb:139:in `block (2 levels) in <main>'
from /Users/skline/.rvm/gems/ruby-1.9.2-p136#rails3tutorial/gems/activerecord-3.0.6/lib/active_record/relation.rb:13:in `each'
from /Users/skline/.rvm/gems/ruby-1.9.2-p136#rails3tutorial/gems/activerecord-3.0.6/lib/active_record/relation.rb:13:in `each'
from ./script/migrate.rb:137:in `block in <main>'
from ./script/migrate.rb:111:in `each'
from ./script/migrate.rb:111:in `<main>'
Any tips for reading this error and for how to debug.
Note here is my code:
NetworkCommunications.all.each do |nc|
if nc.NETWORK_COMM_TYPE_ID==1 && nc.SENDER_CONSUMER_ID != 0
q = Question.new
q.created_at = nc.LAST_MOD_TIME
category = CommunicationInterestMapping.where(:COMMUNICATION_ID => nc.COMMUNICATIONS_ID).first
if category
cie = ConsumerInterestExpertLookup.find(category.CONSUMER_INTEREST_EXPERT_ID)
if cie
q.category = Category.find_by_name cie.CONSUMER_INTEREST_EXPERT_NAME
else
puts "No category"
end
end
message = NetworkCommunicationsMessage.where(:COMMUNICATIONS_ID => nc.COMMUNICATIONS_ID).first
q.title = message.SUBJECT
q.description = message.MESSAGE
q.permalink = message.QUESTION_SLUG
email = find_email_from_consumer_id(nc.SENDER_CONSUMER_ID)
q.user = User.find_by_email email
children = NetworkCommunications.where(:PARENT_COMMUNICATIONS_ID => nc.COMMUNICATIONS_ID)
puts children
if children
children.each do |ncc|
if ncc.NETWORK_COMM_TYPE_ID == 2
q.answer = Answer.new
q.answer.created_at = ncc.LAST_MOD_TIME
message_a = NetworkCommunicationsMessage.where(:COMMUNICATIONS_ID => ncc.COMMUNICATIONS_ID).first
q.answer.text = message_a.MESSAGE
email_a = find_email_from_consumer_id(ncc.SENDER_CONSUMER_ID)
q.answer.user = User.find_by_email email_a
end
end
end
begin
q.save!
rescue Exception => e
puts "Exception: #{e} title: #{message.SUBJECT}"
end
end
end

To read the stack dump, look at the first line then read downwards:
/Users/skline/.rvm/gems/ruby-1.9.2-p136#rails3tutorial/gems/activemodel-3.0.6/lib/active_model/attribute_methods.rb:367:in `method_missing': undefined method `answer=' for #<Question:0x00000102d59758> (NoMethodError)
from /Users/skline/.rvm/gems/ruby-1.9.2-p136#rails3tutorial/gems/activerecord-3.0.6/lib/active_record/attribute_methods.rb:46:in `method_missing'
from ./script/migrate.rb:139:in `block (2 levels) in <main>'
The first line tells you where the problem was triggered and why: In ActiveModel's attribute_methods method because no setter for answer was found in the object. This was triggered from a call in line 139 of your migrate.rb script. The trick with a stack trace is to read through it, looking for the scripts you've written. Odds are really good the problem is in our code so it's always good to start from the assumption the bug is ours.
if ncc.NETWORK_COMM_TYPE_ID == 2
q.answer = Answer.new
is where the problem is. Your Question class doesn't have a setter for answer. Either you're missing or misspelled an attribute_accessor call or misspelled a def answer= method.
To debug I recommend using Ruby Debugger 1.9. gem install ruby-debug19. It's 1.9.2 studly and easy to use. You can set a breakpoint in your code, then run it from the command-line, which will run until the breakpoint is reached and will stop in the debugger. From there you can list the current lines using l, display the contents of variables using p or do a require 'pp' if you have pretty-printer installed. You can single-step into methods using s or step over them using n, for "next". There's also c to continue, c 100 to continue to a particular line number; 100 in that example. You can use b 100 to set a break-point at line 100, and then c to run, stopping at 100 every time. irb will drop you into IRB with the variables to that point already initialized so you can poke at them. There are lots of other commands, but those are the ones I use most often.

It probably means you have defined the answer attribute for your question class:
class Question < ActiveRecord::Base
attr_accessor :answer
[...]
end
You should also learn how to use rdebug so that you can step through the code and figure this out without help.

I think your model Question doesn't have answer attribute.
In this cast you can study how to debug rails app

Related

Error starting autostart for iOS on x-platform calabash

When I run the test, I write the following error in the console:
undefined method `split' for nil:NilClass (NoMethodError)
/Users/denis/.rvm/gems/ruby-2.1.1/gems/run_loop 1.2.6/lib/run_loop/sim_control.rb:866:in `block in sim_details'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/run_loop-1.2.6/lib/run_loop/sim_control.rb:863:in `each'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/run_loop-1.2.6/lib/run_loop/sim_control.rb:863:in `sim_details'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/run_loop-1.2.6/lib/run_loop/sim_control.rb:290:in `enable_accessibility_on_sims'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/run_loop-1.2.6/lib/run_loop/core.rb:214:in `run_with_options'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/run_loop-1.2.6/lib/run_loop/core.rb:792:in `run'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/calabash-cucumber-0.12.2/lib/calabash-cucumber/launcher.rb:755:in `block in new_run_loop'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/calabash-cucumber-0.12.2/lib/calabash-cucumber/launcher.rb:753:in `times'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/calabash-cucumber-0.12.2/lib/calabash-cucumber/launcher.rb:753:in `new_run_loop'
/Users/denis/.rvm/gems/ruby-2.1.1/gems/calabash-cucumber-0.12.2/lib/calabash-cucumber/launcher.rb:624:in `relaunch'
/Users/denis/Documents/calabash-test-ios/x-platform-example/features/ios/support/01_launch.rb:27:in `Before'
The test itself is an example from here. Here is the code for one of the errors:
def sim_details(primary_key)
unless xcode_version_gte_6?
raise RuntimeError, 'this method is only available on Xcode >= 6'
end
allowed = [:udid, :launch_name]
unless allowed.include? primary_key
raise ArgumentError, "expected '#{primary_key}' to be one of '#{allowed}'"
end
hash = {}
xctools.instruments(:sims).each do |elm|
launch_name = elm[/\A.+\((\d\.\d(\.\d)? Simulator\))/, 0]
udid = elm[XCODE_6_SIM_UDID_REGEX,0]
sdk_version = elm[/(\d\.\d(\.\d)? Simulator)/, 0].split(' ').first
value =
{
:launch_name => launch_name,
:udid => udid,
:sdk_version => RunLoop::Version.new(sdk_version)
}
if primary_key == :udid
key = udid
else
key = launch_name
end
hash[key] = value
end
hash
end
Run this command: bundle exec cucumber -p ios DEVICE_TARGET="6D45E1...6513"
Can anyone come across?
Does the DEVICE_TARGET UUID actually exist in the list of available simulators?
instruments -s will show you the devices. Switching Xcode versions and not rebooting or restarting the CoreSimulator service can result in the simulator not being found.
Looks like this code sdk_version = elm[/(\d\.\d(\.\d)? Simulator)/, 0] is not returning anything. It seems that you are expecting it to return a string. I would debug in that area.
The error is telling you that your selection is resulting in a nil object which of course has no class, and also as such has no .split method. As a result trying to invoke the .split method is giving you the error that the thing you have there, does not have a .split method defined.

ActiveRecord::AssociationTypeMismatch gotNilClass

I am a beginner programmer in Ruby and Ruby on Rails , I'm trying to run a rake command in my task , but when I do the following happens:
rake daily_tasks:process_api
rake aborted!
ActiveRecord::AssociationTypeMismatch: Estado(#47392639701120) expected, got NilClass(#47392580444120)
/home/thiagoamaralr/Desktop/proponente-master-4f8a3b2ddb02a90b2c173cf31383505018d02dd/app/services/create_programa_api.rb:21:in `call'
/home/thiagoamaralr/Desktop/proponente-master-74f8a3b2ddb02a90b2c173cf31383505018d02dd/lib/tasks/daily_tasks.rake:7:in `block (3 levels) in <top (required)>'
/home/thiagoamaralr/Desktop/proponente-master-74f8a3b2ddb02a90b2c173cf31383505018d02dd/lib/tasks/daily_tasks.rake:5:in `each'
/home/thiagoamaralr/Desktop/proponente-master-74f8a3b2ddb02a90b2c173cf31383505018d02dd/lib/tasks/daily_tasks.rake:5:in `block (2 levels) in <top (required)>'
Tasks: TOP => daily_tasks:process_api
(See full trace by running task with --trace)
Follow the task I'm trying to run:
namespace :daily_tasks do
desc "Process the day to day tasks"
task process_api: :environment do
SiconvApi::Programa.find.each do |programa|
if programa.data_inicio_recebimento_propostas && (programa.data_inicio_recebimento_propostas.to_date >= Date.parse("2015/06/01"))
CreateProgramaApi.call(SiconvApi::Serializers::Programa.new(programa))
end
end
end
end
And this is content create_programa_api.rb:
class CreateProgramaApi
def self.call(programa_api)
params = programa_api.to_h
params[:orgao] = Orgao.where("lower(name) = ?", programa_api[:orgao][:nome].mb_chars.downcase).first_or_create(name: programa_api[:orgao][:nome])
params[:orgao_vinculado] = Orgao.where("lower(name) = ?", programa_api[:orgao_vinculado][:nome].mb_chars.downcase).first_or_create(name: programa_api[:orgao_vinculado][:nome])
params[:orgao_executor] = Orgao.where("lower(name) = ?", programa_api[:orgao_executor][:nome].mb_chars.downcase).first_or_create(name: programa_api[:orgao_executor][:nome])
params[:estados] = []
if programa_api[:estados].size == 27
params[:estados] << Estado.find_by(sigla: 'Todos')
else
programa_api[:estados].each do |e|
params[:estados] << Estado.find_by(sigla: e)
end
end
params[:atendes] = [Atende.where("lower(name) = ?", programa_api[:atende_a].mb_chars.downcase).first_or_create(name: programa_api[:atende_a])] if programa_api[:atende_a]
params.delete(:atende_a)
programa = Programa.find_by(codigo: programa_api[:codigo])
if programa
programa.update(params)
else
Programa.create! params
end
end
end
Thanks for your attention!
You have nil object in params[:estados], and Rails can't save this association.
Easiest way to remove them is to call params[:estados].compact! after line 14
This block of code is your problem:
params[:estados] = []
if programa_api[:estados].size == 27
params[:estados] << Estado.find_by(sigla: 'Todos')
else
programa_api[:estados].each do |e|
params[:estados] << Estado.find_by(sigla: e)
end
end
If no record is found, the #find_by returns nil. This is why you are getting the error:
ActiveRecord::AssociationTypeMismatch: Estado(#47392639701120) expected, got NilClass
When calling Programa.create!(params).
One solution would be to call params[:estados].compact! after the if statement (this is using Array#compact! to remove any nil values).
Or, you could instead write that section of code like this:
params[:estados] = Estadio.where(
sigla: (programa_api[:estados].size == 27 ? 'Todos' : programa_api[:estados])
)
With this code, there is no longer a need to call compact! since we already end up with an empty array if no records are found (i.e. there are no nil values).
Note that the behaviour here isn't quite the same - what happens if there are multiple Estado records with the sigla equal to one of programa_api[:estados] or 'Todos'? Previously you were only fetching the "first" such record, whereas now you'd be fetching all of them. This may not be an issue (or may even be the correct behaviour!!) - it's just something to be aware of, at least.

Errors executing jobs with background job manager gem called Que

I'm writing an Rails 4.2.0 application where I need to deliver emails. I've been advised Que gem for managing background jobs. I've done everything as in installation and usage is listed here.
Also I've specified in application.rb these lines:
# For https://github.com/chanks/que
config.active_record.schema_format = :sql
config.active_job.queue_adapter = :que
My job looks like this send_welcome_message.rb :
class SendWelcomeEmail < Que::Job
# Default settings for this job. These are optional - without them, jobs
# will default to priority 100 and run immediately.
#priority = 10
#run_at = proc { 1.minute.from_now }
def run(user_id, options)
#user = User.find(user_id)
UserMailer.welcome_email(#user).deliver_now
# Destroy the job.
destroy
end
end
After running the rails s command my console is populated with these messages:
{
"lib":"que",
"hostname":"...",
"pid":13938,
"thread":69925811873800,
"event":"job_unavailable"
}
And when I enqueue my job like this in controller
SendWelcomeEmail.enqueue 20, priority: 100
and refresh the page, I get the following errors all the time ( despite I can send messages is sync manner without using que ):
{
"lib":"que",
"hostname":"...",
"pid":13938,
"thread":69925811873800,
"event":"job_errored",
"error":{
"class":"ArgumentError",
"message":"wrong number of arguments (1 for 2)"
},
"job":{
"queue":"",
"priority":100,
"run_at":"2015-06-22T01:59:45.187+03:00",
"job_id":11,
"job_class":"SendWelcomeEmail",
"args":[
20
],
"error_count":2
}
}
And when I open rails console in the second terminal and enter there Que.worker_states (it's written here and should return information about every worker in the system) I get [].
I think that I have no workers spawned. Am I right? And how to fix it?
UPDATE
Found error in que log:
wrong number of arguments (1 for 2)
/home/username/train/project/app/jobs/send_welcome_email.rb:8:in `run'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/job.rb:15:in `_run'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/job.rb:99:in `block in work'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/adapters/active_record.rb:5:in `block in checkout'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/abstract/connection_pool.rb:292:in `with_connection'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/adapters/active_record.rb:34:in `checkout_activerecord_adapter'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/adapters/active_record.rb:5:in `checkout'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/job.rb:82:in `work'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/worker.rb:78:in `block in work_loop'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/worker.rb:73:in `loop'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/worker.rb:73:in `work_loop'
/home/username/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/que-0.10.0/lib/que/worker.rb:17:in `block in initialize'
Line 8 is:
def run(user_id, options)
SOLUTION
Now its' working. I've deleted the adapter configuration from application.rb and in place of
SendWelcomeEmail.enqueue 20, priority: 100
wrote
#user = User.find(20)
SendWelcomeEmail.enqueue #user.id, :priority => 100
Now it's works. Funny thing in the second variant the same values are passed to the function. Still error message said that run obtained only 1 argument - 20.
Reading the que gem, it looks like the enqueue method treats priority keyword as a special case: https://github.com/chanks/que/blob/master/lib/que/job.rb#L31
So, your run method is only passed the first argument. The priority keyword gets swallowed by que.
Changing your run method to
def run(user_id)
should fix your issue.

Why do some rake tests fail with 'method_missing: private method location called for...'?

I'm upgrading a Rails 4.0 app to Rails 4.2. All the tests passed prior to the upgrade. However, I am now getting several errors (but no failures). This is the output of one of the tests:
Run options: --seed 30437
# Running:
E
Finished in 1.738307s, 0.5753 runs/s, 0.0000 assertions/s.
/opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.1/lib/action_dispatch/testing/assertions/routing.rb:171:in `method_missing': private method `location' called for #<[MyTestClass]:0xb46557c> (NoMethodError)
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.1/lib/action_dispatch/testing/integration.rb:397:in `method_missing'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest/test.rb:265:in `block in to_s'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest/test.rb:264:in `map'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest/test.rb:264:in `to_s'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:580:in `%'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:580:in `block in aggregated_results'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:579:in `each'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:579:in `each_with_index'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:579:in `each'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:579:in `map'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:579:in `aggregated_results'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:566:in `report'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:638:in `each'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:638:in `report'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:134:in `run'
from /opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/minitest-5.6.0/lib/minitest.rb:56:in `block in autorun'
I can't seem to find anything in my code that could be causing this error. Looking at some of the files mentioned in the stacktrace, it looks like it is erroring while trying to generate a failure message. (Double fail...)
What is the cause of this error?
Edit:
The first line of my test is s = login(:user). If I place a return before that line, the test passes, but if I place it after that line, I still get the error. The login function is defined in my test_helper.rb as follows:
def login(user)
open_session do |sess|
u = users(user)
sess.extend(MySession)
sess.get "/users/sign_in"
sess.assert_response :success
sess.post_via_redirect "/users/sign_in",
user: {
email: u.email,
password: "<password>"
}
assert_equal '/', sess.path
assert_equal "Signed in successfully.", sess.flash[:notice]
end
end
The module MySession contains a few helper methods.
Edit 2:
I opened a rails console session, required my test class, and did MyTestClass.private_methods. This was the output:
=> [:initialize, :_stash_object_in_method, :_superclass_delegating_accessor, :included, :extended, :prepended, :method_added, :method_removed, :method_undefined, :initialize_copy, :attr, :attr_reader, :attr_writer, :attr_accessor, :initialize_clone, :remove_const, :using, :remove_method, :undef_method, :alias_method, :public, :protected, :private, :define_method, :attr_internal_ivar_name, :attr_internal_define, :DelegateClass, :Digest, :timeout, :default_src_encoding, :Nokogiri, :irb_binding, :create_fixtures, :load, :require, :initialize_dup, :sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :warn, :raise, :fail, :global_variables, :__method__, :__callee__, :__dir__, :eval, :local_variables, :iterator?, :block_given?, :catch, :throw, :loop, :respond_to_missing?, :trace_var, :untrace_var, :at_exit, :syscall, :open, :printf, :print, :putc, :puts, :gets, :readline, :select, :readlines, :p, :srand, :rand, :trap, :require_relative, :proc, :lambda, :binding, :caller, :caller_locations, :exec, :fork, :exit!, :system, :spawn, :sleep, :exit, :abort, :Rational, :Complex, :set_trace_func, :gem_original_require, :Pathname, :URI, :rubygems_require, :BigDecimal, :j, :jj, :JSON, :not_implemented, :y, :open_uri_original_open, :pp, :singleton_method_added, :singleton_method_removed, :singleton_method_undefined, :method_missing]
Edit 3:
I opened a rails console session, required my test class, and did test = MyTestClass.new and then test.private_methods. This time, the output did contain :location. This app does have a Location model, and this output appeared in between several other of the app's models, so I suspect that is the problem. The question is now: why do my test classes have private methods named after my app's models? How can I fix or work around this problem? (Obviously I could rename the Location model, but I'd prefer to avoid that, if possible.)
Did the location method change from public to private on whatever class you're calling it on between Rails 4.0 and 4.2?
Maybe I'm misunderstanding, but the error seems pretty straightforward. Somewhere (in your code, or maybe a gem you rely on that's changed versions as well) a method called location is being called which is private. The stack track shows you where it's happening, so take a look at that code and see what's going on.
Might also be helpful to see the test code, but the stack trace is pointing you to the source from which you can trace back what's going on.
I looked through the code on github as well and can't find anything. Any chance you defined a method called location on MyTestClass that overrides the one in MiniTest::Test ? Also, if you go into your console and simply require the MyTestClass, and call .private_methods on it (or rather an instance of it), does :location indeed show up in the list? Any include statements anywhere that you can find in your code that might be bringing in a location method into MyTestClass?
It looks like this question/answer might provide a way to track down where these methods are coming from.
Using the .method and .source_location methods from #jefflunt's link, I was able to find the cause of the problem. When I did test.method(:location).source_location from the rails console, I got this output:
=> ["/opt/rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/activerecord-4.2.1/lib/active_record/fixtures.rb", 885]
When I looked up that line in that file, I found a method that defines private accessor methods for each fixture. Since I had fixtures defined for my Location model, that was the cause of this problem.
Since I don't see another way around this problem (aside from perhaps ditching fixtures, which I'm not ready to put time into just yet), I'll be renaming my Location model.

Why am I getting deadlocks? Using Thread, Queue, and ActiveRecord. Ruby 1.9.3, Rails 2.3.18

I mitigate the low success rate of a particular operation by running multiple threads and waiting for the first one to return with a valid answer. I've created a minimal example below:
THREADS = [] if !defined?(THREADS)
def threadtest
THREADS.clear
queue, mutex, result = Queue.new, Mutex.new, nil
10.times do |i|
THREADS << Thread.new do
counted = (10e6 + rand(10e6)).to_i.times { } # randomize computation time
num = rand(8) # succeed only 1/8 of the time
#mutex.synchronize do # even if wrapped in mutex
# print "#{User.last.inspect}\n" # deadlocks below if uncommented
#end
queue << [num, counted, i]
end
end
10.times do |i|
result, counted, num = queue.pop # deadlocks here
if result.zero?
puts "#{i}: #{num} succeeds after #{counted} counts"
break
elsif num.present?
puts "#{i}: #{num} failed with #{result} after #{counted} counts"
end
end
THREADS.each(&:exit)
return result
end
30.times { threadtest } # deadlock happens on "high load"
The error looks like:
fatal: deadlock detected
from /usr/lib/ruby/1.9.1/thread.rb:189:in `sleep'
from /usr/lib/ruby/1.9.1/thread.rb:189:in `block in pop'
from <internal:prelude>:10:in `synchronize'
from /usr/lib/ruby/1.9.1/thread.rb:184:in `pop'
from (irb):38:in `block in threadtest'
from (irb):37:in `times'
from (irb):37:in `threadtest'
from (irb):53:in `block in irb_binding'
from (irb):53:in `times'
from (irb):53
from /usr/local/bin/irb:12:in `<main>'
I've tried many variations in an attempt to prevent the deadlock, all to no avail. I can detail some of the experimentations I've done so far if requested. I do have one more idea that I've been avoiding due to the amount of refactoring it'd require in my application.
Is it simply the case that ActiveRecord can't be accessed via multiple threads?
I'll update with a few more details as I think of them.
'deadlock detected' error in rails is the the closest related question I found, but it's got no answers.
I had the same error and had to add the
gem 'net-ssh-gateway'
in the file Gemfile.lock, because the version that was bundled didn't work with the installed ruby. Seems like the dependency wasn't resolved any more in the newer ruby version.

Resources