Thursday 19 July 2012

Updating Orders in Commerce Code

When you are using the Order object with synchronization and transactions, there is a specific usage pattern that is critical to follow. Not following the expected pattern can lead to unnecessary ConcurrentUpdateExceptions, InvalidVersionExceptions and deadlocks. The following sequence must be strictly adhered to:

1. Obtain local lock on profile id
2. Begin transaction
3.Synchronize on order
4.Perform all modifications to the order object
5.Call OrderManager.updateOrder
6.End Synchronization
7.End Transaction
8.Release local lock on profile id

Steps 1,2,7 and 8 are done for you in beforeSet and afterSet methods for ATG form handlers where order updates are expected. These include formhandlers that extend PurchaseProcessFormHandler.

If you write a custom code updating an order outside of PurchaseProcessFormHandler (Eg: coupon. droplet, pipeline servlet) etc you code should acquire the lock before updating the order.

The following code snippet is an example to load cookied order and depicts the pattern that is useful to prevent ConcurrentUpdateExceptions, InvalidVersionExceptions and dead locks when multiple threads attempt to update the same order on the same ATG instance.



public void service(DynamoHttpServletRequest request,
DynamoHttpServletResponse response) throws IOException,
ServletException {

ClientLockManager lockManager = getLocalLockManager(); // Should be configured as /atg/commerce/order/LocalLockManager
Profile profile = (Profile)request.resolveName("/atg/userprofiling/Profile");
boolean acquireLock = false;
try
{
 acquireLock = !lockManager.hasWriteLock( profile.getRepositoryId(), Thread.currentThread() );
 if ( acquireLock )
 lockManager.acquireWriteLock( profile.getRepositoryId(), Thread.currentThread() );
 TransactionDemarcation td = new TransactionDemarcation();
 td.begin( getTransactionManager(),TransactionDemarcation.REQUIRES_NEW );
 boolean shouldRollback = false;
 try
 {
 String cookieOrderId =getCookieManager().getCookie("orderIdCookie",request);
Order order = (Order)getOrderManager().loadOrder(cookieOrderId);
OrderHolder orderHolder = (OrderHolder)request.resolveName("/atg/commerce/ShoppingCart");
synchronized (order) {
orderHolder.setCurrent(order);
order.setProfileId(profile.getRepositoryId());
CommerceProfileTools profileTools = getProfileTools();
if(order.getCommerceItemCount() > 0){
profileTools.repriceShoppingCarts(profile,orderHolder,profileTools.getRepriceOrderPricingOp(),request,response);
}
getOrderManager().updateOrder(order);
}
 }
 catch (CommerceException commException) {
shouldRollback = true;
if(isLoggingError()){
logError("CommerceException which loading order", commException);
}
}
 finally
 {
   try
   {
     td.end( shouldRollback );
   }
   catch ( Throwable th )
   {
     logError( th );
   }
 }
} catch (DeadlockException deadLockExc) {
if(isLoggingError()){
logError("DeadLockException", deadLockExc);
}
} catch (TransactionDemarcationException transDemExc) {
if(isLoggingError()){
logError("TransactionDemarcationException", transDemExc);
}
}
finally
{
 try
 {
   if ( acquireLock )
     lockManager.releaseWriteLock( profile.getRepositoryId(), Thread.currentThread(), true );
 }
 catch( Throwable th )
 {
   logError( th );
 }
}
}

1 comment:

  1. error in getCookieManager() method:-
    how to solve it.Please tellme.

    ReplyDelete