<br><br><div class="gmail_quote">On Fri, Jun 28, 2013 at 12:51 AM, William Burns <span dir="ltr">&lt;<a href="mailto:mudokonman@gmail.com" target="_blank">mudokonman@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

<div class="HOEnZb"><div class="h5">On Thu, Jun 27, 2013 at 4:40 PM, Dan Berindei &lt;<a href="mailto:dan.berindei@gmail.com">dan.berindei@gmail.com</a>&gt; wrote:<br>
&gt;<br>
&gt;<br>
&gt; On Thu, Jun 27, 2013 at 4:40 PM, William Burns &lt;<a href="mailto:mudokonman@gmail.com">mudokonman@gmail.com</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; Comments that were outstanding on PR:<br>
&gt;&gt;<br>
&gt;&gt; @danberindei:<br>
&gt;&gt;<br>
&gt;&gt;  &gt; +1 to move the discussion to the mailing list, could you summarize<br>
&gt;&gt; your changes (preferably for both non-tx and tx cases) and send an<br>
&gt;&gt; email to the list?<br>
&gt;&gt;  &gt; And now to add some more to this already unwieldy discussion :)<br>
&gt;&gt;<br>
&gt;&gt;  &gt;  1. Many interceptors check ownership, I don&#39;t think that would be<br>
&gt;&gt; a problem. Besides, I think creating a new L1ReadSynchronizer for<br>
&gt;&gt; every read is just as bad for performance as locking the key on every<br>
&gt;&gt; read, so you&#39;d need that check either way.<br>
&gt;&gt; &gt;<br>
&gt;&gt; We can&#39;t use a try lock approach for L1 invalidation when gets are<br>
&gt;&gt; locked without possibly dropping a L1 update<br>
&gt;&gt; We can&#39;t use a timed lock approach for L1 invalidation when writes<br>
&gt;&gt; lock the key as we could get into a timed deadlock situation when<br>
&gt;&gt; another node concurrently writes to a value/stripe<br>
&gt;&gt;<br>
&gt;&gt; I still don&#39;t see how we can get away with locking on a get.  What are<br>
&gt;&gt; you proposing?<br>
&gt;<br>
&gt;<br>
&gt; I&#39;m proposing something like this:<br>
&gt;<br>
&gt; 1. For get commands: acquire local lock + remote get + store in L1 + release<br>
&gt; local lock<br>
&gt; 2. For invalidate commands: acquire local lock + remove entry from L1 +<br>
&gt; release local lock<br>
&gt; 3. For write commands: invoke on primary owner, and the primary owner sends<br>
&gt; invalidation back to originator; alternatively, skip the invalidation<br>
&gt; command to the originator and do invoke on primary owner + acquire local<br>
&gt; lock + remove entry from L1 + release local lock<br>
<br>
</div></div>I do like this and think it would be simpler for non-tx.  However this<br>
will still not be as performant I wouldn&#39;t think (just a gut feeling).<br></blockquote><div><br>It would indeed be less performant in some cases, e.g. if we have two get commands for a remote key at the same time and the key doesn&#39;t exist, my proposal would request the key twice.<br>

<br>The writes to L1 *should* be rare enough that the extra lock contention there would be minimal. There is a chance that an invalidation will be stuck waiting for a remote get, so it will again be slower than your approach.<br>

<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
 However I am more than certain it will be slower when collisions<br>
occur, which is more likely with lock striping.<br>
<br></blockquote><div><br>I&#39;m not that worried about lock striping - the user can always increase the number of stripes to get less contention. And I hope they know better than to use lock striping and transactions with multiple keys, or they&#39;ll get deadlocks with or without L1 enabled.<br>

 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
I don&#39;t think this however is feasible for tx caches.<br>
<br>
Optimistic - we don&#39;t want to have gets being blocked when the tx is<br>
being committed - that could be quite a bit of a performance hit<br>
especially if it has to do a 2 phase commit.  Updating the L1 on a<br>
remoteGetBeforeWrites  could help remedy this (since get wouldn&#39;t need<br>
to lock), however then you would be almost implementing what I have<br>
and still have locking overhead.  Maybe this would occur so<br>
infrequently it wouldn&#39;t matter though?<br>
<br></blockquote><div><br>Yes, I would propose removing the L1 commit in EntryWrappingInterceptor as well, and moving it to the L1 interceptor - immediately after the value was retrieved from the remote owner. The local lock would also be released immediately.<br>

