Blog Post A Sample Blog Post

How to cache stuff in a .net app.

Long story short. If you're not going to take the time to figure out how it works you're probably not going to get the performance you expect.

So you've got some big ol objects that are expensive to build and you want to store them in a cache so you can play nice with your hardware and maybe give your users a nice performance boost.

What really happens in a cache. There is a pool of memory managed by the .net runtime used for caching data. This pool is actually a key value store a lot like Dictionary. Normally you toss an object in with a string key, and whatever was tossed in gets serialized to a byte array and stored for later use. When you ask for it back, it deserializes the thing and hands it back to you.

Issue 1: Caching large complex objects. Or collections of smaller objects.
Deseriliazing stuff in .net is kinda slow because it needs to use reflection and reasons and stuff. Just suffice it to say that there are a lot of situations where the cost of caching gets eaten by the cost of actually getting the thing back out of the cache. For huge objects with a complex graph this is kinda obvious. Less obvious is if you've got a huge list of things. That's expensive too. It's best to store large simple things. Or small simple things that are expensive to compute.

Issue 2: In Memory vs Out of Process cache. You wouldn't think it but the in memory cache operates differently than the out of process cache. The biggest difference is that an in memory cache points to a reference, so if you pull something from a cache and update it, it stays updated. The opposite is true of an out of process cache. This will jump up and bite you in the face if your site is successful enough to need to run on more than one server. Suddenly you'll run it locally in dev and see the first behaviour and as soon as you deploy it you're caught out. What's worse is that in practice you can get used to the behaviour either way. Get used to your items being immutable in the cache and when you move back down to a single server you'll start seeing some crazy shared state behaviours. Which brings up issue 3...

Issue 3: Threading. Yeah nothing in here is thread safe. Unless you're using out of process, then it is because it's immutable. But if you're not then watch it, you're going to want to use some locking if you want to edit the cached object. It's probably just a good idea to not edit a cached object as a general practice.

So that's what can go wrong with a the runtime cache in .net. Thankfully implementing it is really simple. Here's a quick implementation of using a cache that I'm using to serve this page.

namespace CardboardForts.Blog.Models
{
    using CardboardForts.Blog.Data.Apis;
    using MarkdownSharp;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Caching;

    public class CachedBlogListing : BlogDirectoryListing
    {
        private ObjectCache _blogCache;
        private const string _keyBase = "blog_listing";

        public CachedBlogListing(string path) : base(path)
        {
            _blogCache = MemoryCache.Default;
        }

        public override string GetText(string fileName)
        {
            var key = _keyBase + fileName;
            if (_blogCache.Contains(key))
                return _blogCache[key] as string ?? GetText(fileName);

            var blogPost = base.GetText(fileName);
            var policy = new CacheItemPolicy();
            policy.ChangeMonitors.Add(new HostFileChangeMonitor(new List<string> { fileName }));

            _blogCache.Add(key, blogPost, policy);
            return blogPost;
        }
    }

    public class BlogDirectoryListing : DirectoryListing
    {
        private Markdown _md = new Markdown();
        public BlogDirectoryListing(string path) : base(path) { }

        public override string GetText(string fileName)
        {
            return _md.Transform(base.GetText(fileName));
        }
    }

    public class DirectoryListing 
    {
        string _path;
        public DirectoryListing(string path)
        {
            _path = path;
        }

        public virtual IEnumerable<string> GetFiles()
        {
            return Directory.EnumerateFiles(_path).ToList();
        }
        public virtual string GetText(string fileName)
        {
            return File.Exists(fileName) ? System.IO.File.ReadAllText(fileName) : string.Empty;
        }
    }
}

Leave a Comment:


Blog Search

Side Widget Well

Bootstrap's default well's work great for side widgets! What is a widget anyways...?