Azure implementing caching

The following is from Azure Developer Training lab for AZ-203

Azure Cache for Redis overview

Azure Cache for Redis is based on the popular software Redis. It is typically used as a cache to improve the performance and scalability of systems that rely heavily on backend data-stores. Performance is improved by temporarily copying frequently accessed datato fast storage located close to the application. With Azure Cache for Redis, this fast storage is located in-memory with Azure Cache for Redis instead of being loaded from disk by a database.

Azure Cache for Redis can also be used as an in-memory data structure store, distributed non-relational database, and message broker. Application performance is improved by taking advantage of the low-latency, high-throughput performance of the Redisengine.

Azure Cache for Redis gives you access to a secure, dedicated Azure Cache for Redis, managed by Microsoft, hosted within Azure, and accessible to any application within or outside of Azure.

What type of data can be stored in the cache?

Redis supports a variety of data types all oriented around binary safe strings. This means that you can use any binary sequence for a value, from a string like “i-love-rocky-road” to the contents of an image file. An empty string is also a valid value.

  • Binary-safe strings (most common)
  • Lists of strings
  • Unordered sets of strings
  • Hashes
  • Sorted sets of strings
  • Maps of strings

Each data value is associated to a key which can be used to lookup the value from the cache. Redis works best with smaller values (100k or less), so consider chopping up bigger data into multiple keys. Storing larger values is possible (up to 500 MB), butincreases network latency and can cause caching and out-of-memory issues if the cache isn’t configured to expire old values.

What is a Redis key?

Redis keys are also binary safe strings. Here are some guidelines for choosing keys:

  • Avoid long keys. They take up more memory and require longer lookup times because they have to be compared byte-by-byte. If you want to use a binary blob as the key, generate a unique hash and use that as the key instead. The maximum size of a key is 512MB, but you should never use a key that size.
  • Use keys which can identify the data. For example, “sport:football;date:2008-02-02” would be a better key than “fb:8-2-2”. The former is more readable and the extra size is negligible. Find the balance between size and readability.
  • Use a convention. A good one is “object:id”, as in “sport:football”.

How is data stored in a Redis cache?

Data in Redis is stored in nodes and clusters.

Nodes are a space in Redis where your data is stored.

Clusters are sets of three or more nodes your dataset is split across. Clusters are useful because your operations will continue if a node fails or is unable to communicate to the rest of the cluster.

What are Redis caching architectures?

Redis caching architecture is how we distribute our data in the cache. Redis distributes data in three major ways:

  1. Single node
  2. Multiple node
  3. Clustered

Redis caching architectures are split across Azure by tiers:

  • Basic cache: A basic cache provides you with a single node Redis cache. The complete dataset will be stored in a single node. This tier is ideal for development, testing, and non-critical workloads.
  • Standard cache: The standard cache creates multiple node architectures. Redis replicates a cache in a two-node primary/secondary configuration. Azure manages the replication between the two nodes. This is a production-ready cache with master/slavereplication.
  • Premium tier: The premium tier includes the features of the standard tier but adds the ability to persist data, take snapshots, and back up data. With this tier, you can create a Redis cluster that shards data across multiple Redis nodes to increase availablememory. The premium tier also supports an Azure Virtual Network to give you complete control over your connections, subnets, IP addressing, and network isolation. This tier also includes geo-replication, so you can ensure your data is close to the appthat’s consuming it.

Summary

A database is great for storing large amounts of data, but there is an inherent latency when looking up data. You send a query. The server interprets the query, looks up the data, and returns it. Servers also have capacity limits for handling requests. If too manyrequests are made, data retrieval will likely slow down. Caching will store frequently requested data in memory that can be returned faster than querying a database, which should lower latency and increase performance. Azure Cache for Redis gives you accessto a secure, dedicated, and scalable Redis cache, hosted in Azure, and managed by Microsoft.

Configure Azure Cache for Redis

Create and configure the Azure Cache for Redis instance

