<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Georgy Sayganov</title>
    <description>The latest articles on DEV Community by Georgy Sayganov (@sayganov).</description>
    <link>https://dev.to/sayganov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F637359%2Fa11761a9-3a3f-42c4-baef-f2454e4f47d0.jpg</url>
      <title>DEV Community: Georgy Sayganov</title>
      <link>https://dev.to/sayganov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sayganov"/>
    <language>en</language>
    <item>
      <title>Redis keyspace notifications with a C# example</title>
      <dc:creator>Georgy Sayganov</dc:creator>
      <pubDate>Sun, 23 Apr 2023 21:37:30 +0000</pubDate>
      <link>https://dev.to/sayganov/redis-keyspace-notifications-with-a-c-example-2ahp</link>
      <guid>https://dev.to/sayganov/redis-keyspace-notifications-with-a-c-example-2ahp</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;StackExchange.Redis&lt;/code&gt; library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redis configuration
&lt;/h2&gt;

&lt;p&gt;I presume that a Redis instance is already operational locally or elsewhere and that Redis CLI access is already available to you.&lt;/p&gt;

&lt;p&gt;By default keyspace event notifications are disabled and in order to enable it we need to execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config set notify-keyspace-events Kxge$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

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

&lt;h2&gt;
  
  
  C# example
&lt;/h2&gt;

&lt;p&gt;Let's create a simple console application and install the following NuGet package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;StackExchange.Redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can create a new instance of the ConfigurationOptions class and set its EndPoints property to a single Redis endpoint with the address &lt;code&gt;localhost:6379&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configurationOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ConfigurationOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;EndPoints&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"localhost:6379"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right after we need to create a new ConnectionMultiplexer instance and obtain a pub/sub subscriber connection to the specified server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connectionMultiplexer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConnectAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configurationOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSubscriber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;__keyspace@0__:*&lt;/code&gt; prefix in the channel pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SubscribeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"__keyspace@0__:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Set: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"expire"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Expire: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"expired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Expired: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SubscribeAsync&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;To extract a key from a channel we can use a method something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)..];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UnsubscribeAllAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;connectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisposeAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The &lt;code&gt;DisposeAsync&lt;/code&gt; method is then called on the ConnectionMultiplexer object to release any resources used by the Redis connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run and play
&lt;/h2&gt;

&lt;p&gt;Now run the console app and execute the following two Redis CLU commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set mykey 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command is used to set the value of a Redis key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expire mykey 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this one is used to set an expiration time (in seconds) for a Redis key.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;As a result, we will see this in our output console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set: mykey
Expire: mykey
Expired: mykey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;p&gt;There are some potential disadvantages to using this feature. For example, &lt;code&gt;lack of durability&lt;/code&gt;: 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical use
&lt;/h2&gt;

&lt;p&gt;Redis keyspace notifications is a feature that can be used in a variety of ways. Here are some practical uses of Redis keyspace notifications:&lt;/p&gt;

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

&lt;p&gt;But let me know in the comments how you would use Redis keyspace notifications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redis.io/docs/manual/keyspace-notifications/" rel="noopener noreferrer"&gt;Redis keyspace notifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/StackExchange/StackExchange.Redis" rel="noopener noreferrer"&gt;StackExchange.Redis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/sayganov/a57c8290da9539495879cce10669aa2e" rel="noopener noreferrer"&gt;Complete C# example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redis</category>
      <category>cache</category>
      <category>pubsub</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
