Handling Cache Lookups – A “Command Pattern” Recipe

This is a little recipe I’ve used in a number of projects where we needed some basic re-usable caching code to allow reliable and easy-to-use caching operations. The code is in C# and designed to handle caching of repeat calls to “slow” operations, but you could use this pattern for other purposes.

Typically, your cache implementation (eg. Redis) is nothing more than a very fast dictionary lookup, and provides basic “get”, “set” and “delete” operations. Stale data is usually handled by purging items from the cache after a set time, and items may also disappear from the cache early by a reset or heavy load forcing the older items out first. So whenever you go looking for an item in the cache, you have to anticipate it not being there.

Every cache operation has some standard code wrapped around a specific operation. The typical cache lookup workflow for fetching an item from the cache would be:

  1. Generate a cache key representing the item
  2. Perform cache lookup for an item matching the key, if found return it
  3. If the item is not found
    1. Go fetch it (database query or other slow operation)
    2. Insert it into the cache for next time

If you want to cache operations for speed, you could simply implement this logic for every cache lookup and have a load of “copy and paste” code. But there are better approaches.

I have used this caching pattern when dealing with Redis and my own basic “in memory” mock cache implementations. The point of this post isn’t the detail of the interaction with a specific cache implementation or the individual low-level cache operations, but how you can approach writing code for “get the information from the cache if you can, else fallback to a slow database operation” scenarios.

An Example Application (Without Caching)

To start with, I’m going to pretend I have a really simple MVC app that stores and fetches “products”. There’s a UI layer (MVC), a business layer (ProductService) and a data layer (repository). Mostly, I’m going to ignore the UI and data layer implementation, and focus on the business layer. I don’t care whether the “data” layer is a database, a file, an in-memory lookup or API call to some other service. My assumption is that the data layer is a good candidate for caching – slow fetch operations for data that is repeatedly requested.

I’m going to assume that we have decided to try and cache the “get one product” and “get all products” operations, and that we don’t need to worry about exceptions because nothing will ever go wrong.

So we start with a simple Product model, a controller, and some interfaces specifying how the business and repository services will work.


    public class Product
    {
        public int? Id { get; set; }
        public string Name { get; set; }
        public string Supplier { get; set; }
        public DateTime Date { get; set; }
        public string Details { get; set; }
    }

    public class ProductController : Controller
    {
        private readonly IProductService _productService;

        public ProductController(IProductService productService)
        {
            _productService = productService;
        }

        public ActionResult Index()
        {
            var model = _productService.All();
            return View(model);
        }

        public ActionResult Product(int id)
        {
            var model = _productService.Get(id);
            return View(model);
        }
    }

    public interface IProductService
    {
        Product Get(int id);

        void Add(Product product);

        Product[] All();
    }

    public interface IProductRepository
    {
        Product Get(int id);

        void Add(Product product);

        Product[] All();
    }

The simplest thing that could possibly work is “no caching”. So let’s start with that and just forward all requests to the data layer.


    public class SimpleProductService : IProductService
    {
        private readonly IProductRepository _repository;

        public SimpleProductService(IProductRepository repository)
        {
            _repository = repository;
        }

        public Product Get(int id)
        {
            return _repository.Get(id);
        }

        public void Add(Product product)
        {
            _repository.Add(product);
        }

        public Product[] All()
        {
            return _repository.All();
        }
    }

Not very exciting. So let’s add a cache implementation that lets me fetch, store or remove an item. I’m assuming that we might one day want to control how long an item gets stored in the cache – my CacheLifetimeType enum will be used to determine whether to store an item for a configurable number of minutes, hours or days.


    public interface ICache
    {
        void Set<T>(string key, T value);

        void Set<T>(string key, T value, CacheLifetimeType lifetimeType);

        T Get<T>(string key);

        bool Exists(string key);

        void Clear(string key);
    }

The cache is just a simple lookup, and the simplest implementation is just an in-memory dictionary (great for unit testing, not much value in production). Be careful with primitive types – the types stored have to be nullable, so you can distinguish between “not found in the cache” and “default value cached” (I learned this the hard way with some boolean configuration values).

My basic in-memory cache implementation serializes the data into binary form (you could also use Json), and requires that any type stored in the cache is marked as “Serializable”. For this example, that would require decorating the Product class with the “Serializable” attribute. I have also used the StackExchange.Redis client.

Note: I typically extend any cache implementation to ensure I had useful admin and diagnostic methods. Examples include showing the general “status”, a count or list of all entries, and the ability to wipe the cache while testing/debugging (for entries matching a key pattern, or “everything”).

Caching Fetched Data for Next Time

I’m going to replace my SimpleProductService with something that checks if an item is in the cache before going off to the database.


    public class CachedProductService : IProductService
    {
        private readonly IProductRepository _repository;
        private readonly ICache _cache;

        public CachedProductService(IProductRepository repository, ICache cache)
        {
            _repository = repository;
            _cache = cache;
        }

        public Product Get(int id)
        {
            string cacheKey = id.ToString();

            //Try fetch from cache
            var cachedValue = _cache.Get<Product>(cacheKey);
            if (cachedValue != null)
            {
                return cachedValue;
            }

            //Fallback to a slow data fetch, cache for next time
            Product result = _repository.Get(id);

            if (result != null)
            {
                _cache.Set(cacheKey, result, CacheLifetimeType.Default);
            }
            return result;
        }

        public void Add(Product product)
        {
            _repository.Add(product);
        }

        public Product[] All()
        {
            return _repository.All();
        }
    }

OK, that’s a lot of code we had to add to the Get() method. And that’s for one operation. What if we want to add caching to another operation? Let’s copy and paste!


        public Product[] All()
        {
            string cacheKey = "products";

            var cachedValue = _cache.Get<Product[]>(cacheKey);
            if (cachedValue != null)
            {
                return cachedValue;
            }

            Product[] result = _repository.All();

            if (result != null)
            {
                _cache.Set(cacheKey, result, CacheLifetimeType.Default);
            }
            return result;
        }

Obviously, we have a load of repeat copy-and-paste code for checking the cache (and feeding it for next time) that we’d like to have in a single place for all cached operations to use.

My initial thought was that you could encapsulate this into some kind of helper class, maybe use some generics. So I start writing some not-very-good code.


    //This is an example of how not to do things!
    public class CacheHelper<TReturn, TInput>
    {
        private readonly ICache _cache;

        public CacheHelper(ICache cache)
        {
            _cache = cache;
        }

        public TReturn Fetch(TInput request, string cacheKey)
        {
            if ((_cache != null) && !string.IsNullOrEmpty(cacheKey))
            {
                var cachedValue = _cache.Get<TReturn>(cacheKey);
                if (cachedValue != null)
                {
                    return cachedValue;
                }

                //Specific fetch operation in a generic method! Oops...
                TReturn result = ReallyFetchIt(request);
                _cache.Set(cacheKey, result, CacheLifetimeType.Default);

                return result;
            }
            //No caching
            return ReallyFetchIt(request);
        }
    }

…and that’s not going to work because my generic helper class needs access to a specific operation (the “ReallyFetchIt” method). Thankfully, there’s another way to solve the problem.

Wrapping Operations in Commands

So to make things easy, I’m going to take inspiration from the “Command” design pattern, and wrap all the code to actually fetch a value from a “cache or database” operation in a simple executable command. The plan here is to have a base class than can handle most of the repeat “boilerplate” code, and then have a specific implementation for each different operation.

I’m not even worried about macros and undos and all the other advantages of using the command pattern. Really, I just find that the “encapsulate request as object” in a “runnable command” code structure fits my needs.

