Introduction to `watch` in Redis with C# and StackExchange.Redis

Welcome back! You’ve now learned how to build and execute basic transactions in Redis using StackExchange.Redis in C#. This next lesson takes you a step further by introducing the watch command. This command will help you implement more controlled and conditional transactions. They are essential for scenarios where you need to monitor certain keys and ensure that the operations are completed only when specific conditions are met.

What You'll Learn

In this unit, you will delve into the functionality of the watch command in Redis, utilizing C# and the StackExchange.Redis library. Here’s a quick overview of what you will learn:

  1. Setting Up watch: Understand the importance of monitoring keys to control transaction execution.
  2. Implementing Conditional Updates: Write functions that use watch to implement safer and more conditional updates to your Redis data with C# syntax.
Practical Code Example

Let's take a look at a practical example of how to use watch in your C# code.

using System;
using StackExchange.Redis;

public class RedisExample
{
    private static readonly ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
    private static readonly IDatabase db = redis.GetDatabase();

    public static void UpdateBalance(string userId, int increment)
    {
        var key = $"balance:{userId}";
        while (true)
        {
            try
            {
                var tran = db.CreateTransaction();
                tran.AddCondition(Condition.KeyExists(key));

                var balanceValue = db.StringGet(key);
                int balance = balanceValue.HasValue ? (int)balanceValue : 0;

                tran.StringSetAsync(key, balance + increment);

                bool success = tran.Execute();
                if (success)
                {
                    break;
                }
                else
                {
                    Console.WriteLine("Retrying transaction.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception: {ex.Message}");
            }
        }
    }

    public static void Main(string[] args)
    {
        db.StringSet("balance:1", 100);
        
        UpdateBalance("1", 50);
        
        var value = db.StringGet("balance:1");
        Console.WriteLine($"Updated balance for user 1: {value}");
    }
}

In this example, we start by watching the balance:{userId} key to monitor changes. If another client changes the value before you execute your transaction, the transaction Execute() will fail, and the transaction will retry. This ensures that your balance updates are consistent.

Let's break down each step in the code snippet:

  • We define a method UpdateBalance that takes the userId and increment as arguments.
    • We create a transaction using db.CreateTransaction() to execute multiple commands in a single transaction.
    • Inside the while loop, we add a condition to monitor the balance:{userId} key and ensure that no other client modifies it during the transaction.
    • We retrieve the current balance value using db.StringGet() and set it to 0 if it doesn't exist.
    • We update the balance by adding the increment value to the current balance using tran.StringSetAsync().
    • The tran.Execute() command executes the transaction.
    • If another client changes the balance key before the transaction is executed, Execute() will return false, and the transaction is retried.

Now let's understand how the function is used:

  • We set the initial balance for user 1 to 100.
  • We call the UpdateBalance method with userId="1" and increment=50 to increase the balance by 50.
  • Finally, we retrieve the updated balance value for user 1 and print it.
Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal