If the contract is "Return true iff the object existed", then the second fetch is superfluous + so is the async example I posted. You can use the code you had as-is.
Thanks, Alex On Mon, Feb 22, 2016 at 1:23 PM, Vanessa Williams < vanessa.willi...@thoughtwire.ca> wrote: > Hi Alex, would a second fetch just indicate that the object is *still* > deleted? Or that this delete operation succeeded? In other words, perhaps > what my contract really is is: return true if there was already a value > there. In which case would the second fetch be superfluous? > > Thanks for your help. > > Vanessa > > On Mon, Feb 22, 2016 at 11:15 AM, Alex Moore <amo...@basho.com> wrote: > >> That's the correct behaviour: it should return true iff a value was >>> actually deleted. >> >> >> Ok, if that's the case you should do another FetchValue after the >> deletion (to update the response.hasValues()) field, or use the async >> version of the delete function. I also noticed that we weren't passing the >> vclock to the Delete function, so I added that here as well: >> >> public boolean delete(String key) throws ExecutionException, >> InterruptedException { >> >> // fetch in order to get the causal context >> FetchValue.Response response = fetchValue(key); >> >> if(response.isNotFound()) >> { >> return ???; // what do we return if it doesn't exist? >> } >> >> DeleteValue deleteValue = new DeleteValue.Builder(new >> Location(namespace, key)) >> >> .withVClock(response.getVectorClock()) >> .build(); >> >> final RiakFuture<Void, Location> deleteFuture = >> client.executeAsync(deleteValue); >> >> deleteFuture.await(); >> >> if(deleteFuture.isSuccess()) >> { >> return true; >> } >> else >> { >> deleteFuture.cause(); // Cause of failure >> return false; >> } >> } >> >> >> Thanks, >> Alex >> >> On Mon, Feb 22, 2016 at 10:48 AM, Vanessa Williams < >> vanessa.willi...@thoughtwire.ca> wrote: >> >>> See inline: >>> >>> On Mon, Feb 22, 2016 at 10:31 AM, Alex Moore <amo...@basho.com> wrote: >>> >>>> Hi Vanessa, >>>> >>>> You might have a problem with your delete function (depending on it's >>>> return value). >>>> What does the return value of the delete() function indicate? Right >>>> now if an object existed, and was deleted, the function will return true, >>>> and will only return false if the object didn't exist or only consisted of >>>> tombstones. >>>> >>> >>> >>> That's the correct behaviour: it should return true iff a value was >>> actually deleted. >>> >>> >>>> If you never look at the object value returned by your fetchValue(key) >>>> function, another potential optimization you could make is to only return >>>> the HEAD / metadata: >>>> >>>> FetchValue fv = new FetchValue.Builder(new Location(new Namespace( >>>> "some_bucket"), key)) >>>> >>>> .withOption(FetchValue.Option.HEAD, true) >>>> .build(); >>>> >>>> This would be more efficient because Riak won't have to send you the >>>> values over the wire, if you only need the metadata. >>>> >>>> >>> Thanks, I'll clean that up. >>> >>> >>>> If you do write this up somewhere, share the link! :) >>>> >>> >>> Will do! >>> >>> Regards, >>> Vanessa >>> >>> >>>> >>>> Thanks, >>>> Alex >>>> >>>> >>>> On Mon, Feb 22, 2016 at 6:23 AM, Vanessa Williams < >>>> vanessa.willi...@thoughtwire.ca> wrote: >>>> >>>>> Hi Dmitri, this thread is old, but I read this part of your answer >>>>> carefully: >>>>> >>>>> You can use the following strategies to prevent stale values, in >>>>>> increasing order of security/preference: >>>>>> 1) Use timestamps (and not pass in vector clocks/causal context). >>>>>> This is ok if you're not editing objects, or you're ok with a bit of risk >>>>>> of stale values. >>>>>> 2) Use causal context correctly (which means, read-before-you-write >>>>>> -- in fact, the Update operation in the java client does this for you, I >>>>>> think). And if Riak can't determine which version is correct, it will >>>>>> fall >>>>>> back on timestamps. >>>>>> 3) Turn on siblings, for that bucket or bucket type. That way, Riak >>>>>> will still try to use causal context to decide the right value. But if it >>>>>> can't decide, it will store BOTH values, and give them back to you on the >>>>>> next read, so that your application can decide which is the correct one. >>>>> >>>>> >>>>> I decided on strategy #2. What I am hoping for is some validation that >>>>> the code we use to "get", "put", and "delete" is correct in that context, >>>>> or if it could be simplified in some cases. Not we are using delete-mode >>>>> "immediate" and no duplicates. >>>>> >>>>> In their shortest possible forms, here are the three methods I'd like >>>>> some feedback on (note, they're being used in production and haven't >>>>> caused >>>>> any problems yet, however we have very few writes in production so the >>>>> lack >>>>> of problems doesn't support the conclusion that the implementation is >>>>> correct.) Note all argument-checking, exception-handling, and logging >>>>> removed for clarity. *I'm mostly concerned about correct use of >>>>> causal context and response.isNotFound and response.hasValues. *Is >>>>> there anything I could/should have left out? >>>>> >>>>> public RiakClient(String name, >>>>> com.basho.riak.client.api.RiakClient client) >>>>> { >>>>> this.name = name; >>>>> this.namespace = new Namespace(name); >>>>> this.client = client; >>>>> } >>>>> >>>>> public byte[] get(String key) throws ExecutionException, >>>>> InterruptedException { >>>>> >>>>> FetchValue.Response response = fetchValue(key); >>>>> if (!response.isNotFound()) >>>>> { >>>>> RiakObject riakObject = >>>>> response.getValue(RiakObject.class); >>>>> return riakObject.getValue().getValue(); >>>>> } >>>>> return null; >>>>> } >>>>> >>>>> public void put(String key, byte[] value) throws >>>>> ExecutionException, InterruptedException { >>>>> >>>>> // fetch in order to get the causal context >>>>> FetchValue.Response response = fetchValue(key); >>>>> RiakObject storeObject = new >>>>> >>>>> RiakObject().setValue(BinaryValue.create(value)).setContentType("binary/octet-stream"); >>>>> StoreValue.Builder builder = >>>>> new StoreValue.Builder(storeObject).withLocation(new >>>>> Location(namespace, key)); >>>>> if (response.getVectorClock() != null) { >>>>> builder = >>>>> builder.withVectorClock(response.getVectorClock()); >>>>> } >>>>> StoreValue storeValue = builder.build(); >>>>> client.execute(storeValue); >>>>> } >>>>> >>>>> public boolean delete(String key) throws ExecutionException, >>>>> InterruptedException { >>>>> >>>>> // fetch in order to get the causal context >>>>> FetchValue.Response response = fetchValue(key); >>>>> if (!response.isNotFound()) >>>>> { >>>>> DeleteValue deleteValue = new DeleteValue.Builder(new >>>>> Location(namespace, key)).build(); >>>>> client.execute(deleteValue); >>>>> } >>>>> return !response.isNotFound() || !response.hasValues(); >>>>> } >>>>> >>>>> >>>>> Any comments much appreciated. I want to provide a minimally correct >>>>> example of simple client code somewhere (GitHub, blog post, something...) >>>>> so I don't want to post this without review. >>>>> >>>>> Thanks, >>>>> Vanessa >>>>> >>>>> ThoughtWire Corporation >>>>> http://www.thoughtwire.com >>>>> >>>>> >>>>> >>>>> >>>>> On Thu, Oct 8, 2015 at 8:45 AM, Dmitri Zagidulin <dzagidu...@basho.com >>>>> > wrote: >>>>> >>>>>> Hi Vanessa, >>>>>> >>>>>> The thing to keep in mind about read repair is -- it happens >>>>>> asynchronously on every GET, but /after/ the results are returned to the >>>>>> client. >>>>>> >>>>>> So, when you issue a GET with r=1, the coordinating node only waits >>>>>> for 1 of the replicas before responding to the client with a success, and >>>>>> only afterwards triggers read-repair. >>>>>> >>>>>> It's true that with notfound_ok=false, it'll wait for the first >>>>>> non-missing replica before responding. But if you edit or update your >>>>>> objects at all, an R=1 still gives you a risk of stale values being >>>>>> returned. >>>>>> >>>>>> For example, say you write an object with value A. And let's say >>>>>> your 3 replicas now look like this: >>>>>> >>>>>> replica 1: A, replica 2: A, replica 3: notfound/missing >>>>>> >>>>>> A read with an R=1 and notfound_ok=false is just fine, here. (Chances >>>>>> are, the notfound replica will arrive first, but the notfound_ok setting >>>>>> will force the coordinator to wait for the first non-empty value, A, and >>>>>> return it to the client. And then trigger read-repair). >>>>>> >>>>>> But what happens if you edit that same object, and give it a new >>>>>> value, B? So, now, there's a chance that your replicas will look like >>>>>> this: >>>>>> >>>>>> replica 1: A, replica 2: B, replica 3: B. >>>>>> >>>>>> So now if you do a read with an R=1, there's a chance that replica 1, >>>>>> with the old value of A, will arrive first, and that's the response that >>>>>> will be returned to the client. >>>>>> >>>>>> Whereas, using R=2 eliminates that risk -- well, at least decreases >>>>>> it. You still have the issue of -- how does Riak decide whether A or B is >>>>>> the correct value? Are you using causal context/vclocks correctly? (That >>>>>> is, reading the object before you update, to get the correct causal >>>>>> context?) Or are you relying on timestamps? (This is an ok strategy, >>>>>> provided that the edits are sufficiently far apart in time, and you don't >>>>>> have many concurrent edits, AND you're ok with the small risk of >>>>>> occasionally the timestamp being wrong). You can use the following >>>>>> strategies to prevent stale values, in increasing order of >>>>>> security/preference: >>>>>> >>>>>> 1) Use timestamps (and not pass in vector clocks/causal context). >>>>>> This is ok if you're not editing objects, or you're ok with a bit of risk >>>>>> of stale values. >>>>>> >>>>>> 2) Use causal context correctly (which means, read-before-you-write >>>>>> -- in fact, the Update operation in the java client does this for you, I >>>>>> think). And if Riak can't determine which version is correct, it will >>>>>> fall >>>>>> back on timestamps. >>>>>> >>>>>> 3) Turn on siblings, for that bucket or bucket type. That way, Riak >>>>>> will still try to use causal context to decide the right value. But if it >>>>>> can't decide, it will store BOTH values, and give them back to you on the >>>>>> next read, so that your application can decide which is the correct one. >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> On Thu, Oct 8, 2015 at 1:56 AM, Vanessa Williams < >>>>>> vanessa.willi...@thoughtwire.ca> wrote: >>>>>> >>>>>>> Hi Dmitri, what would be the benefit of r=2, exactly? It isn't >>>>>>> necessary to trigger read-repair, is it? If it's important I'd rather >>>>>>> try >>>>>>> it sooner than later... >>>>>>> >>>>>>> Regards, >>>>>>> Vanessa >>>>>>> >>>>>>> >>>>>>> >>>>>>> On Wed, Oct 7, 2015 at 4:02 PM, Dmitri Zagidulin < >>>>>>> dzagidu...@basho.com> wrote: >>>>>>> >>>>>>>> Glad you sorted it out! >>>>>>>> >>>>>>>> (I do want to encourage you to bump your R setting to at least 2, >>>>>>>> though. Run some tests -- I think you'll find that the difference in >>>>>>>> speed >>>>>>>> will not be noticeable, but you do get a lot more data resilience with >>>>>>>> 2.) >>>>>>>> >>>>>>>> On Wed, Oct 7, 2015 at 6:24 PM, Vanessa Williams < >>>>>>>> vanessa.willi...@thoughtwire.ca> wrote: >>>>>>>> >>>>>>>>> Hi Dmitri, well...we solved our problem to our satisfaction but it >>>>>>>>> turned out to be something unexpected. >>>>>>>>> >>>>>>>>> The keys were two properties mentioned in a blog post on >>>>>>>>> "configuring Riak’s oft-subtle behavioral characteristics": >>>>>>>>> http://basho.com/posts/technical/riaks-config-behaviors-part-4/ >>>>>>>>> >>>>>>>>> notfound_ok= false >>>>>>>>> basic_quorum=true >>>>>>>>> >>>>>>>>> The 2nd one just makes things a little faster, but the first one >>>>>>>>> is the one whose default value of true was killing us. >>>>>>>>> >>>>>>>>> With r=1 and notfound_ok=true (default) the first node to respond, >>>>>>>>> if it didn't find the requested key, the authoritative answer was >>>>>>>>> "this key >>>>>>>>> is not found". Not what we were expecting at all. >>>>>>>>> >>>>>>>>> With the changed settings, it will wait for a quorum of responses >>>>>>>>> and only if *no one* finds the key will "not found" be returned. >>>>>>>>> Perfect. >>>>>>>>> (Without this setting it would wait for all responses, not ideal.) >>>>>>>>> >>>>>>>>> Now there is only one snag, which is that if the Riak node the >>>>>>>>> client connects to goes down, there will be no communication and we >>>>>>>>> have a >>>>>>>>> problem. This is easily solvable with a load-balancer, though for >>>>>>>>> complicated reasons we actually don't need to do that right now. It's >>>>>>>>> just >>>>>>>>> acceptable for us temporarily. Later, we'll get the load-balancer >>>>>>>>> working >>>>>>>>> and even that won't be a problem. >>>>>>>>> >>>>>>>>> I *think* we're ok now. Thanks for your help! >>>>>>>>> >>>>>>>>> Regards, >>>>>>>>> Vanessa >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> On Wed, Oct 7, 2015 at 9:33 AM, Dmitri Zagidulin < >>>>>>>>> dzagidu...@basho.com> wrote: >>>>>>>>> >>>>>>>>>> Yeah, definitely find out what the sysadmin's experience was, >>>>>>>>>> with the load balancer. It could have just been a wrong >>>>>>>>>> configuration or >>>>>>>>>> something. >>>>>>>>>> >>>>>>>>>> And yes, that's the documentation page I recommend - >>>>>>>>>> http://docs.basho.com/riak/latest/ops/advanced/configs/load-balancing-proxy/ >>>>>>>>>> Just set up HAProxy, and point your Java clients to its IP. >>>>>>>>>> >>>>>>>>>> The drawbacks to load-balancing on the java client side (yes, the >>>>>>>>>> cluster object) instead of a standalone load balancer like HAProxy, >>>>>>>>>> are the >>>>>>>>>> following: >>>>>>>>>> >>>>>>>>>> 1) Adding node means code changes (or at very least, config file >>>>>>>>>> changes) rolled out to all your clients. Which turns out to be a >>>>>>>>>> pretty >>>>>>>>>> serious hassle. Instead, HAProxy allows you to add or remove nodes >>>>>>>>>> without >>>>>>>>>> changing any java code or config files. >>>>>>>>>> >>>>>>>>>> 2) Performance. We've ran many tests to compare performance, and >>>>>>>>>> client-side load balancing results in significantly lower throughput >>>>>>>>>> than >>>>>>>>>> you'd have using haproxy (or nginx). (Specifically, you actually >>>>>>>>>> want to >>>>>>>>>> use the 'leastconn' load balancing algorithm with HAProxy, instead >>>>>>>>>> of round >>>>>>>>>> robin). >>>>>>>>>> >>>>>>>>>> 3) The health check on the client side (so that the java load >>>>>>>>>> balancer can tell when a remote node is down) is much less >>>>>>>>>> intelligent than >>>>>>>>>> a dedicated load balancer would provide. With something like >>>>>>>>>> HAProxy, you >>>>>>>>>> should be able to take down nodes with no ill effects for the client >>>>>>>>>> code. >>>>>>>>>> >>>>>>>>>> Now, if you load balance on the client side and you take a node >>>>>>>>>> down, it's not supposed to stop working completely. (I'm not sure >>>>>>>>>> why it's >>>>>>>>>> failing for you, we can investigate, but it'll be easier to just use >>>>>>>>>> a load >>>>>>>>>> balancer). It should throw an error or two, but then start working >>>>>>>>>> again >>>>>>>>>> (on the retry). >>>>>>>>>> >>>>>>>>>> Dmitri >>>>>>>>>> >>>>>>>>>> On Wed, Oct 7, 2015 at 2:45 PM, Vanessa Williams < >>>>>>>>>> vanessa.willi...@thoughtwire.ca> wrote: >>>>>>>>>> >>>>>>>>>>> Hi Dmitri, thanks for the quick reply. >>>>>>>>>>> >>>>>>>>>>> It was actually our sysadmin who tried the load balancer >>>>>>>>>>> approach and had no success, late last evening. However I haven't >>>>>>>>>>> discussed >>>>>>>>>>> the gory details with him yet. The failure he saw was at the >>>>>>>>>>> application >>>>>>>>>>> level (i.e. failure to read a key), but I don't know a) how he set >>>>>>>>>>> up the >>>>>>>>>>> LB or b) what the Java exception was, if any. I'll find that out in >>>>>>>>>>> an hour >>>>>>>>>>> or two and report back. >>>>>>>>>>> >>>>>>>>>>> I did find this article just now: >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> http://docs.basho.com/riak/latest/ops/advanced/configs/load-balancing-proxy/ >>>>>>>>>>> >>>>>>>>>>> So I suppose we'll give those suggestions a try this morning. >>>>>>>>>>> >>>>>>>>>>> What is the drawback to having the client connect to all 4 nodes >>>>>>>>>>> (the cluster client, I assume you mean?) My understanding from >>>>>>>>>>> reading >>>>>>>>>>> articles I've found is that one of the nodes going away causes that >>>>>>>>>>> client >>>>>>>>>>> to fail as well. Is that what you mean, or are there other >>>>>>>>>>> drawbacks as >>>>>>>>>>> well? >>>>>>>>>>> >>>>>>>>>>> If there's anything else you can recommend, or links other than >>>>>>>>>>> the one above you can point me to, it would be much appreciated. We >>>>>>>>>>> expect >>>>>>>>>>> both node failure and deliberate node removal for upgrade, repair, >>>>>>>>>>> replacement, etc. >>>>>>>>>>> >>>>>>>>>>> Regards, >>>>>>>>>>> Vanessa >>>>>>>>>>> >>>>>>>>>>> On Wed, Oct 7, 2015 at 8:29 AM, Dmitri Zagidulin < >>>>>>>>>>> dzagidu...@basho.com> wrote: >>>>>>>>>>> >>>>>>>>>>>> Hi Vanessa, >>>>>>>>>>>> >>>>>>>>>>>> Riak is definitely meant to run behind a load balancer. (Or, at >>>>>>>>>>>> the worst case, to be load-balanced on the client side. That is, >>>>>>>>>>>> all >>>>>>>>>>>> clients connect to all 4 nodes). >>>>>>>>>>>> >>>>>>>>>>>> When you say "we did try putting all 4 Riak nodes behind a >>>>>>>>>>>> load-balancer and pointing the clients at it, but it didn't help." >>>>>>>>>>>> -- what >>>>>>>>>>>> do you mean exactly, by "it didn't help"? What happened when you >>>>>>>>>>>> tried >>>>>>>>>>>> using the load balancer? >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> On Wed, Oct 7, 2015 at 1:57 PM, Vanessa Williams < >>>>>>>>>>>> vanessa.willi...@thoughtwire.ca> wrote: >>>>>>>>>>>> >>>>>>>>>>>>> Hi all, we are still (for a while longer) using Riak 1.4 and >>>>>>>>>>>>> the matching Java client. The client(s) connect to one node in >>>>>>>>>>>>> the cluster >>>>>>>>>>>>> (since that's all it can do in this client version). The cluster >>>>>>>>>>>>> itself has >>>>>>>>>>>>> 4 nodes (sorry, we can't use 5 in this scenario). There are 2 >>>>>>>>>>>>> separate >>>>>>>>>>>>> clients. >>>>>>>>>>>>> >>>>>>>>>>>>> We've tried both n_val = 3 and n_val=4. We achieve >>>>>>>>>>>>> consistency-by-writes by setting w=all. Therefore, we only >>>>>>>>>>>>> require one >>>>>>>>>>>>> successful read (r=1). >>>>>>>>>>>>> >>>>>>>>>>>>> When all nodes are up, everything is fine. If one node fails, >>>>>>>>>>>>> the clients can no longer read any keys at all. There's an >>>>>>>>>>>>> exception like >>>>>>>>>>>>> this: >>>>>>>>>>>>> >>>>>>>>>>>>> com.basho.riak.client.RiakRetryFailedException: >>>>>>>>>>>>> java.net.ConnectException: Connection refused >>>>>>>>>>>>> >>>>>>>>>>>>> Now, it isn't possible that Riak can't operate when one node >>>>>>>>>>>>> fails, so we're clearly missing something here. >>>>>>>>>>>>> >>>>>>>>>>>>> Note: we did try putting all 4 Riak nodes behind a >>>>>>>>>>>>> load-balancer and pointing the clients at it, but it didn't help. >>>>>>>>>>>>> >>>>>>>>>>>>> Riak is a high-availability key-value store, so... why are we >>>>>>>>>>>>> failing to achieve high-availability? Any suggestions greatly >>>>>>>>>>>>> appreciated, >>>>>>>>>>>>> and if more info is required I'll do my best to provide it. >>>>>>>>>>>>> >>>>>>>>>>>>> Thanks in advance, >>>>>>>>>>>>> Vanessa >>>>>>>>>>>>> >>>>>>>>>>>>> -- >>>>>>>>>>>>> Vanessa Williams >>>>>>>>>>>>> ThoughtWire Corporation >>>>>>>>>>>>> http://www.thoughtwire.com >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>>> _______________________________________________ >>>>>>>>>>>>> riak-users mailing list >>>>>>>>>>>>> riak-users@lists.basho.com >>>>>>>>>>>>> >>>>>>>>>>>>> http://lists.basho.com/mailman/listinfo/riak-users_lists.basho.com >>>>>>>>>>>>> >>>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>> >>>>>>>>> >>>>>>>> >>>>>>> >>>>>> >>>>> >>>>> _______________________________________________ >>>>> riak-users mailing list >>>>> riak-users@lists.basho.com >>>>> http://lists.basho.com/mailman/listinfo/riak-users_lists.basho.com >>>>> >>>>> >>>> >>> >> >
_______________________________________________ riak-users mailing list riak-users@lists.basho.com http://lists.basho.com/mailman/listinfo/riak-users_lists.basho.com