<br>The part I wanted to avoid from your proposal was L1ReadSync/L1ReadSynchronizer, because I found it kind of hard to understand. I guess I&#39;d find it more approachable all the synchronization (the map of L1ReadSynchronizers and the interaction with it) would be in a separate class, with its own tests, and the L1 interceptor would only know about the high-level stuff. Like the LockManager also hides a map of locks; not like the LockManager unit tests, because they&#39;re kind of lacking, but LockManager has the advantage that almost all the tests touch it indirectly.<br>

 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Pessimistic - I haven&#39;t looked into pessimistic closely yet, but in<br>
that case I don&#39;t think it ever acquires the local locks so it would<br>
have to acquire a remote lock on a get, which would be pretty<br>
disastrous (gets would indirectly have a weak FORCE_WRITE_LOCK<br>
enabled).  Making this to acquire local locks might work as well, but<br>
then would still have the same issue as optimistic when committing - I<br>
really am not as familiar with Pessimistic to say for sure.<br>
<div class="im"><br></div></blockquote><div><br>Nope, acquiring a local lock should be enough. We don&#39;t want to prevent other txs from modifying the key at all, we just want to delay the invalidation commands triggered by those modifications on the local node. And the lock would only be held for the duration of the remote get + L1 update, not all the way to the commit.<br>

 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">
