【发布时间】:2021-08-04 12:40:10
【问题描述】:
我们将 Grails 4 与 Hibernate 5 和 Postgresql 一起使用,我遇到了一个奇怪的问题,我不知道如何解决。
我创建了一个测试,它创建了一个 Family(一个包含大约 20 个 INSERT 和 UPDATE 查询的大型对象图),然后它尝试使用它的 id 来检索它。
所以有一个FamilyController扩展grails.rest.RestfulController有static responseFormats = ['json']和以下方法:
@Override
protected Family createResource() {
def instance = new NewFamilyCommand()
bindData(instance, getObjectToBind())
instance.validate()
//Check for binding errors
if(instance.hasErrors()) {
throw new ValidationException('Unable to bind new family', instance.errors)
}
//Send the command to the service, which will return us an actual Family domain object
return familyService.addFamilyToUser(instance)
}
和
@Override
protected Family queryForResource(Serializable id) {
def family = familyService.safeGetFamily(Long.parseLong(id))
...
return family
}
使用 Cypress 测试在循环中运行它,并且大部分时间都可以正常工作。
问题是通过返回的id查询Family时不时(好像是50的倍数,巧合的是Tomcat中配置的maxConnections的个数)找不到。
这是数据源的 Tomcat 配置(Grails 推荐的默认配置):
dataSource:
pooled: true
jmxExport: true
driverClassName: org.postgresql.Driver
dialect: "net.kaleidos.hibernate.PostgresqlExtensionsDialect"
username: "test"
password: "test"
properties:
initialSize: 2
maxActive: 50
minIdle: 2
maxIdle: 2
maxWait: 10000
maxAge: 10 * 60000
timeBetweenEvictionRunsMillis: 5000
minEvictableIdleTimeMillis: 60000
validationQuery: "SELECT 1"
validationQueryTimeout: 3
validationInterval: 15000
testOnBorrow: true
testWhileIdle: true
testOnReturn: false
jdbcInterceptors: "ConnectionState;StatementCache(max=200)"
defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
removeAbandoned: true
removeAbandonedTimeout: 300
Postgresql 在 docker 容器中运行,此数据库有 2 个 PIDS,比如 73 和 74。
我查看了 Postgresql 日志,发现成功测试和失败测试之间存在差异。
在成功的场景中,家庭创建和家庭检索都在同一个 PID 中运行。
在失败的情况下,家庭创建由 pid 74 执行,家庭检索由 PID 73 执行。
更奇怪的是 PID 73 大部分时间都是空闲的,它围绕 Family 50 的创建运行它的第一个查询(大概是在开始重用连接时),然后在 Family 101 开始使用家族检索查询,其事务开始于在 PID 74 家族创建事务提交之前(至少在 Postgres 日志中显示,但可能日志没有按时间顺序打印)。
在测试失败后立即检查数据库,我看到家庭保存在数据库中,如果我在查询结果之前添加一点等待时间,测试也会通过。
我想知道这怎么可能,我假设 Postgresl 仅在事务提交后才返回 ID,那么为什么其他 PID 看不到呢?
在解决此问题方面的任何帮助将不胜感激。
更新:似乎与 Hibernate.FLUSH_MODE 有关。如果我在familyService.addFamilyToUser(instance) 中设置sessionFactory.currentSession.setFlushMode(FlushMode.MANUAL),问题就会消失,并且语句会正确排序。即使 Postgres 没有提交所有插入/更新语句,Hibernate 似乎过早地刷新会话并返回家庭 ID。
【问题讨论】:
-
"循环运行,大部分时间运行良好。" - 这是否意味着您有一些外部进程重复向应用发送请求,一个要创建,然后一个要检索(2 个单独的 HTTP 请求)?
-
是的,这是 Cypress 测试
-
控制器操作的返回类型为
Family有点不寻常。您能否展示您的Family create()和Family get(Long id)操作中的内容? -
@JeffScottBrown 用我们控制器的代码更新了问题
-
“使用我们控制器中的代码更新了问题” - 看起来您删除了对将
Family声明为返回类型的控制器操作的引用。您是在使用从RestfulController继承的get和create,还是您自己的实现具有原始问题中表达的返回类型?
标签: postgresql hibernate grails