Get started with Azure Redis Cache in .Net

Redis cashe “replaces” the older managed cache (not really – you can still create it if you want to). Redis cache is at least the new cache in the Azure and should be treated as the primary option for using cache in your cloud appliactions.

Redis is a key-value store, where a single key can contain different data sources like strings, hashes, lists, sets and sorted sets. It comes from the open source community and manages authentication, synchonization. It is also possible to use from several programming languages and not just web sites – consider RemoteApps, any VM deployed application, WebJobs etc.

It comes in 3 different basic main sizes

  • Basic: Single non SLA’ed instance for non-production instances
  • Standard where two instances are keeping the cache on a primary instance replicated to the secondary instance. This one comes with a high SLA.
  • Premium: Faster, bigger, disaster recovery, enhanced security, snapshots and backups. Supports sharding of data across nodes so you can support more than 53GB of cache with greater performance. You can also deploy it in a single VNet for application isolation and enhanced security.
  • A few of the available cache sizes
    A few of the available cache sizes

As you can see from above the smaller instances are actually not very expensive (USD and EUR cost is approximately 10% of SEK). Note that there are multiple sizes within each level with diferent costs and that for the Premium cache the price is per shard not per cache. Calculate your needs what will give you the best/price performance for your needs. Remember that scale-out is often more cost efficient than scale-up (selecting the biggest instances).

Using redis cache is quite simple as most other caching mechanisms (at least for the simpler functionality). The post will make a simple example and will show some different usages.

Set up Redis Cache in Azure

Enter the preview portal (even though you could just as well create this through Azure powershell also if you’re into that). Select Data+Storage and scroll down to Redis Cache.

Setup_1

As usual enter the needed properties (name, subscription, select resource group, location and size) – fairly standard.

Set up your cache
Set up your cache

It will take some minutes to create the Redis Cache. But once it is done we should look at it in the portal and remember some values for use later.

ResisSettings1

We see the hostname which we will need later. We also see that the non SSL port is disabled by default (you can change this on the Access Ports section of the settings if needed but I’ll use the SSL version.

We also need the access keys to access the cache. Press the “Show access keys…” link to display the access keys.

ResisSettings2

Copy the access key to somewhere (notepad anyone?)…

Use in .Net

  1. First create a web project in Visual Studio.
  2. Once the project is started right click on the web project and select “Manage NuGet Packages”
  3. Enter StackExchange.Redis and install it DotNet
  4. Change the default.aspx content placeholder to this:

    <div class=”form-group”><label for=”txtArticleNumber”>Enter article number</label><asp:TextBox ID=”txtArticleNumber” CssClass=”form-control” runat=”server”></asp:TextBox>

    <asp:Button ID=”btnSearch” CssClass=”btn-primary” runat=”server” Text=”Open” OnClick=”btnSearch_Click” />

    </div>

    <div class=”form-group”>

    <label for=”txtArticleInfo”>Article info</label>

    <asp:TextBox ID=”txtArticleInfo” runat=”server” CssClass=”form-control” Rows=”5″ TextMode=”MultiLine”></asp:TextBox>

    </div>

    <div class=”row”>

    <div class=”col-lg-12″>

    <asp:Label ID=”lblStatus” runat=”server” Text=”statustext”></asp:Label></div>

    </div>

    The web app
    The web app
  5. Double click on the button to create the event handler. We are now going to load articles from and to cache. The basic idea if the article is in the cache then use the cache version otherwise “fetch it from the database”.
  6. Modify the page
    1. First we need to create a connection.
      1. declare a variable ConnectionMultiplexer redisConn;
      2. Assign the connection in the page load (in this example) with the correct parameters
    2. In the button click event
      1. Create a cache object
      2. read and write to the cache in the button click event

Full code below

public partial class _Default : Page{

ConnectionMultiplexer redisConn;

protected void Page_Load(object sender, EventArgs e) {

redisConn = ConnectionMultiplexer.Connect(“TheFastDemo.redis.cache.windows.net,abortConnect=false,ssl=true,password=myaccesskeyreplacewithyour”);

}

protected void btnSearch_Click(object sender, EventArgs e){

DateTime start = DateTime.Now;

IDatabase cacheLookup = redisConn.GetDatabase();

RedisValue res = cacheLookup.StringGet(txtArticleNumber.Text);

if (!res.IsNull)

{

//Use cache

Article articleFound = JsonConvert.DeserializeObject<Article>(res);

txtArticleInfo.Text = “From Cache: “ + articleFound.ArticleNumber + ” – “ + articleFound.ArticleNameShort + ” – “ + articleFound.ArticleLong;

}

else

{

//Load from database

Article artFromDB = GetArticleFromDB(txtArticleNumber.Text);

txtArticleInfo.Text = “From DB: “ + artFromDB.ArticleNumber + ” – “ + artFromDB.ArticleNameShort + ” – “ + artFromDB.ArticleLong;

//Save to cache

cacheLookup.StringSet(txtArticleNumber.Text, JsonConvert.SerializeObject(artFromDB));

}

DateTime end = DateTime.Now;

TimeSpan diff = end – start;

lblStatus.Text = “result in “ + diff.ToString();

}

private Article GetArticleFromDB(string articleNo){

System.Threading.Thread.Sleep(2000); //Simulate lookup

Article fetchedArt = new Article() { ArticleNumber = long.Parse(articleNo), ArticleNameShort = “Loret ipsum Short “ + articleNo, ArticleLong = “Loret ipsum Long “ + articleNo };

fetchedArt.SalesCountries.Add(new Country() { CountryCode = “SE”, CountryName = “Sweden” });

fetchedArt.SalesCountries.Add(new Country() { CountryCode = “FR”, CountryName = “France” });

return fetchedArt;

}

}

}