You can create a Redis cache using the Azure portal, the Azure CLI, or Azure PowerShell. There are several parameters you will need to decide in order to configure the cache properly for your purposes.

Name

The Redis cache will need a globally unique name. The name has to be unique within Azure because it is used to generate a public-facing URL to connect and communicate with the service.

The name must be between 1 and 63 characters, composed of numbers, letters, and the ‘-’ character. The cache name can’t start or end with the ‘-‘ character, and consecutive ‘-’ characters aren’t valid.

Resource Group

The Azure Cache for Redis is a managed resource and needs a resource group owner. You can either create a new resource group, or use an existing one in a subscription you are part of.

Location

You will need to decide where the Redis cache will be physically located by selecting an Azure region. You should always place your cache instance and your application in the same region. Connecting to a cache in a different region can significantly increaselatency and reduce reliability. If you are connecting to the cache outside of Azure, then select a location close to where the application consuming the data is running.

Important: Put the Redis cache as close to the data consumer as you can.

Pricing tier

As mentioned in the last unit, there are three pricing tiers available for an Azure Cache for Redis.

  • Basic: Basic cache ideal for development/testing. Is limited to a single server, 53 GB of memory, and 20,000 connections. There is no SLA for this service tier.
  • Standard: Production cache which supports replication and includes an 99.99% SLA. It supports two servers (master/slave), and has the same memory/connection limits as the Basic tier.
  • Premium: Enterprise tier which builds on the Standard tier and includes persistence, clustering, and scale-out cache support. This is the highest performing tier with up to 530 GB of memory and 40,000 simultaneous connections.

You can control the amount of cache memory available on each tier – this is selected by choosing a cache level from C0-C6 for Basic/Standard and P0-P4 for Premium. Check the pricing page for full details.

Tip: Microsoft recommends you always use Standard or Premium Tier for production systems. The Basic Tier is a single node system with no data replication and no SLA. Also, use at least a C1 cache. C0 caches are really meant for simple dev/test scenarios sincethey have a shared CPU core and very little memory.

The Premium tier allows you to persist data in two ways to provide disaster recovery:

  1. RDB persistence takes a periodic snapshot and can rebuild the cache using the snapshot in case of failure.
  2. AOF persistence saves every write operation to a log that is saved at least once per second. This creates bigger files than RDB but has less data loss.

There are several other settings which are only available to the Premium tier.

Virtual Network support

If you create a premium tier Redis cache, you can deploy it to a virtual network in the cloud. Your cache will be available to only other virtual machines and applications in the same virtual network. This provides a higher level of security when your service andcache are both hosted in Azure, or are connected through an Azure virtual network VPN.

Clustering support

With a premium tier Redis cache, you can implement clustering to automatically split your dataset among multiple nodes. To implement clustering, you specify the number of shards to a maximum of 10. The cost incurred is the cost of the original node, multipliedby the number of shards.

Accessing the Redis instance

Redis supports a set of known commands. A command is typically issued as COMMAND parameter1 parameter2 parameter3.

Here are some common commands you can use:

Command Description
ping Ping the server. Returns “PONG”.
set [key] [value] Sets a key/value in the cache. Returns “OK” on success.
get [key] Gets a value from the cache.
exists [key] Returns ‘1’ if the key exists in the cache, ‘0’ if it doesn’t.
type [key] Returns the type associated to the value for the given key.
incr [key] Increment the given value associated with key by ‘1’. The value must be an integer or double value. This returns the new value.
incrby [key] [amount] Increment the given value associated with key by the specified amount. The value must be an integer or double value. Thisreturns the new value.
del [key] Deletes the value associated with the key.
flushdb Delete all keys and values in the database.

Redis has a command-line tool (redis-cli) you can use to experiment directly with these commands. Here are some examples.

> set somekey somevalue OK > get somekey “somevalue” > exists somekey (string) 1 > del somekey (string) 1 > exists somekey (string) 0

Here’s an example of working with the INCR commands. These are convenient because they provide atomic increments across multiple applications that are using the cache.

