我们开发了一个Grails 2.0应用程序,它以前在
MySQL上顺利运行
我们一直要求管理员切换到他们喜欢的PostgreSQL
我们为应用程序添加了一些新功能,包括现在导致我们问题的功能:异步第三方Web服务请求
所以,我们创建了一个域对象,让我们称之为问题.使用afterInsert闭包,将创建一个Resource,以便稍后存储对外部Web服务的调用结果.
class Question implements Serializable {
static hasMany = [resources: Resource]
static constraints = {
resources(nullable: true)
}
def afterInsert() {
Resource.withNewSession {
Resource txt = Resource.create(null)
this.addToResources(txt)
}
}
Resource retrieveResource(){
return this.resources.find{ it instanceof Resource }
}
static Question create(Map params) throws SaveDomainException {
//question creation
}
}
我们创建这样的问题:
//first we create question and save it
def question = Question.create(params)
question.save(flush:true, insert:true)
getThirdPartyService().doCallAsync((int)req.retrieveResource().id)
和ThirdPartyService作为方法doCallAsync产生ExecutorService(通过执行程序grails插件获得,这样这不是可怕的“Hibernate Session – Thread”问题)
它执行一个简单的Resource.get(res_id),其ID如上所示获得
问题是,对于PostgreSQL和DataSource.groovy中的pooled = true,get返回有时为null,而不是资源对象.
我们已经测试了3个不同的请求:get(id),findById(id)和带select的executeQuery.
更奇怪的是,在同一方法中上面的三条线,我们有时会得到不同的结果.三个中只有一个返回null,或者三个返回null,或者没有(这是预期的行为,我记得)
我们打开PostgreSQL查询日志来查看它是否是一个Hibernate缓存问题,但是3个请求都出现在日志中,因此每次数据库都会出现hibernate命中.我们看到资源的插入具有正确的id,然后是提交,然后是三个选择(提供正确的资源ID)
有没有人对我们要进一步测试的内容有所暗示,看看这个bug来自哪里? (我们试图改变连接池,没有运气)
最后一件事,如果我们在请求之前添加一个Thread.sleep(1000)(这是血腥但仅用于测试目的;-)),所有操作都会顺利进行.因此,似乎是postgres流程之间可见性的问题,但我们并不知道如何解决这个问题
最佳答案 你花时间研究这个问题并把它写成一个很好的问题,但遗漏了重要的日志?
我推测你的save和async get之间有重叠的事务.在保存发生之前,保存未提交或get正在查看一致的快照.确保在日志记录中启用了进程标识,或者以其他方式区分连接,并查看语句的顺序.
编辑:看起来像事务快照计时.
在PostgreSQL中,所有语句都在一个事务中(可能是隐式的,只持续一个语句).
默认模式为“Read Committed”,这意味着您可以查看事务期间发生的提交.
还有一个“可序列化”级别的选项,这意味着您(大多数)在交易开始时看到数据库的冻结快照.
欲了解更多,请参阅the docs.
打开两个psql控制台,尝试在一个中提交一些变体,在另一个中选择不同的隔离级别.您应该能够看到您的实时系统发生了什么.