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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With