> set counter 100 OK > incr counter (integer) 101 > incrby counter 50 (integer) 151 > type counter (integer)

Adding an expiration time to values

Caching is important because it allows us to store commonly used values in memory. However, we also need a way to expire values when they are stale. In Redis this is done by applying a time to live (TTL) to a key.

When the TTL elapses, the key is automatically deleted, exactly as if the DEL command were issued. Here are some notes on TTL expirations.

  • Expirations can be set using seconds or milliseconds precision.
  • The expire time resolution is always 1 millisecond.
  • Information about expires are replicated and persisted on disk, the time virtually passes when your Redis server remains stopped (this means that Redis saves the date at which a key will expire).

Here is an example of an expiration:

> set counter 100 OK > expire counter 5 (integer) 1 > get counter 100 … wait … > get counter (nil)

Accessing a Redis cache from a client

To connect to an Azure Cache for Redis instance, you’ll need several pieces of information. Clients need the host name, port, and an access key for the cache. You can retrieve this information in the Azure portal through the Settings > Access Keys page.

  • The host name is the public Internet address of your cache, which was created using the name of the cache. For example sportsresults.redis.cache.windows.net.
  • The access key acts as a password for your cache. There are two keys created: primary and secondary. You can use either key, two are provided in case you need to change the primary key. You can switch all of your clients to the secondary key, andregenerate the primary key. This would block any applications using the original primary key. Microsoft recommends periodically regenerating the keys – much like you would your personal passwords.

Warning: Your access keys should be considered confidential information, treat them like you would a password. Anyone who has an access key can perform any operation on your cache!

Use the client API to interact with Redis

Redis is an in-memory NoSQL database which can be replicated across multiple servers. It is often used as a cache, but can be used as a formal database or even message-broker.

It can store a variety of data types and structures and supports a variety of commands you can issue to retrieve cached data or query information about the cache itself. The data you work with is always stored as key/value pairs.

Executing commands on the Redis cache

Typically, a client application will use a client library to form requests and execute commands on a Redis cache. You can get a list of client libraries directly from the Redis clients page. A popular high-performance Redis client for the .NET language isStackExchange.Redis. The package is available through NuGet and can be added to your .NET code using the command line or IDE.

Connecting to your Redis cache with StackExchange.Redis

Recall that we use the host address, port number, and an access key to connect to a Redis server. Azure also offers a connection string for some Redis clients which bundles this data together into a single string.

A connection string is a single line of text that includes all the required pieces of information to connect and authenticate to a Redis cache in Azure. It will look something like the following (with the cache-name and password-here fields filled in with realvalues):