&gt;<br>
&gt; All the lock acquisitions would use a regular timeout, not a try lock<br>
&gt; approach.<br>
</div>In this case they could, yes.<br>
<div class="im">&gt;<br>
&gt;&gt;<br>
&gt;&gt; &gt;   2. By default L1 invalidations are sent as multicasts, so I&#39;m not sure<br>
&gt;&gt; &gt; ISPN-3273 really matters here. BTW, I wonder if we have a check to only send<br>
&gt;&gt; &gt; L1 invalidations from one node if the threshold is 0...<br>
&gt;&gt; I agree that is the default, but we should support the operation,<br>
&gt;&gt; although it doesn&#39;t matter for this discussion.  Also I am curious as<br>
&gt;&gt; to why multicast for L1 isn&#39;t set to say 2 by default?  It seems<br>
&gt;&gt; wasteful to send a multicast to all members that they process when<br>
&gt;&gt; only 1 would do anything about it.  Do you know why this is like that?<br>
&gt;<br>
&gt;<br>
&gt; I suppose it&#39;s because we don&#39;t have good perf numbers for different L1<br>
&gt; invalidation threshold numbers...<br>
&gt;<br>
&gt; The problem is, we don&#39;t have a way to count all the requestors of a key in<br>
&gt; the cluster, so it&#39;s reasonably likely that with a threshold of 2 you&#39;d get<br>
&gt; 1 unicast invalidation from one owner + 1 multicast invalidation from the<br>
&gt; other owner, making it less efficient than a single multicast invalidation.<br>
<br>
</div>It is a nitpick anyways, and really shouldn&#39;t make that big of a difference.<br>
<div> <br></div></blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">
&gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;  3a. Right, for put commands we can&#39;t hold the local lock while<br>
&gt;&gt; &gt; executing the remote put, or we&#39;ll have a deadlock. But I think a shorter<br>
&gt;&gt; &gt; lock, held only after the remote put completed (or after the lock on the<br>
&gt;&gt; &gt; primary owner was acquired, with txs) should work.<br>
&gt;&gt; Same point under 1<br>
&gt;<br>
&gt;<br>
&gt; I don&#39;t see how we could get a deadlock if we don&#39;t hold the local lock<br>
&gt; during the remote write invocation.<br>
<br>
</div>Agree.<br>
<div class="im"><br>
&gt;<br>
&gt;&gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;  3b. We&#39;d also have an ownership check before, so we&#39;d only serialize<br>
&gt;&gt; &gt; the get commands that need to go remotely for the same key. I think it would<br>
&gt;&gt; &gt; be almost the same as your solution (although it does have one ?<br>
&gt;&gt; &gt; disadvantage - if the key doesn&#39;t exist in the cache, all the get commands<br>
&gt;&gt; &gt; will go remotely). The number of L1 writes should be very small compared to<br>
&gt;&gt; &gt; the number of L1 reads anyway, otherwise it would be more efficient to get<br>
&gt;&gt; &gt; the key from the owner every time.<br>
&gt;&gt; You are saying an optimization for owner nodes so they don&#39;t do the<br>
&gt;&gt; &quot;corralling&quot; for keys they own?  I like that.  Also I don&#39;t think it<br>
&gt;&gt; has the disadvantage, it only does remotes it if isn&#39;t an owner.<br>
&gt;<br>
&gt;<br>
&gt; I meant your corralling strategy means if you have 2 concurrent get commands<br>
&gt; and one of them retrieves a null from the entry owners, the other command<br>
&gt; will return null directly. With regular locking, the other command wouldn&#39;t<br>
&gt; find anything in L1 and it would do another remote get.<br>
&gt;<br>
&gt; I don&#39;t think there&#39;s any disadvantage in skipping the corralling for key<br>
&gt; owners, in fact I think we need to skip it if the key already exists in L1,<br>
&gt; too.<br>
</div>+1<br>
<div class="HOEnZb"><div class="h5">&gt;<br>
&gt;&gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; It would be nice to agree on what guarantees we want to provide for L1<br>
&gt;&gt; &gt; invalidation in non-tx caches, I&#39;m not sure if we can do anything to prevent<br>
&gt;&gt; &gt; this scenario:<br>
&gt;&gt; Actually this scenario doesn&#39;t occur with non-tx since writes don&#39;t<br>
&gt;&gt; update the L1 with their value, they just invalidate.  Tx caches are<br>
&gt;&gt; fine with this because they acquire the primary owner lock for the<br>
&gt;&gt; duration of the write including the L1 update so you can&#39;t have this<br>
&gt;&gt; ordering.<br>
&gt;<br>
&gt;<br>
&gt; Sounds good.<br>
&gt;<br>
&gt;&gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; A initiates a put(k, v1) to the primary owner B<br>
&gt;&gt; &gt; B performs the put(k, v1), invalidates every non-owner and returns<br>
&gt;&gt; &gt; B performs another put(k, v2), invalidating every non-owner<br>
&gt;&gt; &gt; A receives the result from B and puts k=v1 in its L1<br>
&gt;&gt;<br>
&gt;&gt; @pruivo:<br>
&gt;&gt;<br>
&gt;&gt; &gt; The invalidation does not need to wait for the remote get. When you<br>
&gt;&gt; &gt; receive an invalidation, you can mark the current remote get invalid. The<br>
&gt;&gt; &gt; invalidation command can return immediately and the remote get can be<br>
&gt;&gt; &gt; repeated. Also, it removes the key from data container (if exists)<br>
&gt;&gt; Dan hit it right in the head.  Unfortunately there is no guarantee the<br>
&gt;&gt; cancellation can work properly, so it is a best effort and if not wait<br>
&gt;&gt; until we know we will invalidate properly.<br>
&gt;&gt; &gt; The writes can update the L1 through your L1Synchronized by adding a<br>
&gt;&gt; &gt; simple method like updateL1(newValue). The blocking threads will return<br>
&gt;&gt; &gt; immediately the new value and they don&#39;t need to wait for the reply.<br>
&gt;&gt; Non tx cache write operations aren&#39;t safe to update L1 with the value<br>
&gt;&gt; since they don&#39;t acquire the owning lock while updating the L1, which<br>
&gt;&gt; means you could have interleaved writes.  Which is the primary reason<br>
&gt;&gt; I rejected ISPN- 3214.  For tx caches we can&#39;t do this since the<br>
&gt;&gt; update has to take part of the tx, which the get would be updating the<br>
&gt;&gt; L1 outside of a transaction.<br>
&gt;&gt; &gt; I see... However, I think that all the events should synchronize at some<br>
&gt;&gt; &gt; point (update by remote get, update by local put and invalidation).<br>
&gt;&gt; I was hoping that would cover this.  Other than the outstanding issue<br>
&gt;&gt; in ISPN-2965.<br>
&gt;&gt;<br>
&gt;&gt; On Thu, Jun 27, 2013 at 9:18 AM, William Burns &lt;<a href="mailto:mudokonman@gmail.com">mudokonman@gmail.com</a>&gt;<br>
&gt;&gt; wrote:<br>
&gt;&gt; &gt; First off I apologize for the length.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; There have been a few Jiras recently that have identified L1 consistency<br>
&gt;&gt; &gt; issues with both TX and non TX sync caches.  Async caches with L1 have<br>
&gt;&gt; &gt; their<br>
&gt;&gt; &gt; own issues as well, but I only wanted to talk about sync caches.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; <a href="https://issues.jboss.org/browse/ISPN-3197" target="_blank">https://issues.jboss.org/browse/ISPN-3197</a><br>
&gt;&gt; &gt; <a href="https://issues.jboss.org/browse/ISPN-2965" target="_blank">https://issues.jboss.org/browse/ISPN-2965</a><br>
&gt;&gt; &gt; <a href="https://issues.jboss.org/browse/ISPN-2990" target="_blank">https://issues.jboss.org/browse/ISPN-2990</a><br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; I have proposed a solution in<br>
&gt;&gt; &gt; <a href="https://github.com/infinispan/infinispan/pull/1922" target="_blank">https://github.com/infinispan/infinispan/pull/1922</a> which should start L1<br>
&gt;&gt; &gt; consistency down the right track.  There are quite a few comments on it<br>
&gt;&gt; &gt; if<br>
&gt;&gt; &gt; you want to look into it more, but because of that I am moving this to<br>
&gt;&gt; &gt; the<br>
&gt;&gt; &gt; dev mailing list.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; The key changes in the PR are the following (non-tx):<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; 1. Concurrent reads for a key that can retrieve a remote value are<br>
&gt;&gt; &gt; &quot;corralled&quot; into a single thread of execution for that given key.  This<br>
&gt;&gt; &gt; would reduce network traffic with concurrent gets for the same key.<br>
&gt;&gt; &gt; Note<br>
&gt;&gt; &gt; the &quot;corralling&quot; only happens on a per key basis.<br>
&gt;&gt; &gt; 2. The single thread that is doing the remote get would update the L1 if<br>
&gt;&gt; &gt; able (without locking) and make available the value to all the requests<br>
&gt;&gt; &gt; waiting on the get.<br>
&gt;&gt; &gt; 3. Invalidations that are received would first check to see if there is<br>
&gt;&gt; &gt; a<br>
&gt;&gt; &gt; current remote get occurring for it&#39;s keys.  If there is it will attempt<br>
&gt;&gt; &gt; to<br>
&gt;&gt; &gt; cancel the L1 write(s) before it occurs.  If it cannot cancel the L1<br>
&gt;&gt; &gt; write,<br>
&gt;&gt; &gt; then it must also wait on the current remote get completion and<br>
&gt;&gt; &gt; subsequently<br>
&gt;&gt; &gt; run the invalidation.  Note the cancellation would fail when the remote<br>
&gt;&gt; &gt; get<br>
&gt;&gt; &gt; was done and it is in the middle of updating the L1, so this would be<br>
&gt;&gt; &gt; very<br>
&gt;&gt; &gt; small window.<br>
&gt;&gt; &gt; 4. Local writes will also do the same thing as the invalidation with<br>
&gt;&gt; &gt; cancelling or waiting.  Note that non tx local writes only do L1<br>
&gt;&gt; &gt; invalidations and don&#39;t write the value to the data container.  Reasons<br>
&gt;&gt; &gt; why<br>
&gt;&gt; &gt; I found at <a href="https://issues.jboss.org/browse/ISPN-3214" target="_blank">https://issues.jboss.org/browse/ISPN-3214</a><br>
&gt;&gt; &gt; 5. Writes that require the previous value and don&#39;t have it in the L1<br>
&gt;&gt; &gt; would<br>
&gt;&gt; &gt; also do it&#39;s get operations using the same &quot;corralling&quot; method.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; 4/5 are not currently implemented in PR.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; This approach would use no locking for non tx caches for all L1<br>
&gt;&gt; &gt; operations.<br>
&gt;&gt; &gt; The synchronization point would be done through the &quot;corralling&quot; method<br>
&gt;&gt; &gt; and<br>
&gt;&gt; &gt; invalidations/writes communicating to it.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Transactional caches would do almost the same thing as non-tx.  Note<br>
&gt;&gt; &gt; these<br>
&gt;&gt; &gt; changes are not done in any way yet.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; 1. Gets would now update the L1 immediately after retrieving the value<br>
&gt;&gt; &gt; without locking, but still using the &quot;corralling&quot; technique that non-tx<br>
&gt;&gt; &gt; does.  Previously the L1 update from a get was transactional.  This<br>
&gt;&gt; &gt; actually<br>
&gt;&gt; &gt; would remedy issue [1]<br>
&gt;&gt; &gt; 2. Writes currently acquire the remote lock when committing, which is<br>
&gt;&gt; &gt; why tx<br>
&gt;&gt; &gt; caches are able to update the L1 with the value.  Writes would do the<br>
&gt;&gt; &gt; same<br>
&gt;&gt; &gt; cancellation/wait method as non-tx.<br>
&gt;&gt; &gt; 3. Writes that require the previous value and don&#39;t have it in the L1<br>
&gt;&gt; &gt; would<br>
&gt;&gt; &gt; also do it&#39;s get operations using the same method.<br>
&gt;&gt; &gt; 4. For tx cache [2] would also have to be done.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; [1] -<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; <a href="https://issues.jboss.org/browse/ISPN-2965?focusedCommentId=12779780&amp;page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-12779780" target="_blank">https://issues.jboss.org/browse/ISPN-2965?focusedCommentId=12779780&amp;page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-12779780</a><br>


