Concorrenza quando si utilizza GORM in Grails
-
03-07-2019 - |
Domanda
Diciamo che ho una funzione contatore che aggiorna un contatore usando SQL grezzo:
public void updateCounter() {
executeSql("UPDATE counter SET count_value = count_value + 1 WHERE id = 1;");
}
Il database si assicurerebbe che due chiamate simultanee al contatore vengano gestite come previsto - che tutte le chiamate aggiornerebbero il contatore con un incremento e nessun aggiornamento andrebbe perso.
Piuttosto che eseguirlo eseguendo un comando SQL non elaborato, mi piacerebbe utilizzare GORM. Il modo ingenuo di farlo sarebbe qualcosa sulla falsariga di:
public void updateCounter() {
Counter c = Counter.get(1)
c.countValue += 1
c.save()
}
In questo caso suppongo che un aggiornamento potrebbe andare perso se due thread chiamano il metodo updateCounter () nello stesso momento. Qual è la corretta "quotazione Grails / GORM" gestire questo problema di concorrenza?
Soluzione
È possibile utilizzare strategie di blocco "pessimistiche" o "ottimistiche", entrambe supportate da Hibernate e quindi da GORM. La strategia GORM predefinita è "ottimista" (che utilizza la colonna / proprietà della versione dell'entità di dominio persistente, creata per impostazione predefinita). Potrebbe essere usato in questo modo:
...
try {
Counter c = Counter.get(1)
c.countValue += 1
c.save(flush:true)
}
catch(org.springframework.dao.OptimisticLockingFailureException e) {
// deal with concurrent modification here
}
...
Se invece preferisci la strategia di blocco 'pessimista' (che bloccherà tutte le altre letture simultanee, tra l'altro), potresti farlo usando il meta-metodo GORM esplicito 'lock', in questo modo:
...
Counter c = Counter.lock(1) //lock the entire row for update
c.countValue += 1
c.save(flush:true) //GORM will autorelease the lock once the TX is committed
...
Spero che questo aiuti.