我们开发了一个以前在 MySQL 上顺利运行的 Grails 2.0 应用程序
我们的管理员一直要求我们切换到他们更喜欢的 PostgreSQL
我们为应用程序添加了一系列新功能,包括现在给我们带来问题的功能:异步第三方 Web 服务请求
因此,我们创建了一个域对象,我们称它为问题。使用 afterInsert
闭包,将创建一个 Resource
,以便稍后存储对外部网络服务的调用结果。
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
(通过 executors grails 插件获得,所以这不是可怕的“Hibernate Session - Thread”问题)
它执行一个简单的 Resource.get(res_id)
并获得如上所示的 Id
问题在于,对于 PostgreSQL 和 DataSource.groovy
中的 pooled = true
,get
有时返回 null,有时返回资源对象。
我们测试了 3 种不同的请求:get(id)
、findById(id)
和带有选择的 executeQuery
.
更奇怪的是,上面三行在同一个方法中,有时会得到不同的结果。三个中只有一个返回 null,或者三个返回 null,或者没有(我记得这是预期的行为)
我们打开了PostgreSQL的查询日志,看看是不是Hibernate缓存的问题,但是3个请求都出现在日志中,所以hibernate每次都命中数据库。我们看到插入了具有正确 ID 的资源,然后是提交,然后是三个选择(提供了正确的资源 ID)
有没有人提示我们将进一步测试什么以查看此错误的来源? (我们尝试更改连接池,但没有成功)
最后一件事,如果我们在请求之前添加一个 Thread.sleep(1000)
(这很血腥,但仅用于测试目的 ;-) ),所有运行都会顺利进行。所以,这似乎是 postgres 进程之间的可见性问题,但我们不知道如何解决这个问题
最佳答案
您花时间研究了这个问题并将其写成一个很好的问题,但遗漏了重要日志?
我推测您的保存和异步获取之间存在重叠事务。要么保存未提交,要么 get 在保存发生之前看到一致的快照。确保您在日志记录中启用了 process-id,或通过其他方式来区分连接并查看语句发生的顺序。
编辑:看起来像交易快照时间。
在 PostgreSQL 中,所有语句都在一个事务中(可能是隐式的并且只持续一个语句)。
默认模式是“Read Committed”,这意味着您可以看到事务期间发生的提交。
还有“可序列化”级别的选项,这意味着您(大部分)在事务开始时看到数据库的卡住快照。
有关更多信息,请参阅 the docs .
打开两个 psql 控制台并尝试一些变体,在一个中提交并在另一个中选择不同的隔离级别。您应该能够看到您的实时系统发生了什么。
关于hibernate - chalice : Select returns (randomly) null while row exists in PostgreSQL with connection pooling turned on,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11904717/