Multithreading “圣杯”;行被另一个事务更新或删除(或未保存的值映射不正确)";
Grails(2.3.7)中似乎没有明确的并发问题解决方案。我尝试了所有的建议,但当我增加并发线程的数量时,以下代码总是失败:Multithreading “圣杯”;行被另一个事务更新或删除(或未保存的值映射不正确)";,multithreading,grails,gorm,database-concurrency,Multithreading,Grails,Gorm,Database Concurrency,Grails(2.3.7)中似乎没有明确的并发问题解决方案。我尝试了所有的建议,但当我增加并发线程的数量时,以下代码总是失败: package simpledb import grails.transaction.Transactional import groovy.transform.Synchronized import org.apache.commons.logging.LogFactory @Transactional class OwnerService { priva
package simpledb
import grails.transaction.Transactional
import groovy.transform.Synchronized
import org.apache.commons.logging.LogFactory
@Transactional
class OwnerService {
private static final myLock1 = new Object()
private static final myLock2 = new Object()
@Synchronized('myLock1')
static public saveOwner(def ownerName) {
def ownerInstance = null
Owner.withNewTransaction {
ownerInstance = Owner.findOrCreateByName(ownerName)
ownerInstance.save(failOnError: true, flush: true)
}
ownerInstance
}
@Synchronized('myLock2')
static public associateDog(def ownerId, def dogId) {
def lockedOwnerInstance
Owner.withNewTransaction {
lockedOwnerInstance = Owner.lock(ownerId)
def lockedDogInstance = Dog.lock(dogId)
lockedOwnerInstance.addToDogs(lockedDogInstance)
lockedOwnerInstance.save(failOnError: true, flush: true)
}
lockedOwnerInstance
}
}
它在“def lockedDogInstance=Dog.lock(dogId)”行失败:
上述设计非常简单,因为主人和狗之间存在多对多关系:
狗类:
package simpledb
class Dog {
String name
Breed breed = null
Integer age = null
static hasMany = [owners: Owner]
static belongsTo = Owner
static mapping = { owners lazy: false }
static constraints = {
name blank: false, nullable: false, unique: true
breed nullable: true
age nullable: true
}
}
所有者类别:
package simpledb
class Owner {
String name;
static hasMany = [dogs: Dog]
static mapping = { dogs lazy: false }
static constraints = {
}
}
仅供参考-数据库是MySQL
有什么建议吗?好的,这里有很多建议,我打赌大部分都可以处理掉。因此,与其试图修复它,不如让我们将其降至最低限度,然后从那里开始:
withNewTransaction()
可以运行。你也不需要冲洗Dog
,因为您没有更改它(将它添加到Owner.dogs
只会在联接表中创建一条记录)package simpledb
import grails.transaction.Transactional
import org.apache.commons.logging.LogFactory
@Transactional
class OwnerService {
def saveOwner(def ownerName) {
def ownerInstance = Owner.findOrCreateByName(ownerName)
ownerInstance.save(failOnError: true)
ownerInstance
}
def associateDog(def ownerId, def dogId) {
def ownerInstance = Owner.lock(ownerId)
def dogInstance = Dog.read(dogId)
ownerInstance.addToDogs(dogInstance)
ownerInstance.save(failOnError: true)
ownerInstance
}
}
看看你能走多远。您甚至可以删除所有者锁。除了@Emmanuel Rosa所说的之外,如果有太多的并发更新发生,您是否可以确保在保存之前调用“刷新”(关于所有者)?(方法) 但是,联接表的添加不应该受到这些问题的影响。只有当一些狗试图被“重新添加”到相同的主人身上时,它才可能导致问题
另一种方法(不是在本例中,而是在本例中)如果只更新一列或两列,则可以使用纯SQL。感谢@Emmanuel Rosa的回复。我做了您推荐的更改(这是我开始做的)。但是,我继续收到与前面列出的相同的错误消息。与我上面列出的代码中偶尔出现的线程相比,这次多个线程在此错误上失败。不知何故,当涉及到并发数据库修改时,Grails无法按文档所述工作。您的应用程序的哪些方面使多个线程能够尝试更新同一个域模型?应用程序本质上是接受带有JSON负载的REST请求,并将其持久化到数据库中。但是,在创建记录之前,我们需要检查数据是否已经存在,在这种情况下,需要更新数据。我正在使用JMeter测试代码,因为它允许模拟多线程调用。但是,如果您发出REST POST来创建记录,并且由于记录已经存在而失败,那么它应该以失败作为响应,以便客户机可以返回并发出PUT来更新它。如果由于记录更改导致PUT失败,则客户端应重新尝试PUT;最多n次,然后最终放弃。我应该进一步阐述。不同的客户端同时发出多个请求。如果json负载包含数据库中不存在的某个键,则需要创建一行,并使用对该行的引用来填充其他表。如果键已经存在,则检索引用并填充其他表。希望有帮助。
package simpledb
import grails.transaction.Transactional
import org.apache.commons.logging.LogFactory
@Transactional
class OwnerService {
def saveOwner(def ownerName) {
def ownerInstance = Owner.findOrCreateByName(ownerName)
ownerInstance.save(failOnError: true)
ownerInstance
}
def associateDog(def ownerId, def dogId) {
def ownerInstance = Owner.lock(ownerId)
def dogInstance = Dog.read(dogId)
ownerInstance.addToDogs(dogInstance)
ownerInstance.save(failOnError: true)
ownerInstance
}
}