2011-01-13 6 views
1

Gibt es eine Möglichkeit, parallele Transaktionen mit RSpec zu testen? Wenn ich sage, dass ich ein Bankkonto habe, das in einer Transaktion gesperrt werden muss, bevor es verringert oder erhöht wird. Wie auch immer, obwohl ich die Option RSpec transactional_fixtures deaktiviert habe, kann ich keine parallele Transaktion in zwei getrennten Threads starten. Aus irgendeinem Grund sind beide aufgehängt. Gegeben Konto ist ein Modell Dann ist diese Spezifikation hängen:Parallele Transaktionen mit RSpec testen

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 

Gibt es eine Möglichkeit, es nicht hängen zu machen, während noch in der Lage sein, um die Fähigkeit der Transaktion zu demonstrieren?

Antwort

0

Ich kann mir keinen guten Grund vorstellen, warum Sie parallele, gleichzeitige Transaktionen auf derselben Ressource haben möchten. Was genau versuchen Sie in Ihrem Beispiel zu erreichen?

In Ihrem Code verwenden Sie die gleiche Instanz, so dass die Tatsache, dass Sie sich in einem Deadlock befinden, erwartet wird. Sie möchten wahrscheinlich überdenken, was Sie hier versuchen möchten.

+0

Sagen Sie AR Cache mein 'Account.find()' Ergebnis? –

+0

Nein. Sie erhalten einen Deadlock, weil beide Transaktionen dieselbe Ressource anfordern (und daher sperren), in diesem Fall @account. Wenn Sie auf diese Weise Threads verwenden müssen, empfehle ich die Verwendung der Mutex-Klasse, um sicherzustellen, dass Sie kein Deadlock erhalten. (Google Mutex und Threads und Sie werden viele Beispiele finden). – grokling

1

Sie müssen in Ihren Threads unterschiedliche Verbindungen zur Datenbank verwenden. Zuerst den roten Faden trennen, dann in jedem Thread wieder, und schließlich in der Haupt wieder, etwa so:

ActiveRecord::Base.connection.disconnect! 
(0..1).each do |index| 
    threads << Thread.new do 
    ActiveRecord::Base.establish_connection 
    # ... 
    end 
end 
ActiveRecord::Base.establish_connection 

Es gibt andere Probleme bei der Prüfung auf diese Weise: Sie haben keine Garantie für die Stelle, an der Gleichzeitigkeit passiert. Der Rubin GIL wird auch nicht helfen. Sie sollten stattdessen Forks anstelle von Threads verwenden oder das Fork_Break-Juwel verwenden: https://github.com/remen/fork_break