public class Article {

private List<Country> _salesCountries = new List<Country>();

public long ArticleNumber { get; set; }

public string ArticleNameShort { get; set; }

public string ArticleLong { get; set; }

public List<Country> SalesCountries {

get { return _salesCountries; }

set { _salesCountries = value; }

}

}

public class Country

{

public string CountryCode { get; set; }

public string CountryName { get; set; }

}

The program now tries to load the article from cache if it is there – alternatively loads from the “database” and adds to the cache so the next call is fetched from the cache. Note also that the cache, like expected, remains in memory if you restart your web application.

You can manually edit the timespan for how long the data is persisted if untouched in redis cache by tweaking the StringSet command. However – by default – the data is not actually removed just because you set a value. See section about Expiry conditions Redis Cache. So the row below might not have any effect and will likely remain after 5 minutes also.

cacheLookup.StringSet(txtArticleNumber.Text, JsonConvert.SerializeObject(artFromDB), new TimeSpan(0,5,0), When.Always)

The when parameter determines under what condition the key data should be saved, other alternatives are When.Exists and When.NotExists. See separate section for clearer explanation of the when parameter.

Working with sets

You can also add multiple strings to a set and work with the sets in the code

//load allowed colors to cache cacheLookup.SetAdd(“allowedColorsDropDown”, “Red”);

cacheLookup.SetAdd(“allowedColorsDropDown”, “Blue”);

cacheLookup.SetAdd(“allowedColorsDropDown”, “Green”);

//Loop all values

foreach (var res2 in cacheLookup.SetScan(“allowedColorsDropDown”))

{ System.Diagnostics.Debug.WriteLine(res2); };

Sorted Sets

You can have Redis Cache sort values for you. The following adds players and their goal scoring numbers to a list and keeps it sorted based on the number of goals they have scored.

internal class Player {public string Name { get; set; }

public int Scores { get; set; }

}

Player p1 = new Player() { Name = “Francesco Totti”, Scores = 10000 };

Player p2 = new Player() { Name = “Alessandro Del Piero”, Scores = 1 };

Player p3 = new Player() { Name = “Gabriel Batistuta”, Scores = 20000 };

cacheLookup.SortedSetAdd(“scorerList”, JsonConvert.SerializeObject(p1),p1.Scores);

cacheLookup.SortedSetAdd(“scorerList”, JsonConvert.SerializeObject(p2), p2.Scores);

cacheLookup.SortedSetAdd(“scorerList”, JsonConvert.SerializeObject(p3), p3.Scores);

//Loop all values

foreach (var res2 in cacheLookup.SortedSetScan(“scorerList”)) {

Player temp = JsonConvert.DeserializeObject<Player>(res2.Element);

System.Diagnostics.Debug.WriteLine(temp.Name);

};

 

The result in the immediate window is

  • Allesandro Del Piero
  • Francesco Totti
  • Gabriel Batistuta

You can remove individual members of the Sets also…

cacheLookup.SortedSetRemove(“scorerList”, JsonConvert.SerializeObject(p2));

which will effectively remove one of the values from the SortedSet.

Working with other datatypes

Though the key is absoultely a string you can absolutely work with other datatypes as well. The below code saves and reads integers and dobles from/to redis cache.

cacheLookup.StringSet(“myInteger”, int.MaxValue);

cacheLookup.StringSet(“myDouble”, double.MaxValue);

int readIntFromCache = (int)cacheLookup.StringGet(“myInteger”);

double readDoubleFromCache = (double)cacheLookup.StringGet(“myDouble”);

Understanding the When parameter

You can use the When parameter to set the condition for when you want to set the Cache key value or not. When.Always will always set the value while When.NotExist will only set it it the key does not exist, and When.Exists will only set it if the Key already exists.

