<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">Hi Vladimir<div><br></div><div>I haven't spent too much time thinking about eager locking yet - swamped with the finishing touches on 4.0.0.ALPHA2 and getting a public splash ready. &nbsp;:-)&nbsp;</div><div><br></div><div>Also, infinispan-dev (now cc'd!) is pretty informal so don't worry about getting a perfect, distilled design. &nbsp;The purpose of the list is to distil the design.</div><div><br></div><div>I've included my initial thoughts inline, below.</div><div><br><div><div>On 28 Apr 2009, at 03:18, Vladimir Blagojevic wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0; "><div bgcolor="#ffffff" text="#000000"><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">Manik,<br></font></font></p><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">Have you thought about if we need non-eager locking? I have put sketches of requirements for locking. Maybe you can put final touches and we can post it to infinispan-dev.<br></font></font></p><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">Regards,<br>Vladimir<br><br></font></font></p><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">Introduction</font></font></p><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">The difference between eager and non-eager locking is related to timing and method of lock acquisition across the cluster. Eager locking is used by a user who wants to lock set of keys prior to invoking a batch of operations on those keys. If locks are successfully obtained on all keys across all cluster nodes user is sure that he/she will not get lock acquisition exception during those batch operations.</font></font></p><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">As far as implementation goes eager locking is executed by synchronously replicating lock command across the network. Depending on a cache mode, lock acquisitions are attempted on all specified keys on all cluster nodes or on a subset of cluster nodes - in case of DIST cache set-up. Lock operation either succeeds or fails. Eager locking can be used with or without transactions.</font></font></p></div></span></blockquote><div><br></div><div>All makes sense, except that we need to think who the lock 'owner' is going to be if we are not running in a tx. &nbsp;The way the cache works, locks are held by the thread (if not in a tx) or the associated GlobalTransaction object (if in a tx).</div><div><br></div><div>Let's take these cases one at a time. &nbsp;Let's start with (what I think) is the most pertinent:</div><div><br></div><div>A. &nbsp;Transactions + Eager locking</div><div>---------------------------------</div><div><br></div><div>when you do the following:</div><div><br></div><div>1. &nbsp;tx.begin()</div><div>2. &nbsp;cache.put(K, V)</div><div>3. &nbsp;tx.commit()</div><div><br></div><div>what happens is, at 1., a new GTX instance is associated with the transaction. &nbsp;At&nbsp;2. &nbsp;locks are acquired on K on the *local* cache, the owner being GTX. &nbsp;At 3., a prepare() is broadcast and the locks on K are acquired on all remote participating caches, the owner being GTX again.</div><div><br></div><div>The GTX is propagated with the PrepareCommand as it is a field in&nbsp;AbstractTransactionBoundaryCommand.</div><div><br></div><div>So now, an explicit lock in the scope of a transaction would also need to propagate the GTX - just so we know who the lock owner should be. &nbsp;E.g.,</div><div><br></div><div><div>1. &nbsp;tx.begin()</div><div>2. &nbsp;cache.lock(K)</div><div>3. &nbsp;// read K,V and calculate V2 which is a function of V. &nbsp;E.g., V2 = V + 1</div><div>4. &nbsp;cache.put(K, V2)</div><div>5. &nbsp;tx.commit()</div><div><br></div><div>In the above scenario, step 2 broadcasts a LockControlCommand which is constructed with the GTX and the necessary key(s). &nbsp;Remote caches acquire the locks using the GTX as the owner, and responds positively. &nbsp;(this RPC call would *have* to be sync regardless of cache mode).</div><div><br></div><div>The way I see it, cache.unlock() should be *optional*. &nbsp;If unlock() is not called, when the transaction completes all locks associated with the transaction are released anyway.</div><div><br></div><div>If unlock() is used, it would need some special behaviour. &nbsp;Consider:</div><div><br></div><div><div>1. &nbsp;tx.begin()</div><div>2. &nbsp;cache.lock(K)</div><div>3. &nbsp;cache.put(K2, V2)</div><div>4. &nbsp;cache.unlock(K)</div><div>5. &nbsp;tx.commit()</div><div><br></div><div>In the above case, unlock() would release locks at step 4 since the tx has not actually modified K. &nbsp;If, however, we have:</div><div><br></div><div><div>1. &nbsp;tx.begin()</div><div>2. &nbsp;cache.lock(K)</div><div>3. &nbsp;cache.put(K, V)</div><div>4. &nbsp;cache.unlock(K)</div><div>5. &nbsp;tx.commit()</div><div><br></div><div>then the unlock() should be a no-op (maybe don't even bother with the RPC) since we know that the tx has modified K and we can only feasibly release the lock once the transaction completes in 5.</div><div><br></div><div><div>B. &nbsp;Transactions + Non-eager locking</div><div>---------------------------------</div><div><br></div><div>So I guess non-eager would be that the acquisition of remote locks are deferred to when the prepare() broadcasts, and any potential lock&nbsp;acquisition&nbsp;failures happen at the time of prepare(). &nbsp;This is what we have in place right now anyway, I need to think about whether lock() and unlock() makes sense in a non-eager context. &nbsp;</div><div><br></div><div><div>C. &nbsp;No transaction + Eager locking</div><div>---------------------------------</div><div><br></div><div>This gets a bit more tricky, because in this case the lock owner is the Thread. &nbsp;And this does not translate to anything meaningful cluster-wide. &nbsp;Assume:</div><div><br></div><div>1. &nbsp;cache.lock(K)</div><div>2. &nbsp;cache.get(K)</div><div>3. &nbsp;cache.put(K, V)</div><div>4. &nbsp;cache.unlock()</div><div><br></div><div>Here, we notice that explicit use of unlock() is needed to release locks acquired by lock(), unlike the case where a committing (or rolling back) transaction would do this for you.</div><div><br></div><div>The tricky thing here is, who holds lock ownership across a cluster? &nbsp;:-) &nbsp;If this is standalone mode, this is simple - the Thread-owner paradigm works fine. &nbsp;Perhaps we need to change the locking code so that the non-tx lock owner is a combination of thread-name + cache address? &nbsp;I can see this leading to tricky stale locks though if we have a join/rejoin midway during a code sequence like above.</div><div><br></div><div><div>D. &nbsp;No transaction + Non-eager locking</div><div>---------------------------------</div><div><br></div><div>Just as in B., I'm not sure if this is a valid use case. &nbsp;I.e., how is this useful and what is its purpose. &nbsp;(perhaps just acquire local locks on lock() and only acquire the remote locks when the actual put() is performed?)</div><div><br></div><div><br></div><div><br></div><div>Anyway, to sum things up: the way I see it, B and D are cases that still need further thought (i.e., the non-eager cases). &nbsp;C needs a lot more in-depth thought as it has the potential to break a lot of stuff as it changes lock ownership for all non-transactional cases. &nbsp;So I am inclined to suggest that lock() and unlock() only works for case A. &nbsp;For other cases, we do not support the use of these methods. &nbsp;At least for now, until we think of C in greater detail.</div><div><br></div><div>What do you think? &nbsp;Apart from that, the detailed changes you mentioned below all make sense (except that I would add lockOwner as a field on LockControlCommand, etc).</div><div><br></div><div>Cheers</div><div>Manik</div><div><br></div></div></div></div></div><div><br></div></div></div><div><br></div><br><blockquote type="cite"><span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0; "><div bgcolor="#ffffff" text="#000000"><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">Example usage</font></font></p><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">Cache.lock(k1, k2, k3)<br>lots of rw operations with k1, k2, k3 while being sure no lock acquisition exceptions will be raisedCache.unlock(k1, k2, k3)</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font color="#000000" face="Times New Roman"><font size="3">Implementation</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><font size="3">1. Add the following API to AdvancedCache and implementation to CacheDelegate</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><font size="3">void lock(K key, boolean eager);<br>void lock(Collection&lt;? extends K> keys, boolean eager);<br>void unlock(K key);<br>void unlock(Collection&lt;? extends K> keys);</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><font size="3">2. Add the following API to CommandsFactory along with implementation in CommandsFactoryImpl</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><font size="3">public LockControlCommand buildLockControlCommand();</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><font size="3">3. LockControlCommand has following fields:</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><font size="3">a boolean to indicate whether this is a lock or unlock<br>a boolean to indicate whether this is an eager lock<br>an array of keys to be locked/unlocked</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><font size="3">4. Add LockingInterceptor that intercepts LockControlCommand and handles acquiring/releasing of locks as needed. Lock acquisition/release is implemented by using already existing LockManager. If LockManager is able to lock all required keys true is returned, otherwise false.</font></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p><p style="margin-bottom: 0.21cm; "><font face="Times New Roman"><br></font></p></div></span><br class="Apple-interchange-newline"></blockquote></div><br><div apple-content-edited="true"> <span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0; "><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; "><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><div>--</div><div>Manik Surtani</div><div><a href="mailto:manik@jboss.org">manik@jboss.org</a></div><div>Lead, Infinispan</div><div>Lead, JBoss Cache</div><div><a href="http://www.infinispan.org">http://www.infinispan.org</a></div><div><a href="http://www.jbosscache.org">http://www.jbosscache.org</a></div><div><br></div></div></span><br class="Apple-interchange-newline"></div></span><br class="Apple-interchange-newline"> </div><br></div></body></html>