Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do basic WATCH with StackExchange.Redis

I would like to do a basic watch with StackExchange.Redis. Where if a key is changed during the transaction, it fails.

StackExchange.Redis has abstracted this nicely to a "Condition" api, which supports concepts of "Equals" and "Exists".

That's really nice, but I would like to just do something like "Unchanged". I might be missing something, but it's not obvious to me at this point on how to do that.

Is it possible to do something like:

var transaction = redis.CreateTransaction();
transaction.AddCondition(Condition.StringUnchanged("key")); //the API here could maybe be simplified
var val = transaction.StringGet("key"); //notably, this is not async because you would have to get the result immediately - it would only work on watched keys
transaction.StringSetAsync("key", val + 1);
transaction.Execute();

Or even a possible better version (which would do the same thing):

var transaction = redis.CreateTransaction();
var val = transaction.Watch("key"); //this would return the value!
transaction.StringSetAsync("key", val + 1);
transaction.Execute();

Currently the only way I understand of doing this would be to do something along the lines of:

var val = redis.StringGet("key");
var transaction = redis.CreateTransaction();
transaction.AddCondition(Condition.StringEqual("key", val));
transaction.StringSetAsync("key", val + 1);
transaction.Execute();

Which from an attempt at reading the SE.Redis code I understand to translate to something like (not sure how accurate this is):

val = GET key
WATCH key
MULTI
val = val + 1
SET key $val
checkVal = GET key
(then if checkVal != val:) UNWATCH
(otherwise:) EXEC

I'm still learning more about Redis, but I'm not quite sure what the benefit of this is. Wouldn't you rather the end result be something more like this?:

WATCH key
MULTI
val = GET key
val = val + 1
SET key $val
EXEC

Or is this not possible with the way SE.Redis works?

like image 604
Ben Wilde Avatar asked Nov 06 '14 21:11

Ben Wilde


1 Answers

The reason WATCH isn't exposed directly is because of how SE.Redis is designed to multiplex commands from different call stacks on a single connection. This makes it necessary for any transaction work to be very tightly managed.

I am unclear of quite what the purpose of "unchanged" would be by itself, without comparison to some known value - otherwise you're just creating a race condition. It would definitely be possible to add support for it, but I'd really like to understand the expected use-case first. Can you explain?


Re your edit; your preferred example (the last one) simply isn't possible with redis - nothing to do with SE.Redis; if you do a GET inside a MULTI, you don't get the answer until the EXEC completes - so you can't possibly use the value in the SET: it isn't available yet.

If it wasn't for multiplexing, you could re-order your second example (based on what SE.Redis does) a bit:

WATCH key
val = GET key
MULTI
val = val + 1
SET key $val
EXEC

this is the typical use of WATCH: you watch the things that you're querying in advance, then you know that {key} is unchanged during this loop (or at least, the transaction will have aborted; no inconsistent state). However, WATCH does not play well with a multiplexer, which is why SE.Redis forces you down the route of fetching the value in advance of the transaction, then allowing you to compare the value to assert that it is unchanged. Same result; slightly different approach, but it is multiplexer-safe. For more on that topic see here.

like image 184
Marc Gravell Avatar answered Nov 15 '22 01:11

Marc Gravell