问题
Is there any way to test parallel transaction using RSpec?
Saying I have a bank account balance that need to be locked inside a transaction before it is decreased or increased.
However, currently, although I have turned off RSpec transactional_fixtures
option, I cannot launch 2 parallel transaction in two separated threads. For some reason, both are hung.
Given Account is a model
Then this spec will hang:
it "should ensure operations are performed correctly" do
@account = Account.create(:balance => 0)
threads = []
(0..1).each do |index|
threads << Thread.new do
Account.transaction do
account = Account.find(@account.id, :lock => true)
account.balance += 100
sleep 0.5
account.save!
end
end
end
threads.each{|t|t.join}
@account.reload.balance.should == 200
end
Is there any way to make it not hang while still be able to demonstrate the ability of transaction?
回答1:
You need to use different connections to the database in your threads. First, disconnect the main thread, then reconnect in each thread, and finally reconnect in the main, like so:
ActiveRecord::Base.connection.disconnect!
(0..1).each do |index|
threads << Thread.new do
ActiveRecord::Base.establish_connection
# ...
end
end
ActiveRecord::Base.establish_connection
There are other problems in testing that way: You have no guarantee of the point where concurrency happens. The ruby GIL will also not help. You should use forks instead of threads, or use the fork_break gem: https://github.com/remen/fork_break
回答2:
I cannot think of a good reason why you'd want to have parallel, concurrent transactions on the same resource. What exactly are you trying to achieve in your example?
In your code you're using the same instance so the fact that you're getting yourself into a deadlock is expected. You probably want to re-think what you're trying to do here.
来源:https://stackoverflow.com/questions/4682575/testing-parallel-transactions-using-rspec