Redis Transactions
Redis provides a way to do Transactions, but the transactions are somewhat different than what you know from a SQL Relational Database perspective. In SQL Relation Database world you can executed partially and then rolled back the entire transaction. While Redis uses the MULTI and EXEC commands where every command passed as part of a transaction is executed one after another until they have all completed and only after they have completed then other clients may execute their commands. You can think of it more like a queue that stores all your commands and when you call EXEC it loops through the queue and execute the task on the queue one after the other while being locked in the loop i,e no other thread could come in.
Below is an example of a Client queuing up the calls to Redis using MULTI in Redis client library to make a call to Redis Server to process the commands in an EXEC.
Note: You see Client2 also making a call to Redis but the command is only executed after the process of Client commands.
One important thing to note is when using Redis Transaction one cannot make decisions inside the block, but you can use WATCH to prevent a key to be changed in a transaction. E.g We can WATCH the key “A” in our sample and when we try to use it in MULTI and EXEC it will fail to change A. A will be discarded as shown below, when A was set as 4, even inside of the block we were not able to make the change the EXEC was in some way ROLLBACKED i.e DISCARD
127.0.0.1:6379> WATCH A
OK
127.0.0.1:6379> INCR A
(integer) 4
127.0.0.1:6379> GET A
"4"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR A
QUEUED
127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379>
127.0.0.1:6379> GET A
"4"
In Redis StackExchange library uses Conditions to mimic Watch since Redis StackExchange uses Multiplex. Commands can come in different stages but conditions can be used to detect changes.
C# code using Redis Transactions
class Program
{
static void Main(string[] args)
{
var redis = RedisStore.RedisCache;
RedisKey alphaKey = "alphaKey";
RedisKey betaKey = "betaKey";
RedisKey gammaKey = "gammaKey";
redis.KeyDelete(alphaKey, CommandFlags.FireAndForget);
redis.KeyDelete(betaKey, CommandFlags.FireAndForget);
redis.KeyDelete(gammaKey, CommandFlags.FireAndForget);
var trans = redis.CreateTransaction();
var incr = trans.StringSetAsync(alphaKey, "abc");
var exec = trans.ExecuteAsync();
var result = redis.Wait(exec);
var alphaValue = redis.StringGet(alphaKey);
Console.WriteLine($"Alpha key is {alphaValue} and result is {result}");
//using conditions to watch keys
var condition = trans.AddCondition(Condition.KeyNotExists(gammaKey));
var keyIncrement = trans.StringIncrementAsync(gammaKey);
exec = trans.ExecuteAsync();
result = redis.Wait(exec);
var gammaValue = redis.StringGet(gammaKey);
Console.WriteLine($"Gamma key is {gammaValue} and result is {result}");
//fail condition
condition = trans.AddCondition(Condition.KeyNotExists(gammaKey));
keyIncrement = trans.StringIncrementAsync(gammaKey);
exec = trans.ExecuteAsync();
//resultis false
result = redis.Wait(exec);
gammaValue = redis.StringGet(gammaKey);
//value is still 1 and result is false
Console.WriteLine($"Gamma key is {gammaValue} and result is {result}");
Console.ReadKey();
}
}
So this covers the basic usage of Redis Transactions, in the next blog post I will cover how to use Lua in Redis.
For the code please visit
https://github.com/taswar/RedisForNetDevelopers/tree/master/11.RedisTransaction
For previous Redis topics
- Intro to Redis for .NET Developers
- Redis for .NET Developer – Connecting with C#
- Redis for .NET Developer – String Datatype
- Redis for .NET Developer – String Datatype part 2
- Redis for .NET Developer – Hash Datatype
- Redis for .NET Developer – List Datatype
- Redis for .NET Developer – Redis Sets Datatype
- Redis for .NET Developer – Redis Sorted Sets Datatype
- Redis for .NET Developer – Redis Hyperloglog
- Redis for .NET Developer – Redis Pub Sub
- Redis for .NET Developers – Redis Pipeline Batching