I create an abstract FetchCommand class that can handle generic caching and fall through to specific database code if necessary, and then go with a really simple implementation for my FetchProductCommand. This first draft of a “fetch product” command doesn’t actually have everything it needs for caching.


    /// <summary>
    /// Generic base class of a command that calls an external operation with optional caching for performance
    /// </summary>
    /// <remarks>
    /// TReturn type MUST be nullable - for primitive/non-nullable types, cache can confuse default value with "no value found so return default".
    /// </remarks>
    public abstract class FetchCommand<TReturn, TInput>
    {
        private readonly ICache _cache;

        /// <summary>
        /// Recommended cache lifetime - override for short/long cache life.
        /// </summary>
        protected virtual CacheLifetimeType LifetimeType { get { return CacheLifetimeType.Default; } }

        protected FetchCommand()
        {
            _cache = null;
        }

        protected FetchCommand(ICache cache)
        {
            _cache = cache;
        }

        /// <summary>
        /// Get cache key - if not implemented or no key specified, command implementation will not cache fetched values
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        protected virtual string GetCacheKey(TInput request)
        {
            return string.Empty;
        }

        /// <summary>
        /// Run the command, get data
        /// </summary>
        /// <param name="request">Input request</param>
        /// <param name="cacheOverride">Optional instruction to (if set true) override cache, fetch latest and update cache</param>
        /// <returns>Search result of specified return type</returns>
        public TReturn Run(TInput request, bool cacheOverride = false)
        {
            string cacheKey = GetCacheKey(request);
            if ((_cache != null) && !string.IsNullOrEmpty(cacheKey))
            {
                //Note: assume that if we're overriding cache we fetch, clear and re-cache
                //To avoid caching old values while we fetch new, don't clear/fetch/re-cache, instead fetch THEN update cache

                if (!cacheOverride)
                {
                    //Try fetch from cache
                    var cachedValue = _cache.Get<TReturn>(cacheKey);
                    if (cachedValue != null)
                    {
                        return cachedValue;
                    }
                }

                //If we get to here, either we override cached value OR result was never cached
                TReturn result = FetchCore(request);

                //May want to validate the result, and if necessary prevent caching
                if (IsValidResult(result))
                {
                    _cache.Set(cacheKey, result, LifetimeType);
                }

                return result;
            }
            //No caching
            return FetchCore(request);
        }

        protected abstract TReturn FetchCore(TInput request);


        /// <summary>
        /// Is the returned result valid? If not, do not cache!
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        protected virtual bool IsValidResult(TReturn result)
        {
            if (result == null)
            {
                return false;
            }
            return true;
        }
    }

    public class FetchProductCommand : FetchCommand<Product, int>
    {
        private readonly IProductRepository _repository;

        public FetchProductCommandNoCache(IProductRepository repository)
        {
            _repository = repository;
        }

        protected override Product FetchCore(int request)
        {
            return _repository.Get(request);
        }
    }

Yes, it’s a lot of code, but the pay-off comes when you re-use this for several operations by implementing the base FetchCommand. Each command implementation only needs to worry about what happens if the cache yields no value and you actually have to query the current value from the database.

Note that I also added the option of a “cache override” to my FetchCommand to allow you to bypass any cached value and go straight to the source.

My ProductService code, where I actually use the command, becomes nice and simple again:


        public Product Get(int id)
        {
            return new FetchProductCommand(_repository).Run(id);
        }

Of course, we don’t actually have any caching yet, because our command doesn’t have access to a cache, and doesn’t yet supply a valid cache key. So let’s fix that.


    public class FetchProductCommand : FetchCommand<Product, int>
    {
        private readonly IProductRepository _repository;

        protected override string GetCacheKey(int request)
        {
            return request.ToString();
        }

        public FetchProductCommand(IProductRepository repository, ICache cache) : base(cache)
        {
            _repository = repository;
        }

        protected override Product FetchCore(int request)
        {
            return _repository.Get(request);
        }
    }

So now we have a more elegant solution that we can use for any operation. By wrapping the detail of the operation in a command and pushing most of the tedious cache interaction to the base class, we simply wrap what’s different about each operation in its own command. So we can fetch single products, or lists of products, or users, or anything really, without having to worry about how the cache works (or whether there even is a cache).

