I am running into some trouble with transactions & hibernate sessions while testing concurrency in an integration test.
Essentially we have a process that updates a few different database entries including some accounting entries. This process should be idempotent for a given id, including when being called concurrently from different threads.
I am trying to update an old test that hasn't worked for a while to work in Grails 4.0.12. This test last worked in Grails 2.x.
The code below is generalized for simplicity.
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
}
This test fails. Upon initial investigation it looks to be failing because nothing within the future closures gets committed in a way that is visible within the test. It looks as if the function was never called.
Upon interrogating the exception caught in that try block I can see that all the iterations are actually failing with an exception unrelated to the business logic. The exception is:
org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions. Collection : [com.kiind.coreservices.KiindUser.paymentInfoByCurrency#70]
The above com.kiind.coreservices.KiindUser.paymentInfoByCurrency is a collection of domain objects that live in the database but are only read in this function.
So my question is, is there a best practice for testing concurrency in Grails 4 / Spock? Is it bad practice to test concurrency in an integration test?
Specifically, is there a way to attach the test's hibernate session to the threads so that they can all share it? Or get the threads to work with their own session, complete their transactions, and then update the session in the test with the current state of the database during the then?
Thanks for any assistance and let me know if additional information is required.