<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">One thing, which is often missed by newcomers to Riak [I'm not saying you missed it], is the importance of managing client IDs, and passing the right vector clocks back to the server. <div><br></div><div> { Basho'ers ... please corret me if I'm wrong }</div><div><br></div><div>Kresten</div><div><br><div><br></div><div><br></div><div>So, Rule#1 (which has two clauses), which you can always revert to:</div><div><br></div><div>1.a / every client needs a clientID, which is distinct for that client.  Be sure to always pass it along in all calls (in Java that is done by calling setClientID on the RiakClient, at the HTTP-level, it is done by passing the X-Riak-ClientId HTTP header).</div><div><br></div><div>1.b / when you send an update (HTTP PUT or DELETE), always pass along the X-Riak-Vectorclock from a corresponding GET.  <b>If you don't do this, your PUT is likely to go to /dev/null, </b>because Riak thinks that it is a replay of an old request.</div><div><br></div><div>Until you're re really familiar with how Riak works, you should always do these two, or you will be severely burned when you realize that it doesn't behave as expected.  Believe me, I've been there.</div><div><br></div><div><br></div><div>1.a / Choosing a good client ID</div><div>========================</div><div><br></div><div>If you don't choose a client ID, Riak will do it for you ... BUT .. it will choose a new one for EVERY REQUEST.  This has many issues, so Riak should really require YOU to come up with one in stead; perhaps it will do so at some point in the future.</div><div><br></div><div>Riak has some special optimizations if your client ID is the Base64-encoding of a byte array of length 4.  So, a good, default way to choose a client id is thus:</div><div><br></div><div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">       </span><span style="color: #971365">static</span> SecureRandom <span style="color: #2121c4">rnd</span> = <span style="color: #971365">new</span> SecureRandom();</div><p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; min-height: 15.0px"><span class="Apple-tab-span" style="white-space:pre"> </span><br class="webkit-block-placeholder"></p><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">      </span><span style="color: #971365">static</span> ThreadLocal<String> <span style="color: #2121c4">CLIENT_ID</span> = <span style="color: #971365">new</span> ThreadLocal<String>() {</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">             </span><span style="color: #971365">protected</span> String initialValue() {</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">                      </span><span style="color: #971365">return</span> randomClientID();</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">               </span>};</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">       </span>};</div><p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; min-height: 15.0px"><span class="Apple-tab-span" style="white-space:pre">  </span><br class="webkit-block-placeholder"></p><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">      </span><span style="color: #971365">public </span><span class="Apple-style-span" style="color: rgb(151, 19, 101); ">static </span>String getClientID() {</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span style="color: #971365"></span></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">           </span><span style="color: #971365">return</span> <span style="color: #2121c4">CLIENT_ID</span>.get();</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">      </span>}</div><p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; min-height: 15.0px"><span class="Apple-tab-span" style="white-space:pre">   </span><br class="webkit-block-placeholder"></p><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">      </span><span style="color: #971365">static</span> <span style="color: #971365">private</span> String randomClientID() {</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">             </span><span style="color: #971365">byte</span>[] bytes = <span style="color: #971365">new</span> <span style="color: #971365">byte</span>[4];</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">                </span><span style="color: #2121c4">rnd</span>.nextBytes(bytes);</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">          </span><span style="color: #971365">return</span> Base64.encode(bytes);;</div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 11px/normal Monaco; "><span class="Apple-tab-span" style="white-space:pre">     </span>}</div></div><div><br></div><div>This makes it so that each thread in your application is assigned a new random ClientID, which is often useful if your client is multi-threaded.</div><div><br></div><div>The above code is *alot* better than the default of having the server side choose a new client id for every request.</div><div><br></div><div>If you have some kind of logical unique, non-concurrent client concept in your system, that may be even better.  It could e.g. be the IMEI of your mobile phone, if your Riak client app is running on a Phone; or it could be a userid, if you are sure that only one user is accessing the system at a time.</div><div><br></div><div><br></div><div><div>1.b / Passing the VectorClock</div><div>=======================</div><div><br></div></div><div>Secondly, you need to make sure that you pass the vector clock. </div><div><br></div><div>You should think of the vector clock as an opaque "optimistic concurrency token", that you receive when you do a GET, and have to pass in when you do a PUT ... and then you get a new "optimistic concurrency token", that you have to use henceforth.</div><div><br></div><div>Depending on the configuration of your buckets, using an old vector clock will simply cause the PUT request to be ignored (if allow_mult=false), or cause siblings to be created (if allow_mult=true).  This is where Riak is often "not what you expect", but there is a good reason for this behavior.</div><div><br></div><div>IT IS ABSOLUTELY PARAMOUNT TO UNDERSTAND THIS.</div><div><br></div><div><br></div><div><br></div><div>The above two things (1.a and 1.b) are so difficult to understand for newcomers, and a bit tricky to get right, so IMHO a new Java client should provide some way to avoid doing these mistakes as the default behavior.</div><div><br></div><div>- So, it should choose a good client ID fo you if you don't.</div><div>- And it should make it so that you can't do UPDATE/PUT without having first GOT'en the riak object.  </div><div><br></div><div>The last part is especially tricky.  Perhaps we should have the API look like this to help that ....</div><div><br></div><div>  interface RiakObject {</div><div>     ...</div><div>  }</div><div><br></div><div><div>  interface UpdateableRiakObject extends RiakObject { ... }</div></div><div><div>  interface CreateableRiakObject extends RiakObject { ... }</div></div><div><br></div><div>  RiakClient {</div><div>      UpdateableRiakObject update(UpdateableRiakObject o) throws NotModified</div><div>      { ... send PUT ... }</div><div><br></div><div>      UpdateableRiakObject create(CreateableRiakObject o) throws AlreadyThere</div><div>      { ... send PUT ... }</div><div><br></div><div>      UpdateableRiakObject get(bucket, key);</div><div><br></div><div>      CreateableRiakObject fresh(bucket, key);</div><div>  }</div><div><br></div><div>I.e. NOT EXPOSE constructors for the implementors of RiakObject.  The only way to get an UpdateableRiakObject is to call RiakClient.get, or as the result of calling update/create; you can't just allocate one.  Also calling update/create should "invalidate" the original object so that it cannot accidentally be used again.  </div><div><br></div><div>I really think we need to have a way to enforce the linear nature of these things.  Otherwise people get fooled.</div><div><br></div><div><br></div><div><br></div><div>Kresten</div><div><br></div><div><br></div><div><br></div><div><br></div></div></body></html>