We still only need a single line to run the command for “fetch item from cache if possible, else go all the way to the database”.


        public Product Get(int id)
        {
            return new FetchProductCommand(_repository, _cache).Run(id);
        }

At this point, we could even dispense with our separate “service” layer and just have the controller run commands directly.

And if we wanted to get the absolute latest information, that “cache override” option I added is available to all commands:


            return new FetchProductCommand(_repository, _cache).Run(id, true);

One annoyance with this approach is that individual commands still need access to the cache and other services – it might be worth creating a “command factory” to dish out command instances pre-loaded with references to the cache, data repositories and other services.

One design decision I have made is to pass the specific product request into the “run” method, rather than making the command immutable. You can do either, it might depend on whether you want a factory dishing out a “generic” FetchProductCommand, or building a specific “get product number 27” command.

Cache Keys

Every item in the cache is a (key,value) pair, where the key represents a specific item. So far in this example, I’ve used the most basic key – an item id. But what if you were storing say products and users in the cache? There’s the risk of id clashes and all sorts of issues. So you could have a more complex key eg. “Product2”, or “UserJohnDoe”. Suddenly, the string-building necessary to generate cache keys became complex.

For this, we can build a little helper, and use some simple fluent syntax to build our own cache keys.


    public class CacheKeyGenerator
    {
        public const string Separator = "|";

        private IList<string> _keys = new List<string>();

        public string Key
        {
            get
            {
                return string.Join(Separator, _keys);
            }
        }

        public CacheKeyGenerator Add(string key)
        {
            if (!string.IsNullOrEmpty(key))
            {
                _keys.Add(key);
            }
            return this;
        }

        public CacheKeyGenerator Add(int? key)
        {
            if (key.HasValue)
            {
                _keys.Add(key.ToString());
            }
            return this;
        }

        //Extend to handle other types
    }

    //Example usage:
    var key = new CacheKeyGenerator().Add(request.Username).Add(request.Id).Key;

Our example cached “get product” command took a very simple request (an integer Id), a more complex operation might require a request class with multiple properties. It might also make sense to have a request class that generates its own cache key (so the command doesn’t even have to know the details of the data it’s requesting). This is especially useful if you need that key elsewhere eg. to purge the cache of a specific value.

If you’re not worried about your cache keys being human-readable, you could use some kind of hash or checksum approach instead of string-building to generate your cache keys.

Removing Cached Items

To prevent “stale” data, cached information should be purged from the cache. This usually happens automatically – old items disappear and are cached when you fetch them again. In the example I also added a “cache override” flag to allow for force-fetching the latest information.

Intervening to remove/refresh cached information can be tricky. Clearing a single item from the cache is easy. Clearing the entire cache is easy. Clearing selected items from the cache is hard, and your best bet is to clear any items where the key matches a pattern. This might lead you into having to know the details of how individual items/operations are cached, and really you want a caching strategy that hides such details.

The example shows caching the “get all products” method. In practice, this might not be the smart thing to do (especially as the “get all products” method might have a query filter, and then you might have many different variants of search results cached). Any time you have the same item of data stored multiple times in the cache (eg. from “get one” and “get all” operations) then you either risk stale/mismatched data, or have to deal with the pain of knowing which operations to purge from the cache when you update.

Alternative Approaches?

This probably isn’t the only way to deal with caching in an elegant fashion. To try and remove the need for creating a new class for each specific cached operation, I did investigate the possibility of just wrapping my cached operation in a “using” statement (with something like a generic IDisposable cache handler), but I ran into the same problems as my initial helper method – the generic before/after cache wrapping needs not only the key (for the cache check) but the value (to populate for next time), and there’s no knowing whether you’ll be running the specific fetch operation until the generic cache check has happened.

You can also use this “command” approach for your non-cached API and database queries if you can’t wrap the operation in a simple using statement. And if you do need to be able to chain actions together or handle undo actions, then wrapping your operations in a command might make sense.