Perché il blocco ottimistico di activerecord funziona una volta per riga?
-
03-07-2019 - |
Domanda
In qualche modo, ricevo sempre questi di venerdì.
La mia domanda precedente riguardava lo stesso problema, ma ora posso restringere un po 'le cose:
Ci ho giocato tutto il giorno, cercando di dargli un senso. Ho una tabella con una colonna lock_version, specificata così:
add_column :jobs, :lock_version, :integer, :default=>0
E faccio qualcosa del genere:
foo = job.create!
first = Job.find(foo.id)
second = Job.find(foo.id)
Verifico quindi che il primo e il secondo si riferiscano allo stesso oggetto: i loro ID sono gli stessi e vedo quella riga nel database usando lo strumento da riga di comando mysql.
first.some_attribute_field = 'first'
second.some_attribute_field = 'second'
first.save
second.save
nessun problema finora. Ottengo correttamente un'eccezione ActiveRecord :: StaleObjectError. TUTTAVIA :
first = Job.find(foo.id)
second = Job.find(foo.id)
first.some_attribute_field = 'first'
second.some_attribute_field = 'second'
first.save
second.save
... e non succede nulla. Si scopre che l'unica volta in cui ottengo un comportamento corretto (eccezione generata) è quando il primo e il secondo hanno una lock_version di 0. Dopo il primo salvataggio, tuttavia, non è MAI di nuovo 0. Cosa diavolo succede con questo?
Sto usando ruby ??1.8.6 e il record attivo 2.2.2
Grazie ...
Soluzione
quando chiami first.save la seconda volta il valore di some_attribute_field è già uguale a " first " ;, activerecord lo sa quindi non si aggiorna nel db a lock_version non viene incrementato. Il secondo salvataggio funziona poiché il db non è mai stato modificato con il "primo".
Prova a cambiare il valore nel secondo test in qualcosa di diverso da "primo" in modo che sia diverso da ciò che è nel db.
Altri suggerimenti
Non sono un tipo Ruby, ma il blocco ottimistico mi è familiare, quindi cercherò di aiutarti a eseguire il debug.
Presumo che il secondo salvataggio aggiorni effettivamente il database. Se entrambi gli oggetti hanno lock_version diversi E lock_version viene utilizzato in UPDATE, semplicemente non è possibile (UPDATE aggiorna zero righe). Quindi, abbiamo solo due alternative:
- lock_version non viene utilizzato nell'istruzione UPDATE o utilizzato in modo errato
- entrambi gli oggetti hanno ottenuto la stessa lock_version in qualche modo
( in realtà, esiste una terza alternativa: entrambi save () sono nella propria transazione, ma ritengo che tu abbia AUTOCOMMIT = true )
Puoi rendere visibili le effettive istruzioni SQL? La dichiarazione di aggiornamento dovrebbe essere simile a
... WHERE JOB_ID=123 AND LOCK_VERSION=8
Quando avrai a portata di mano le domande, sarebbe molto più semplice capire cosa sta succedendo.
P.S. e ancora uno: nel tuo esempio in un altro argomento hai questo oggetto:
#<Job id: 323, lock: 8, worker_host: "second">
La chiamata save () può essere ignorata dal contenitore se le proprietà non vengono modificate rispetto al tempo di caricamento. Non so se ActiveRecord abbia questa ottimizzazione o no. Ma se lo fa, il secondo save () viene ignorato e il blocco ottimistico non ha la possibilità di entrare.
Come ha detto Vladimir, il tuo codice di test / esempio è un po 'difettoso. Il primo non viene memorizzato nella base di dati durante il secondo salvataggio! () Perché nessun attributo è stato modificato. Vedi il seguente esempio:
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
questo produce:
% script/runner another_tester.rb
First lock before 1
First lock after 1
L'uso del tuo esempio nella versione 2.3.2 di rotaie funziona come dovrebbe con un'eccezione di oggetto obsoleto quando il secondo viene salvato (entrambe le volte!)