在集成測驗中測驗并發性時,我在事務和休眠會話方面遇到了一些麻煩。
本質上,我們有一個更新幾個不同資料庫條目的程序,包括一些會計條目。這個程序對于給定的 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?
感謝您的任何幫助,如果需要其他資訊,請告訴我。
uj5u.com熱心網友回復:
我相信通過池中的另一個執行緒查看一個執行緒的原子事務的唯一方法是關閉事務管理。你可以在 spock 集成測驗中做到這一點,你當然也可以在真正的服務中做到這一點。但不建議這樣做,交易是為了避免這種情況。
換句話說,@Guillaume 上面所說的。
uj5u.com熱心網友回復:
您正在從 Hibernate 會話在主執行緒中加載一個物件:
Thing thing = Thing.findAll(someId)[0]
然后你在另一個執行緒中使用同一個物件:
serviceToBeTested.functionToBeTestedThatCommitsThingsToDB(thing)
Hibernate 會話和與該會話關聯的物件完全不是執行緒安全的,因此您不能這樣做。它讓每個執行緒管理自己的會話并且永遠不會在執行緒之間傳遞物件的安全方式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/337481.html
標籤:休眠 圣杯 并发 集成测试 grails-orm
