【发布时间】:2021-12-06 16:29:26
【问题描述】:
在集成测试中测试并发性时,我在事务和休眠会话方面遇到了一些问题。
基本上我们有一个更新一些不同的数据库条目的过程,包括一些会计条目。对于给定的 id,此过程应该是幂等的,包括从不同线程并发调用时。
我正在尝试更新一个有一段时间无法在 Grails 4.0.12 中运行的旧测试。该测试最后一次在 Grails 2.x 中运行。
为简单起见,下面的代码是通用的。
void testName() {
given:
Thing thing = Thing.findAll(someId)[0]
int attemptsPerThing = 5
when:
def pool = Executors.newFixedThreadPool(attemptsPerThing)
List<Future> futureList = []
for (int i = 0; i < attemptsPerThing; i++) {
Future future = pool.submit({
try {
serviceToBeTested.functionToBeTestedThatCommitsThingsToDB(thing)
} catch (Exception _) {}
})
futureList.add(future)
}
for (Future future in futureList) {
def futureResults = future.get()
assert futureResults == null
}
pool.shutdown()
assert pool.awaitTermination(60, TimeUnit.SECONDS)
sessionFactory.currentSession.clear()
then:
// QUERY DATABASE AND ASSERT THAT THE CALL ONLY COMPLETED ONCE AND DATA IS AS EXPECTED
}
此测试失败。在最初的调查中,它看起来是失败的,因为在未来的闭包中没有任何东西以在测试中可见的方式提交。看起来好像该函数从未被调用过。
在询问该 try 块中捕获的异常时,我可以看到所有迭代实际上都失败了,并出现了与业务逻辑无关的异常。例外是:
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions. Collection : [com.kiind.coreservices.KiindUser.paymentInfoByCurrency#70]
上面的com.kiind.coreservices.KiindUser.paymentInfoByCurrency是一个域对象的集合,它们存在于数据库中,但只能在这个函数中读取。
所以我的问题是,是否有在 Grails 4 / Spock 中测试并发的最佳实践?在集成测试中测试并发性是不好的做法吗?
具体来说,有没有办法将测试的休眠会话附加到线程,以便它们都可以共享?或者让线程使用它们自己的会话,完成它们的事务,然后在then 期间使用数据库的当前状态更新测试中的会话?
感谢您的帮助,如果需要其他信息,请告诉我。
【问题讨论】:
标签: hibernate grails concurrency integration-testing grails-orm