Rapid Prototyping with the Redis Cache

Leveraging Redis Hashes and the StackExchange API for Rapid Prototyping

The Redis Cache is a excellent persistence option for rapid storage and retrieval of cached data. Also referred to as a store for key-value pairs, or a data structure server, Redis is fairly easy to both learn and use. For learning, I used Karl Seguin's post on Redis along with the searchable index of commands provided by Redis. To make life easier, I also included the general purpose Redis client written by the developers at StackExchange.

Here are my methods that mimic basic CRUD operations for a generic product or service issues list using Redis instead of a database. The RedisKey is just a C# string, but doesn't have to be. You'll notice I use hashes instead of strings in the methods below. The reason for this is not so I can have one key to manage a collection of field/value pairs within a Redis hash. What's the difference between a string's key and its corresponding value and a hash's field and its corresponding value other than implementation and efficiency? When using hashes, you need a key to retrieve the hash itself, and a field "value" to retrieve the actual "value" associated with the field. When you choose to use strings, the key is used as a value you associate with another value.

A key is very much like a key in a dictionary or associative array. For strings, all you need is the key to look up your corresponding value, which can be many different .NET types, including a binary object. The key used for setting and getting hashes is similar, but you will retrieve the entire hash of field/value pairs using a single key. Should you wish to retrieve a hash field/value pair, you can do that too, and easily, but you need both the key and the field value.

Efficieny; this leads me to the next point, which is taken from "An introduction to Redis data types and abstractions"; "It is worth noting that small hashes (i.e., a few elements with small values) are encoded in special way in memory that make them very memory efficient" (http://redis.io/topics/data-types-intro). Interestingly enough, the Redis command(s) docs state that "Redis running on an entry level laptop can scan a 1 million key database in 40 milliseconds."

 

 

    
private List<IssueItem> PutIssue(IssueItem issueToPut) { var redisConnection = RedisConnectionFactory.RedisConnection; var redisCacheDb = redisConnection.GetDatabase(); var existingHashEntries = redisCacheDb.HashGetAll(RedisKey); var newHashEntries = new StackExchange.Redis.HashEntry[existingHashEntries.Length + 1]; var hashEntryIssue = new StackExchange.Redis.HashEntry(issueToPut.Id, StackExchangeRedisExtensions.Serialize(issueToPut)); // My first pass at updating a hash entry, which really should be done like in the UpdateIssue method below. That is, there is no need // to resync the hash entry array like this. // for (int i = 0; i < existingHashEntries.Length; i++) { newHashEntries[i] = existingHashEntries[i]; } newHashEntries[existingHashEntries.Length] = hashEntryIssue; redisCacheDb.HashSet(RedisKey, newHashEntries); return new List<IssueItem> { issueToPut }; } private List<IssueItem> UpdateIssue(IssueItem issueToUpdate) { var redisConnection = RedisConnectionFactory.RedisConnection; var redisCacheDb = redisConnection.GetDatabase(); redisCacheDb.HashSet(RedisKey, issueToUpdate.Id, StackExchangeRedisExtensions.Serialize(issueToUpdate)); return new List<IssueItem> { issueToUpdate }; } private List<IssueItem> RemoveIssue(IssueItem issueToUpdate) { var redisConnection = RedisConnectionFactory.RedisConnection; var redisCacheDb = redisConnection.GetDatabase(); redisCacheDb.HashDelete(RedisKey, issueToUpdate.Id); return new List<IssueItem> { issueToUpdate }; } private List<IssueItem> GetIssues() { var redisConnection = RedisConnectionFactory.RedisConnection; var redisCacheDb = redisConnection.GetDatabase(); var hashEntries = redisCacheDb.HashGetAll(RedisKey); if (hashEntries != null) { var issueList = hashEntries.Select(hashEntry => { return StackExchangeRedisExtensions.Deserialize<IssueItem>(hashEntry.Value); }).ToList(); return issueList; } return null; }