<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jan 21, 2015 at 3:12 AM, Sanne Grinovero <span dir="ltr">&lt;<a href="mailto:sanne@infinispan.org" target="_blank">sanne@infinispan.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span>On 20 January 2015 at 13:32, Dan Berindei &lt;<a href="mailto:dan.berindei@gmail.com" target="_blank">dan.berindei@gmail.com</a>&gt; wrote:<br>
&gt; Adrian, I don&#39;t think that will work. The Hash doesn&#39;t know the number of<br>
&gt; segments so it can&#39;t tell where a particular key will land - even assuming<br>
&gt; knowledge about how the ConsistentHash will map hash codes to segments.<br>
&gt;<br>
&gt; However, I&#39;m all for replacing the current Hash interface with another<br>
&gt; interface that maps keys directly to segments.<br>
<br>
</span>Right, I&#39;ll eventually need a different abstraction, or a change to<br>
the Hash interface. However my need seems highly specialistic, I&#39;m not<br>
sure if there would be a general interest into such a capability for<br>
other Hash implementors?<br></blockquote><div><br></div><div>If it&#39;s better than KeyAffinityService and/or grouping, I&#39;m pretty sure there will be other takers.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
Never ever sign if you have more interesting comments below, I only<br>
saw them by chance ;)<br></blockquote><div><br></div><div>Oops, I only intended to reply to Adrian, and I forgot to remove the signature when I went further... I removed it now :)</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<div><div><br>
<br>
&gt; On Tue, Jan 20, 2015 at 4:08 AM, Adrian Nistor &lt;<a href="mailto:anistor@redhat.com" target="_blank">anistor@redhat.com</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; Hi Sanne,<br>
&gt;&gt;<br>
&gt;&gt; An alternative approach would be to implement an<br>
&gt;&gt; org.infinispan.commons.hash.Hash which delegates to the stock<br>
&gt;&gt; implementation for all keys except those that need to be assigned to a<br>
&gt;&gt; specific segment. It should return the desired segment for those.<br>
&gt;&gt;<br>
&gt;&gt; Adrian<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; On 01/20/2015 02:48 AM, Sanne Grinovero wrote:<br>
&gt;&gt; &gt; Hi all,<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; I&#39;m playing with an idea for some internal components to be able to<br>
&gt;&gt; &gt; &quot;tag&quot; the key for an entry to be stored into Infinispan in a very<br>
&gt;&gt; &gt; specific segment of the CH.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Conceptually the plan is easy to understand by looking at this patch:<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; <a href="https://github.com/Sanne/infinispan/commit/45a3d9e62318d5f5f950a60b5bb174d23037335f" target="_blank">https://github.com/Sanne/infinispan/commit/45a3d9e62318d5f5f950a60b5bb174d23037335f</a><br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Hacking the change into ReplicatedConsistentHash is quite barbaric,<br>
&gt;&gt; &gt; please bear with me as I couldn&#39;t figure a better way to be able to<br>
&gt;&gt; &gt; experiment with this. I&#39;ll probably want to extend this class, but<br>
&gt;&gt; &gt; then I&#39;m not sure how to plug it in?<br>
&gt;<br>
&gt;<br>
&gt; You would need to create your own ConsistentHashFactory, possibly extending<br>
&gt; ReplicatedConsistentHashFactory. You can then plug the factory in with<br>
&gt;<br>
&gt; configurationBuilder.clustering().hash().consistentHashFactory(yourFactory)<br>
&gt;<br>
&gt; However, this isn&#39;t a really good idea, because then you need a different<br>
&gt; implementation for distributed mode, and then another implementation for<br>
&gt; topology-aware clusters (with rack/machine/site ids). And your users would<br>
&gt; also need to select the proper factory for each cache.<br>
<br>
</div></div>Right, this is the complexity I was facing. I&#39;ll stick to my hack<br>
solution for our little POC... but ultimately I&#39;ll need to plug this<br>
in if none of the solutions below work out.<br></blockquote><div><br></div><div>In the meantime Adrian clarified that you&#39;re creating the configuration yourself, so there&#39;s no problem picking the proper consistent hash factory - as long as the user didn&#39;t plug in his own for the indexed cache.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<span><br>
&gt;&gt; &gt; What would you all think of such a &quot;tagging&quot; mechanism?<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; # Why I didn&#39;t use the KeyAffinityService<br>
&gt;&gt; &gt; - I need to use my own keys, not the meaningless stuff produced by the<br>
&gt;&gt; &gt; service<br>
&gt;&gt; &gt; - the extensive usage of Random in there doesn&#39;t seem suited for a<br>
&gt;&gt; &gt; performance critical path<br>
&gt;<br>
&gt;<br>
&gt; You can plug in your own KeyGenerator to generate keys, and maybe replace<br>
&gt; the Random with a static/thread-local counter.<br>
<br>
</span>Thanks for the tip on KeyGenerator, I&#39;ll investigate on that :)<br>
But I&#39;ll never add a static/threadlocal.. I&#39;d rather commit my ugly<br>
code from the commit linked above.<br></blockquote><div><br></div><div>Indeed, the fact that you need to generate a different key every time makes KeyAffinityService harder to work with.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<span><br>
&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; # Why I didn&#39;t use the Grouping API<br>
&gt;&gt; &gt; - I need to pick the specific storage segment, not just co-locate with<br>
&gt;&gt; &gt; a different key<br>
&gt;&gt; &gt;<br>
&gt;<br>
&gt;<br>
&gt; This is actually a drawback of the KeyAffinityService more than Grouping.<br>
&gt; With grouping, you can actually follow the KeyAffinityService strategy and<br>
&gt; generate random strings until you get one in the proper segment, and then<br>
&gt; tag all your keys with that exact string.<br>
<br>
</span>Interesting! A bit convoluted but could spare me to plug in the HashFactory.<br>
BTW I really dislike this idea of the KeyAffinityService to generate<br>
random keys until it works out..  I guess it might not be too bad if<br>
you want to pick a node out of ten, but I&#39;m working at segment<br>
granularity level and with the right luck it could take a long time.<br>
It would be nice to have a function like this which would return in a<br>
deterministic amount of time, like simply an inverse Hash.<br></blockquote><div><br></div><div>Weird, I was 100% sure that there&#39;s no way to reverse MurmurHash3, but it seems there is [1]. Our implementation is a bit different, but it shouldn&#39;t be very hard to adopt.</div><div><br></div><div>On the other hand, AbstractTopologyAwareEncoder1x.denormalizeSegmentHashIds brute-forces MurmurHash3 on each topology update to get a &quot;denormalized&quot; start value for each segment, which has to map to 0.2% of the segment. I don&#39;t remember how much it took, but it wasn&#39;t that bad. The good part about using grouping is that you can have lots of keys with the same group key, so you would only have to find the inverse once.</div><div><br></div><div>[1] <a href="https://131002.net/siphash/#at" target="_blank">https://131002.net/siphash/#at</a></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<span><br>
&gt;&gt; &gt; The general goal is to make it possible to &quot;tag&quot; all entries of an<br>
&gt;&gt; &gt; index, and have an independent index for each segment of the CH. So<br>
&gt;&gt; &gt; the resulting effect would be, that when a primary owner for any key K<br>
&gt;&gt; &gt; is making an update, and this triggers an index update, that update is<br>
&gt;&gt; &gt;   A) going to happen on the same node -&gt; no need to forwarding to a<br>
&gt;&gt; &gt; &quot;master indexing node&quot;<br>
&gt;&gt; &gt;   B) each such writes on the index happen on the same node which is<br>
&gt;&gt; &gt; primary owner for all the written entries of the index.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; There are two additional nice consequences:<br>
&gt;&gt; &gt;   - there would be no need to perform a reliable &quot;master election&quot;:<br>
&gt;&gt; &gt; ownership singleton is already guaranteed by Infinispan&#39;s essential<br>
&gt;&gt; &gt; logic, so it would reuse that<br>
&gt;&gt; &gt;   - the propagation of writes on the index from the primary owner<br>
&gt;&gt; &gt; (which is the local node by definition) to backup owners could use<br>
&gt;&gt; &gt; REPL_ASYNC for most practical use cases.<br>&gt;&gt; &gt;<br>
&gt;&gt; &gt; So net result is that the overhead for indexing is reduced to 0 (ZERO)<br>
&gt;&gt; &gt; blocking RPCs if the async repl is acceptable, or to only one blocking<br>
&gt;&gt; &gt; roundtrip if very strict consistency is required.<br>
&gt;<br>
&gt;<br>
&gt; Sounds very interesting, but I think there may be a problem with your<br>
&gt; strategy: Infinispan doesn&#39;t guarantee you that one of the nodes executing<br>
&gt; the CommitCommand is the primary owner at the time the CommitCommand is<br>
&gt; executed. You could have something like this:<br>
<br>
</span>Index storage is generally used without transactions, but even<br>
assuming we had transactions enabled or the &quot;vanilla&quot; put operation<br>
suffered had a similar timing issue (as we&#39;d determine this node to be<br>
owner higher up in the search stack, before the actual put reaches the<br>
Infinispan core API) it&#39;s not a problem as we&#39;d simply lose locality<br>
of the write: it would be slightly less efficient, but still write the<br>
&quot;right thing&quot;.<br></blockquote><div><br></div><div><div>I was thinking about the indexed cache, not the storage cache. In the storage cache I think the only serious problem is when the originator dies, especially if it&#39;s also the primary owner, neither tx nor non-tx deal with that properly ATM.</div></div><div><br></div><div>However, if I understood your idea correctly, only the primary owner in the indexed cache will ever write to the index, and the indexed cache may well use transactions. So I think my scenario is relevant, and the index won&#39;t be updated by B or anyone else.</div><div><br></div><div>Non-transactional caches do not have the issue, the index update can only disappear if you use async replication in the index storage cache. Instead you can have a single write triggering multiple index updates, but I&#39;m guessing you have that covered.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
The intention is to maximise locality with the hints, but failing<br>
locality on writes I just expect it to be handled as any other put<br>
operation on Infinispan.. with a couple more RPCs, with any race<br>
condition regarding topology changes being handled at lower level.<br>
Which is exactly why I&#39;m now working on top of  container segments:<br>
they are a stable building block and allow us to not worry on how<br>
you&#39;ll actually distribute data or re-route update commands.<br>
<br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Thanks all for the suggestions!<br>
<span><font color="#888888">Sanne<br>
</font></span><div><div><br>
&gt;<br>
&gt; Cluster [A, B, C, D], key k, owners(k) = [A, B] (A is primary)<br>
&gt; C initiates a tx that executes put(k, v)<br>
&gt; Tx prepare succeeds on A and B<br>
&gt; A crashes, but the other nodes don&#39;t detect the crash yet<br>
&gt; Tx commit succeeds on B, who still thinks is a backup owner<br>
&gt; B detects the crash, installs a new cluster view consistent hash with<br>
&gt; owners(k) = [B]<br>
&gt;</div></div></blockquote><div> </div></div></div><div class="gmail_extra">Cheers</div><div class="gmail_extra">Dan</div><div class="gmail_extra"><br></div></div>