DEV Community

Cover image for Redis keyspace notifications with a C# example
Georgy Sayganov
Georgy Sayganov

Posted on • Edited on

Redis keyspace notifications with a C# example

Intro

One of Redis' key features is its ability to trigger notifications based on key events happening in its keyspace, allowing for real-time updates. It could be a useful feature for constructing scalable and reliable systems that have to react quickly to changes in data.

In this blog post, we will explore Redis keyspace notifications and demonstrate how to use them in a C# application. We will cover the different types of events that can trigger notifications, how to configure Redis to send notifications, and how to receive and process notifications in a C# application using the StackExchange.Redis library.

Redis configuration

I presume that a Redis instance is already operational locally or elsewhere and that Redis CLI access is already available to you.

By default keyspace event notifications are disabled and in order to enable it we need to execute the following command:

config set notify-keyspace-events Kxge$
Enter fullscreen mode Exit fullscreen mode

Each letter in the Kxge$ argument represents a different type of Redis event that should be published to the Pub/Sub mechanism:

  • K: Keyspace events for string commands (e.g. SET, GET)
  • x: Keyspace events for expired events (e.g. expired keys)
  • g: Keyspace events for generic commands (e.g. DEL, EXPIRE)
  • e: Keyspace events for events related to keys that are being modified by an EVAL command or a Lua script
  • $: Keyspace events for string commands received via Redis' LRANGE and LTRIM commands

This is pretty much it. Later, after the C# example part, we are going to execute a couple of more Redis CLI commands to set and expire a key.

C# example

Let's create a simple console application and install the following NuGet package:

StackExchange.Redis
Enter fullscreen mode Exit fullscreen mode

Then we can create a new instance of the ConfigurationOptions class and set its EndPoints property to a single Redis endpoint with the address localhost:6379.

var configurationOptions = new ConfigurationOptions
{
    EndPoints =
    {
        "localhost:6379"
    }
};
Enter fullscreen mode Exit fullscreen mode

Right after we need to create a new ConnectionMultiplexer instance and obtain a pub/sub subscriber connection to the specified server.

var connectionMultiplexer = await ConnectionMultiplexer.ConnectAsync(configurationOptions);
var subscriber = connectionMultiplexer.GetSubscriber();
Enter fullscreen mode Exit fullscreen mode

Now the most interesting part. Here we are subscribing to Redis keyspace notifications. Specifically, it is subscribing to keyspace events that occur on Redis database 0 (zero) by using the __keyspace@0__:* prefix in the channel pattern.

await subscriber.SubscribeAsync("__keyspace@0__:*", (channel, type) =>
{
    var key = GetKey(channel);

    switch (type)
    {
        case "set":
            Console.WriteLine($"Set: {key}");
            break;
        case "expire":
            Console.WriteLine($"Expire: {key}");
            break;
        case "expired":
            Console.WriteLine($"Expired: {key}");
            break;
    }
});
Enter fullscreen mode Exit fullscreen mode

The SubscribeAsync method of the Redis subscriber object is used to subscribe to the keyspace events, and it takes a delegate function that will be called whenever a keyspace event occurs on a key that matches the subscription pattern.

The switch statement then handles each type of keyspace event that may occur and writes a message to the console indicating the type of event and the key that was affected by the event. In our example, we are catching only 3 types: set, expire, and expired. But there are many more, you can find the complete list on the Redis docs website.

To extract a key from a channel we can use a method something like this:

static string GetKey(string channel)
{
    var index = channel.IndexOf(':');

    if (index >= 0 && index < channel.Length - 1)
    {
        return channel[(index + 1)..];
    }

    return channel;
}
Enter fullscreen mode Exit fullscreen mode

Finally, let's add this:

await subscriber.UnsubscribeAllAsync();
await connectionMultiplexer.DisposeAsync();
Enter fullscreen mode Exit fullscreen mode

The UnsubscribeAllAsync method is called on the Redis subscriber object to unsubscribe from all the channels that have been subscribed to using the SubscribeAsync method. This method is used to terminate the subscription and stop listening for events.

The DisposeAsync method is then called on the ConnectionMultiplexer object to release any resources used by the Redis connection.

Run and play

Now run the console app and execute the following two Redis CLU commands:

set mykey 0
Enter fullscreen mode Exit fullscreen mode

The command is used to set the value of a Redis key.

expire mykey 10
Enter fullscreen mode Exit fullscreen mode

And this one is used to set an expiration time (in seconds) for a Redis key.

Once the expire command is executed, Redis will start a timer for the key, and after 10 seconds, the key will be automatically removed from the database.

As a result, we will see this in our output console:

Set: mykey
Expire: mykey
Expired: mykey
Enter fullscreen mode Exit fullscreen mode

Disadvantages

There are some potential disadvantages to using this feature. For example, lack of durability: keyspace notifications are not guaranteed to be delivered to clients in the event of a network failure or server crash. This can lead to data loss or inconsistency.

Practical use

Redis keyspace notifications is a feature that can be used in a variety of ways. Here are some practical uses of Redis keyspace notifications:

  • Cache Invalidation: it can be used to invalidate cached data whenever the corresponding key is updated or deleted.
  • Real-time Analytics: it can be used to trigger real-time analytics and reporting whenever new data is added to the database.

But let me know in the comments how you would use Redis keyspace notifications.

Links

Top comments (0)