2009-05-01 3 views
4

Irgendwie bekomme ich diese immer freitags.Warum funktioniert das optimistische Sperren von ActiveRecord nur einmal pro Zeile?

Meine frühere Frage war in Bezug auf das gleiche Problem, aber ich kann jetzt schmale Dinge ein wenig nach unten:

Ich habe mit dieser ganzen Tag spielen und versuchen, einen Sinn zu machen. Ich habe eine Tabelle mit einem lock_version colum, so angegeben:

add_column :jobs, :lock_version, :integer, :default=>0 

Und ich etwas tun, wie folgt aus:

foo = job.create! 
first = Job.find(foo.id) 
second = Job.find(foo.id) 

ich überprüfen dann, dass erste und zweite auf das gleiche Objekt beziehen - ihre IDs sind die Das gleiche und ich sehe diese Zeile in der Datenbank mit dem mysql-Befehlszeilentool.

first.some_attribute_field = 'first' 
second.some_attribute_field = 'second' 
first.save 
second.save 

kein Problem so weit. Ich bekomme eine ActiveRecord :: StaleObjectError-Ausnahme korrekt. JEDOCH:

first = Job.find(foo.id) 
second = Job.find(foo.id) 
first.some_attribute_field = 'first' 
second.some_attribute_field = 'second' 
first.save 
second.save 

... und nichts passiert. Es stellt sich heraus, dass das einzige Mal, wenn ich das richtige (ausgelöste Ausnahme) Verhalten bekomme, ist, wenn erste und zweite eine lock_version von 0 haben. Nach dem ersten Speichern ist es NIE wieder 0. Was in aller Welt ist damit los?

Ich bin mit Rubin 1.8.6 und aktiven Datensatz 2.2.2

Dank ...

Antwort

5

Beim ersten Aufruf.Speichern Sie beim zweiten Mal den Wert von some_attribute_field gleich "first", activerecord weiß dies, so dass es nicht in der db aktualisiert wird, um lock_version wird nicht inkrementiert. Die zweite Sicherung funktioniert, da die db niemals mit der "ersten" geändert wurde.

Versuchen Sie, den Wert im zweiten Test auf etwas anderes als "first" zu ändern, so dass es sich von dem in der db unterscheidet.

+0

Vielleicht möchten Sie 'schmutzig' und 'Änderungen' in der API nachschlagen (gotapi.com ist was ich verwende). Es tut nichts, weil es nichts gibt * zu * tun. – Sai

2

Ich bin kein Typ Rubin, aber optimistische Sperren sind für mich vertraut, so dass ich versuchen Sie, es zu debuggen.

Ich nehme an, der zweite Speicher aktualisiert tatsächlich die Datenbank. Wenn beide Objekte unterschiedliche lock_version haben und lock_version in UPDATE verwendet wird, ist dies einfach nicht möglich (UPDATE würde null Zeilen aktualisieren). So haben wir nur zwei Alternativen:

  • lock_version nicht in Update-Anweisung verwendet wird oder falsch
  • beiden Objekte bekamen die gleichen lock_version irgendwie

(tatsächlich gibt es eine dritte Alternative: beide save() sind in ihrer eigenen Transaktion, aber ich glaube, Sie haben AUTOCOMMIT = true)

Können Sie tatsächliche SQL-Anweisungen sichtbar machen? Die Update-Anweisung sollte wie

... WHERE JOB_ID=123 AND LOCK_VERSION=8 

lesen Wenn Sie die tatsächlich in der Hand fragt haben werden, wäre es viel einfacher sein, einen Sinn zu geben, was los ist.

P.S. und eine weitere: in Ihrem Beispiel in einem anderen Thema haben Sie dieses Objekt:

#<Job id: 323, lock: 8, worker_host: "second"> 

Der save() Aufruf kann je nach Container ignoriert werden, wenn die Eigenschaften vergleichen nicht geändert Zeit zu laden. Ich weiß nicht, ob ActiveRecord diese Optimierung hat oder nicht. Wenn dies jedoch der Fall ist, wird second save() ignoriert, und optimistisches Sperren hat keine Chance.

1

Wie Vladimir sagte, ist Ihr Test/Beispiel-Code ein bisschen fehlerhaft. Erst wird beim zweiten Speichern (!) Nicht in der Datenbank gespeichert, da sich keine Attribute geändert haben. Siehe das folgende Beispiel:

foo = Account.create! 

first = Account.find(foo.id) 
first.cash = 100 
first.save! 


first = Account.find(foo.id) 
first.cash = 100 

puts "First lock before " + first.lock_version.to_s 
first.save! 
puts "First lock after " + first.lock_version.to_s 

dies erzeugt:

% script/runner another_tester.rb        
First lock before 1 
First lock after 1 

Arbeiten mit dem Beispiel in der 2.3.2 Version von Schienen funktioniert wie es sollte mit einer Stale Objekt Ausnahme, wenn der zweite gespeichert (beiden Male!)

+0

Ich habe gerade den gleichen Test mit 2.2.2 versucht, und es funktioniert korrekt (veraltete Objekt Ausnahme beide Male) – reto

Verwandte Themen