Tuesday, June 19, 2012

Read-only transaction

As discussed earlier, for BE processing the pessimistic locking is adopted. The lock on mysql (select ... for update) is only released after transaction commits. This posts a challenge while there are multiple BE threads running and might easily get into dead lock situation.

To reduce the lock contention, I use the following approach: create new nested transaction to do the DB update, so only acquire/release lock in very short period time rather than holding it for the whole long transaction period (In my case, auditing). Note that the outer transaction need to be set to read only to avoid the flush completely, which is not just unnecessary but also failing because the object has older occVersion (Optimistic Concurrency Version). Although the outer and inner transactions have two different sessions, they are bound to the same thread, Hibernate will try to flush the objects in outer transaction right after the inner transaction commits.

// Or similarly, txTemplate.setReadOnly(true);
sessionFactory.getCurrentSession().setFlushMode(FlushMode.NEVER); 

// Simplify for testing, in the real world, we get the objects by a big query, and 
// running a bunch of things to determine whether the object need to be updated or not
final CoSTemplate ct = (CoSTemplate)templateDao.read(4);

TransactionTemplate txTemplate = new TransactionTemplate(transactionManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);

txTemplate.execute(new TransactionCallbackWithoutResult() {
    protected void doInTransactionWithoutResult(TransactionStatus status) { 
    // the object is reloaded from DB by getSession().refresh(tpl, LockMode.UPGRADE);
    CoSTemplate ct2 = (CoSTemplate)templateDao.lockRead(ct);
  
    History history = new History("something");
    ct2.addHistory(history);
    templateDao.update(ct2);
}
});