int readIntFromCache = 1000; cacheLookup.StringSet(“myInteger”, 1000, new TimeSpan(0, 5, 0), When.Always, CommandFlags.None);

readIntFromCache = (int)cacheLookup.StringGet(“myInteger”);

Debug.WriteLine(“Startvalue=” + readIntFromCache.ToString());

cacheLookup.StringSet(“myInteger”, readIntFromCache – 1,new TimeSpan(0,5,0), When.NotExists,CommandFlags.None);

readIntFromCache = (int)cacheLookup.StringGet(“myInteger”);

Debug.WriteLine(“Same value as it will only set value if not present: “ + readIntFromCache.ToString());

cacheLookup.StringSet(“myInteger”, readIntFromCache – 1, new TimeSpan(0, 5, 0), When.Exists, CommandFlags.None);

readIntFromCache = (int)cacheLookup.StringGet(“myInteger”);

Debug.WriteLine(“Recuce the valus as it had contition that string existed:” + readIntFromCache.ToString());

cacheLookup.StringSet(“myInteger”, readIntFromCache – 1, new TimeSpan(0, 5, 0), When.Always, CommandFlags.None);

readIntFromCache = (int)cacheLookup.StringGet(“myInteger”);

Debug.WriteLine(“Always will always run: “ +readIntFromCache.ToString());

The output window will display

  • Startvalue: 1000
  • Same value as it will only set value if not present: 1000
  • Recuce the valus as it had contition that string existed: 999
  • Always will always run: 998

Note: Incrementing/decrementing values can be done more efficiently with:

cacheLookup.StringIncrement(“myInteger”, -1, CommandFlags.None);

 

Alerts & Monitoring

It is easy to turn on Monitoring of your cache.

Turn on Azure Redis Cache Monitoring

You select a storage account (must be same region as the cache). You can thereafter get lots of usefull information about the usage of the cache. When it is activated you can trace the usage of the redis Cache.

Part of the Redis Cache information
Part of the Redis Cache information

 

You can also set up alert rules in the cache. It could be about used memory, number of keys and a lot of other things. The example below sets up a rule that will alert when the number of keys are > 100.

Setup alert on Redis Cache
Setup alert on Redis Cache

Expiry – Redis Cache

Expiry of keys in redis cache is by default Never done unless you are short of memory in your Redis Cache, even then it all depends on the evicion policy of your Azure Redis Cache. You can tweak this in the settings of the portal. But first you have to understand what options you have available (the following is taken form the Redis documentation (read in full http://redis.io/topics/lru-cache ) :

“The following policies are available:

  • noeviction: return errors when the memory limit was reached and the client is trying to execute commands that could result in more memory to be used (most write commands, but DEL and a few more exceptions).
  • allkeys-lru: evict keys trying to remove the less recently used (LRU) keys first, in order to make space for the new data added.
  • volatile-lru: evict keys trying to remove the less recently used (LRU) keys first, but only among keys that have an expire set, in order to make space for the new data added.
  • allkeys-random: evict random keys in order to make space for the new data added.
  • volatile-random: evict random keys in order to make space for the new data added, but only evict keys with an expire set.
  • volatile-ttl: In order to make space for the new data, evict only keys with an expire set, and try to evict keys with a shorter time to live (TTL) first.

The policies volatile-lru, volatile-random and volatile-ttl behave like noeviction if there are no keys to evict matching the prerequisites.

In general as a rule of thumb:

  • Use the allkeys-lru policy when you expect a power-law distribution in the popularity of your requests, that is, you expect that a subset of elements will be accessed far more often than the rest. This is a good pick if you are unsure.”

So it we want a traditional cache that throws away old cache when needed it is recommended that you set the allkeys-lru. So we can do that in the portal.

LastRecentlyUsed for all keys

But carefully look at the other options also they might suit your needs better.

IMPORTANT: Default is volitile-lru which only evicts keys with eviction time set.

 

More features

For ASP.Net you can set up Redis Cache to act as a session state provider. Remember though that “state kills scalability” even though using a fast cacheing  mechanism makes the performance hit lower. Read about the ASP.NET Session State Provider for Azure Redis Cache here https://azure.microsoft.com/en-gb/documentation/articles/cache-asp.net-session-state-provider/).

You can also use the ASP.NET Output Cache Provider for Azure Redis Cache for storing preprocessed pages for reuse in later requests  (see https://azure.microsoft.com/en-gb/documentation/articles/cache-asp.net-output-cache-provider/).

The StackExchange.Redis classes contains many more features like transactions and pub/sub scenarios. Read the full documentation here https://github.com/StackExchange/StackExchange.Redis.

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s