[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False

You can pass this string to StackExchange.Redis to create a connection the server.

Notice that there are two additional parameters at the end:

  • ssl – ensures that communication is encrypted.
  • abortConnection – allows a connection to be created even if the server is unavailable at that moment.

There are several other optional parameters you can append to the string to configure the client library.

Tip: The connection string should be protected in your application. If the application is hosted on Azure, consider using an Azure Key Vault to store the value.

Creating a connection

The main connection object in StackExchange.Redis is the StackExchange.Redis.ConnectionMultiplexer class. This object abstracts the process of connecting to a Redis server (or group of servers). It’s optimized to manage connections efficientlyand intended to be kept around while you need access to the cache.

You create a ConnectionMultiplexer instance using the static ConnectionMultiplexer.Connect or ConnectionMultiplexer.ConnectAsync method, passing in either a connection string or a ConfigurationOptions object.

Here’s a simple example:

using StackExchange.Redis; … var connectionString = “[cache-name].redis.cache.windows.net:6380,password=[password-here],ssl=True,abortConnect=False”; var redisConnection = ConnectionMultiplexer.Connect(connectionString); // ^^^ store and re-use this!!!

Once you have a ConnectionMultiplexer, there are 3 primary things you might want to do:

  1. Access a Redis Database. This is what we will focus on here.
  2. Make use of the publisher/subscript features of Redis. This is outside the scope of this module.
  3. Access an individual server for maintenance or monitoring purposes.
Accessing a Redis database

The Redis database is represented by the IDatabase type. You can retrieve one using the GetDatabase() method:

IDatabase db = redisConnection.GetDatabase();

Tip: The object returned from GetDatabase is a lightweight object, and does not need to be stored. Only the ConnectionMultiplexer needs to be kept alive.

Once you have a IDatabase object, you can execute methods to interact with the cache. All methods have synchronous and asynchronous versions which return Task objects to make them compatible with the async and await keywords.

Here is an example of storing a key/value in the cache:

bool wasSet = db.StringSet(“favorite:flavor”, “i-love-rocky-road”);

The StringSet method returns a bool indicating whether the value was set (true) or not (false). We can then retrieve the value with the StringGet method:

string value = db.StringGet(“favorite:flavor”); Console.WriteLine(value); // displays: “”i-love-rocky-road””

Getting and Setting binary values

Recall that Redis keys and values are binary safe. These same methods can be used to store binary data. There are implicit conversion operators to work with byte[] types so you can work with the data naturally:

byte[] key = …; byte[] value = …; db.StringSet(key, value);

byte[] key = …; byte[] value = db.StringGet(key);

StackExchange.Redis represents keys using the RedisKey type. This class has implicit conversions to and from both string and byte[], allowing both text and binary keys to be used without any complication. Values are represented by theRedisValuetype. As with RedisKey, there are implicit conversions in place to allow you to pass string or byte[].

Other common operations

The IDatabase interface includes several other methods to work with the Redis cache. There are methods to work with hashes, lists, sets, and ordered sets.

Here are some of the more common ones that work with single keys, you can read the source code for the interface to see the full list.

Method Description
CreateBatch Creates a group of operations that will be sent to the server as a single unit, but not necessarily processed as a unit.
CreateTransaction Creates a group of operations that will be sent to the server as a single unit and processed on the server as a single unit.
KeyDelete Delete the key/value.
KeyExists Returns whether the given key exists in cache.
KeyExpire Sets a time-to-live (TTL) expiration on a key.
KeyRename Renames a key.
KeyTimeToLive Returns the TTL for a key.
KeyType Returns the string representation of the type of the value stored at key. The different types that can be returned are: string, list,set, zset and hash.
Executing other commands

The IDatabase object has an Execute and ExecuteAsync method which can be used to pass textual commands to the Redis server. For example:

var result = db.Execute(“ping”); Console.WriteLine(result.ToString()); // displays: “PONG”

The Execute and ExecuteAsync methods return a RedisResult object which is a data holder that includes two properties:

  • Type which returns a string indicating the type of the result – “STRING”, “INTEGER”, etc.
  • IsNull a true/false value to detect when the result is null.

You can then use ToString() on the RedisResult to get the actual return value.

You can use Execute to perform any supported commands – for example, we can get all the clients connected to the cache (“CLIENT LIST”):

var result = await db.ExecuteAsync(“client”, “list”); Console.WriteLine($”Type = {result.Type}\r\nResult = {result}”);

This would output all the connected clients:

Type = BulkString Result = id=9469 addr=16.183.122.154:54961 fd=18 name=DESKTOP-AAAAAA age=0 idle=0 flags=N db=0 sub=1 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=subscribe numops=5 id=9470 addr=16.183.122.155:54967 fd=13 name=DESKTOP-BBBBBB age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=client numops=17

Storing more complex values

Redis is oriented around binary safe strings, but you can cache off object graphs by serializing them to a textual format – typically XML or JSON. For example, perhaps for our statistics, we have a GameStats object which looks like:

public class GameStat { public string Id { get; set; } public string Sport { get; set; } public DateTimeOffset DatePlayed { get; set; } public string Game { get; set; } public IReadOnlyList<string> Teams { get; set; } public IReadOnlyList<(string team, int score)> Results { get; set; } public GameStat(string sport, DateTimeOffset datePlayed, string game, string[] teams, IEnumerable<(string team, int score)> results) { Id = Guid.NewGuid().ToString(); Sport = sport; DatePlayed = datePlayed; Game = game; Teams = teams.ToList(); Results = results.ToList(); } public override string ToString() { return $”{Sport} {Game} played on {DatePlayed.Date.ToShortDateString()} – ” + $”{String.Join(‘,’, Teams)}\r\n\t” + $”{String.Join(‘\t’, Results.Select(r => $”{r.team } – {r.score}\r\n”))}”; } }

We could use the Newtonsoft.Json library to turn an instance of this object into a string:

var stat = new GameStat(“Soccer”, new DateTime(1950, 7, 16), “FIFA World Cup”, new[] { “Uruguay”, “Brazil” }, new[] { (“Uruguay”, 2), (“Brazil”, 1) }); string serializedValue = Newtonsoft.Json.JsonConvert.SerializeObject(stat); bool added = db.StringSet(“event:1950-world-cup”, serializedValue);

We could retrieve it and turn it back into an object using the reverse process:

var result = db.StringGet(“event:1950-world-cup”); var stat = Newtonsoft.Json.JsonConvert.DeserializeObject<GameStat>(result.ToString()); Console.WriteLine(stat.Sport); // displays “Soccer”

Cleaning up the connection

Once you are done with the Redis connection, you can Dispose the ConnectionMultiplexer. This will close all connections and shutdown the communication to the server.

redisConnection.Dispose(); redisConnection = null;

Content Delivery Network

A content delivery network (CDN) is a distributed network of servers that can efficiently deliver web content to users. CDNs store cached content on Edge servers that are close to users to minimize latency. These Edge servers are located in point of presence (POP) locations that are distributed throughout the globe.

CDNs are typically used to deliver static content, such as images, style sheets, documents, client-side scripts, and HTML pages. The major advantages of using a CDN are lower latency and faster delivery of content to users—regardless of their geographicallocation in relation to the datacenter where the application is hosted. CDNs can also help to reduce the load on a web application, because the application does not have to service requests for the content that is hosted in the CDN.

Typical uses for a CDN include:

  • Delivering static resources, often from a website, for client applications. These resources can be images, style sheets, documents, files, client-side scripts, HTML pages, HTML fragments, or any other content that the server does not need to modify for eachrequest.
  • Delivering public static and shared content to devices such as mobile phones and tablets.
  • Serving entire websites that consist of only public static content to clients without requiring any dedicated compute resources.
  • Streaming video files to client devices on demand, taking advantage of the low latency and reliable connectivity available from the globally located datacenters that offer CDN connections.
  • Supporting Internet of Things (IoT) solutions. The huge numbers of devices and appliances involved in an IoT solution can easily overwhelm an application if it has to distribute firmware updates directly to each device.
  • Coping with peaks and surges in demand without requiring the application to scale, avoiding the consequent increased running costs.

Azure CDN

In Azure, the Azure Content Delivery Network (Azure CDN) is a global CDN solution for delivering high-bandwidth content that is hosted in Azure or in any other location. Using Azure CDN, you can cache publicly available objects loaded from Azure Blobstorage, a web application, a virtual machine, or any publicly accessible web server. Azure CDN can also accelerate dynamic content, which cannot be cached, by taking advantage of various network optimizations by using CDN POPs. An example is using routeoptimization to bypass Border Gateway Protocol (BGP).

Here’s how Azure CDN works.

  1. A user (Example User) requests a file (also called an asset) by using a URL with a special domain name, such as endpoint_name.azureedge.net. This name can be an endpoint hostname or a custom domain. The Domain Name System (DNS) routesthe request to the best-performing POP location, which is usually the POP that is geographically closest to the user.
  2. If no Edge servers in the POP have the file in their cache, the POP requests the file from the origin server. The origin server can be an Azure web app, Azure cloud service, Azure storage account, or any publicly accessible web server.
  3. The origin server returns the file to an Edge server in the POP.
  4. An Edge server in the POP caches the file and returns the file to the original requestor (Example User). The file remains cached on the Edge server in the POP until the Time to Live (TTL) specified by its HTTP headers expires. If the origin server didn’tspecify a TTL, the default TTL is seven days.
  5. Additional users can then request the same file by using the same URL that the original requestor (Example User) used, which can also be directed to the same POP.
  6. If the TTL for the file hasn’t expired, the POP Edge server returns the file directly from the cache. This process results in a faster, more responsive user experience.

Manage Azure CDN by using Azure CLI

The Azure Command-Line Interface (Azure CLI) provides one of the most flexible methods to manage your Azure CDN profiles and endpoints. You can get started by listing all of your existing CDN profiles:

az cdn profile list

This will globally list every CDN profile associated with your subscription. If you want to filter this list down to a specific resource group, you can use the –resource-group parameter:

az cdn profile list –resource-group ExampleGroup

To create a new profile, you should use the new create verb for the az cdn profile command group:

az cdn profile create –name DemoProfile –resource-group ExampleGroup

By default, the CDN will be created by using the standard tier and the Akamai provider. You can customize this further by using the –sku parameter and one of the following options:

  • Custom_Verizon
  • Premium_Verizon
  • Standard_Akamai
  • Standard_ChinaCdn
  • Standard_Verizon

After you have created a new profile, you can use that profile to create an endpoint. Each endpoint requires you to specify a profile, a resource group, and an origin URL:

az cdn endpoint create –name ContosoEndpoint –origin www.contoso.com –profile-name DemoProfile –resource-group ExampleGroup

You can customize the endpoint further by assigning a custom domain to the CDN endpoint. This helps ensure that users see only the domains you choose instead of the Azure CDN domains:

az cdn custom-domain create –name FilesDomain –hostname files.contoso.com –endpoint-name ContosoEndpoint –profile-name DemoProfile –resource-group ExampleGroup

Cache expiration in Azure CDN

Because a cached resource can potentially be out-of-date or stale (compared to the corresponding resource on the origin server), it is important for any caching mechanism to control when content is refreshed. To save time and bandwidth consumption, a cachedresource is not compared to the version on the origin server every time it is accessed. Instead, as long as a cached resource is considered to be fresh, it is assumed to be the most current version and is sent directly to the client. A cached resource is considered tobe fresh when its age is less than the age or period defined by a cache setting. For example, when a browser reloads a webpage, it verifies that each cached resource on your hard drive is fresh and loads it. If the resource is not fresh (stale), an up-to-date copy isloaded from the server.

Caching rules

Azure CDN caching rules specify cache expiration behavior both globally and with custom conditions. There are two types of caching rules:

  • Global caching rules. You can set one global caching rule for each endpoint in your profile that affects all requests to the endpoint. The global caching rule overrides any HTTP cache-directive headers, if set.
  • Custom caching rules. You can set one or more custom caching rules for each endpoint in your profile. Custom caching rules match specific paths and file extensions; are processed in order; and override the global caching rule, if set.

For global and custom caching rules, you can specify the cache expiration duration in days, hours, minutes, and seconds.

Purging and preloading assets by using the Azure CLI

The Azure CLI provides a special purge verb that will unpublish cached assets from an endpoint. This is very useful if you have an application scenario where a large amount of data is invalidated and should be updated in the cache. To unpublish assets, you mustspecify either a file path, a wildcard directory, or both:

az cdn endpoint purge –content-paths ‘/css/*’ ‘/js/app.js’ –name ContosoEndpoint –profile-name DemoProfile –resource-group ExampleGroup

You can also preload assets into an endpoint. This is useful for scenarios where your application creates a large number of assets, and you want to improve the user experience by prepopulating the cache before any actual requests occur:

az cdn endpoint load –content-paths ‘/img/*’ ‘/js/module.js’ –name ContosoEndpoint –profile-name DemoProfile –resource-group ExampleGroup