Environment:
We have an application that is deployed in a JBoss 4.2.3.GA server and which is using Hibernate 3.4 and JTA 1.0.
There's an importer which creates or updates certain entities and then imports some data. Most of that import is done in a new transaction due to several reasons and within each transaction the entities created/updated in the outer transaction might be updated again.
The call sequence looks like this pseudo code:
Service1:
//container managed transaction T1 is started here
import() {
A a = ... ;//new or read from database
if( isNew( a ) ) {
create(a);
} else {
update(a);
}
Service1 s = ...; //injected or looked up
for( D d : someDataList ) {
//nested transaction T2 is started due to this call, T1 should be suspended
s1.importData(d);
//nested transaction T2 should have been committed here
}
Service2:
@TransactionAttribute(REQUIRES_NEW)
importData(D d) {
A a = ...; //get the corresponding A instance and update as needed
update(a);
//other stuff such as importing d
}
Problem:
The problem now is that we'd eventually run into a racing condition with several transactions trying to lock the same tables but we so far have neither been able to reproduce the problem nor to identify the real cause.
We have some assumptions, though:
Since during T1 some entities are updated, the transation acquires some database locks. T1 is then suspended because T2 is started, which in turn tries to acquire the same database locks and is thus blocked. T2 eventually times out and T1 then can finish normally and release the locks.
Possible solution?:
So far, there only seems to be one possible solution: wrap all updates in T1 into another transaction T1* (possibly skipping T1 altogether) and have T1* and T2 run sequentially.
Would this be a sane solution, provided the business case allows for this (which I'm not sure of, since I didn't implement that business case myself)?
There might be other solutions as well, and if so, please provide some hints. However, I doubt that because it seems like T1 would have to release the lock before T2 tries to acquire it, thus basically making T1 and T2 run sequentially.
Questions
From the above, the following questions emerge:
- Is our assumption correct, i.e. does T1 hold locks that T2 would need as well and which it can't release because it is suspended?
- Is the solution I described above the only approach or are there other ways without manual transaction demarcation?
Thanks for reading all this :)
Update 1:
Since I'm not the writer of the code I have to dig into it as well. So far there's no hint on any explicit locking in Hibernate and thus AFAIK Hibernate only uses database locks during writes to the database resp. when the database connection is opened.
We're using automatic flushes and thus T1 might open a connection before T2 tries the same in some cases, but T1 can't commit and close the connection since it is suspended until after T2 has committed. Hence the locks that the database seems to aquire due to the flush of T1 can't be released before T2 flushes as well.
Using manual flushes wouldn't be a solution, since we'd then have lost updates if T2 commits before T1 but the changes to the entities are the other way round. I know it's a flaw in the design and we need to fix that, but I'd also like to confirm our assumptions are correct in order to provide a sensible fix :)