2
votes

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 :)

1
where are you obtaining the lock?Adisesha

1 Answers

1
votes

I will try to give some clues,

You say that table is locked.I didn't see where you are doing it.Hibernate does not acquire any lock by default. You need to explicitly tell it do so, see. Even here, you are not obtaining lock on table but on single row. Your assumptions can hold true only if you are using pessimistic locking and both transactions require lock on same row.

If you are not using pessimistic locking, you should have got optimistic lock exception when T1 tries to commit. So it is possible that your scenario leads to same object not updated by both transactions.

It is difficult to answer your question without debugging, and knowing what's going on in 'create' and 'update'.