&gt;&gt; &gt; [2] - <a href="https://issues.jboss.org/browse/ISPN-1540" target="_blank">https://issues.jboss.org/browse/ISPN-1540</a><br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Also rehashing is another issue, but we should be able to acquire the<br>
&gt;&gt; &gt; state<br>
&gt;&gt; &gt; transfer lock before updating the L1 on a get, just like when an entry<br>
&gt;&gt; &gt; is<br>
&gt;&gt; &gt; committed to the data container.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Any comments/concerns would be appreciated.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Thanks,<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;  - Will<br>
&gt;&gt;<br>
&gt;&gt; _______________________________________________<br>
&gt;&gt; infinispan-dev mailing list<br>
&gt;&gt; <a href="mailto:infinispan-dev@lists.jboss.org">infinispan-dev@lists.jboss.org</a><br>
&gt;&gt; <a href="https://lists.jboss.org/mailman/listinfo/infinispan-dev" target="_blank">https://lists.jboss.org/mailman/listinfo/infinispan-dev</a><br>
&gt;<br>
&gt;<br>
&gt;<br>
&gt; _______________________________________________<br>
&gt; infinispan-dev mailing list<br>
&gt; <a href="mailto:infinispan-dev@lists.jboss.org">infinispan-dev@lists.jboss.org</a><br>
&gt; <a href="https://lists.jboss.org/mailman/listinfo/infinispan-dev" target="_blank">https://lists.jboss.org/mailman/listinfo/infinispan-dev</a><br>
_______________________________________________<br>
infinispan-dev mailing list<br>
<a href="mailto:infinispan-dev@lists.jboss.org">infinispan-dev@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/infinispan-dev" target="_blank">https://lists.jboss.org/mailman/listinfo/infinispan-dev</a><br>
</div></div></blockquote></div><br>