Concurrencia al usar GORM en Grails
-
03-07-2019 - |
Pregunta
Supongamos que tengo una función de contador que actualiza un contador utilizando SQL sin formato:
public void updateCounter() {
executeSql("UPDATE counter SET count_value = count_value + 1 WHERE id = 1;");
}
La base de datos se aseguraría de que dos llamadas simultáneas al contador se manejen como se esperaba, que todas las llamadas actualizarían el contador con un incremento y que no se perderían actualizaciones.
En lugar de ejecutar esto emitiendo un comando de SQL sin procesar, me gustaría usar GORM. La mejor manera de hacerlo sería algo como:
public void updateCounter() {
Counter c = Counter.get(1)
c.countValue += 1
c.save()
}
En este caso, asumo que una actualización podría perderse si dos subprocesos llaman al método updateCounter () en el mismo momento. ¿Cuál es la correcta " Grails / GORM-way " para manejar este problema de concurrencia?
Solución
Puede utilizar estrategias de bloqueo "pesimistas" u "optimistas", ambas compatibles con Hibernate y, por lo tanto, con GORM. La estrategia GORM predeterminada es "optimista" (que utiliza la columna / propiedad de la versión de la entidad de dominio persistente, creada de forma predeterminada). Podría ser usado así:
...
try {
Counter c = Counter.get(1)
c.countValue += 1
c.save(flush:true)
}
catch(org.springframework.dao.OptimisticLockingFailureException e) {
// deal with concurrent modification here
}
...
Si prefieres la estrategia de bloqueo 'pesimista' (lo que bloqueará todas las demás lecturas concurrentes, por cierto), puedes hacerlo utilizando el metamétodo GORM de 'bloqueo' explícito, como por ejemplo:
...
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
...
Espero que esto ayude.