views:

32

answers:

1

Hello,

I use nhibernate as my orm and i am trying to implement something that looks straightforward but has become unneccessarily difficult. It concerns how to implement concurrent updates to an entity where it is expected that the entity will be updated simultaneously by more than one user in a multiuser scenario. The entity is an account of some kind that has a balance.

An example is an inventory system that requires updating a balance of the inventory item based on movement entries in or out. I've included below an shortened version of the code below. The code simply saves the entry and relies on nhibernate to cascade saves all the way to the items.

The system is expected to handle multiple users. The objects are created in the UI over a period of time. Each item's balance will be adjusted from the quantity on hand at the time the entry was created in the UI and the item fetched. It may be several minutes before the user finally saves.

If another user has made other enteries on the same items, the balances that the first user is working with are now invalid. I know that if a save is attempted NHibernate will throw a stale data exception. But this is not what I want since concurrent updates is a neccessity. What I want is that the current balance of the item be incremented or decremented as required by the quantity of the entry. Or that the item be fetched again at the time of update, locked, a recalculation performed and then finally updated, but I don't know how this can be achieved in this scenario and where exactly the code would reside. I would want to avoid it being in the domain object since controlling pessimistic locking etc is an application/persistence concern. It seems this should be a common scenario but I've not seen anything discussing this.

P.S. I have another concern about how the refetch-lock-recalculate-update approach I outlined above in a situation where an entry have many lines. Will seperate lock requests be sent for each item or is there a way to acquire locks for all the items of entry lines in one go.

Any help would be appreciated.

class TestConcurrentUpdates
{ 
   ISession session; 
   Entry entry; 
   ItemRepository itemRep; 
   EntryRepository entryRep; 

   void testMethod()
   {
       ...

       Entry entry = new Entry();

       Item item1 = itemRepository.Get(item1ID); 
       Item item2 = itemRepository.Get(item1ID); 
       Item item3 = itemRepository.Get(item1ID); 

       entry.Lines.Add(new EntryLine(this, item1, 10)); 
       entry.Lines.Add(new EntryLine(this, item2, -4)); 
       entry.Lines.Add(new EntryLine(this, item3, 2)); 
       using (ITransaction transaction = session.BeginTransaction()) 
       { 
           entryRep.Save(entry); 
           transaction.Commit(); 
       } 
   }
} 


Item 
{ 
   string name; 
   double balance; 
   IList<EntryLine> entries = new List<EntryLine>(); 

   public double getBalance() { return balance; } 


   void addEntryLine(EntryLine line) 
   { 
       entries.add(line); 
       balance += line.getQuantity(); 
   } 
} 


class Entry 
{ 
   IList<EntryLine> lines = new List<EntryLine>(); 
   public IList<EntryLine> Lines { get { return lines; } } 
} 

class EntryLine 
{ 
   Entry entry; 
   double quantity; 
   Item item; 
   public EntryLine(Entry entry, Item item, double quantity) 
   { 
       this.entry = entry; 
       this.quantity = quantity; 
       this.item = item; 
       item.addEntryLine(this); 
   } 

   public double getQuantity() 
   { 
       return quantity; 
   } 
}
A: 

Ayende has an article about applying locking to aggregate roots that should help you with this.

DanP
Thanks for the response. I had read this article before but when you referred to it, I went back to read it again. I don't think it addresses the scenario described. I'll add a bit more detail in a follow up post so you can decide if i'm correct.
Jideo
The scenario solution described by ayende uses a "trick" to effectively lock an aggregate root by incrementing its version number so when another user edits and tries to save the same aggregate root/children, a stale object execption will be thrown.The situation I have in mind is where you actually expect that there will be concurrent updates on a object but don't want any exception raised for concurrent updates. That's why I gave the example of an account balance which can be updated simultaneously by multiple users. Ideally this would be done by acquiring a pessimistic lock.
Jideo
What i'm finding difficult is dealing with the objects getting stale during the user interaction time:t1 - User1 creates a new transaction object.t2 - User1 selects the account object and it is fetched. t3 - The transaction is added to account, account.addtransaction(trans), and the account balance incremented or decremented by the transaction amount based on the balance fetched at time t2. t4 - User2 creates a new transaction object t5 - User2 selects the same account object and it is fetched, the account is identical, same version with t2.
Jideo
t6 - Transaction is added to second account object and the account balance is decremented or incremented by transaction amount.t7 - User1 saves transaction and account.t8 - User2 saves transaction and account. A stale object exception is thrown because account version has changed because of save at t7What I would have wanted as the outcome at t8 is that account.balance = account.balance +- trans1.amount +- trans2.amount
Jideo