I’ve been struggling over the last few days with an application that is using the ASP.NET cache quite bit. As it turns out the programmatic cache in ASP.NET in most of my applications is not keeping items in them under almost all circumstances.
When I’m talking about the programmatic cache, I mean the Context.Cache object and manually adding items into the cache. I’m a big fan of HTML fragment caching for rendering portions of a page either via code based rendering or from control based rendering, capturing the output and then storing it in cache. I use this commonly for portions of sidebars that are semi-dynamic to avoid excessive use of user controls which gets messy and often serves no good purpose if the use is one off. To create a user control just to get caching seems like overkill.
I’ve noticed recently as I was debugging my RSS problem on my live server that the cache in most of my applications is empty! Completely empty – and that should not be… items are added and while they are typically added with relatively short timeouts of a few minutes or less sometimes, they are long enough that I would notice them.
To check this out I just created a simple page and simply iterate through the cache and echo items out like this:
protected void Page_Load(object sender, EventArgs e)
{
foreach (DictionaryEntry d in this.Cache)
{
Response.Write(d.Key);
Response.Write(" - ");
Response.Write(d.Value);
Response.Write("<hr>");
}
}
If you’re looking for something way more sophisticated you can also check out Steve Smith’s CacheManager tool here:
http://aspalliance.com/cachemanager/
So when I look at the cache when I start my application right from the start I usually see a bunch of items popping into the cache as I would expect. But as the application keeps for a while cache items start disappearing out of the cache even though items are definitely written to it. In some cases I can see the items show up very briefly (seconds or less) and almost immediatedly disappear.
What’s going on here? I suspect that the problem has to do with memory. The server I run this site on is pretty old and it has only 512megs of memory. It’s running 5 different ASP.NET AppPools and a number of older FoxPro West Wind Web Connection apps. Some of the ASP.NET apps are pretty resource intensive. The server has no real performance problems but memory is definitely tight and I’m wary to update this old machine at this point.
But it’s an eye opener to see that the ASP.NET Cache is not actually caching in most application level situations in this low memory environment. Apparently the memory pressure on the box is keeping the cache from even storing tiny little entries. Some of the values I’m storing are things like two or three line value summaries that only need to refresh every so often. Even these things which are less than 80 string characters won’t stay in the cache.
It’s disconcerting because, unless you actually check your cache contents occasionally you really have no idea of what’s getting cached. I had happily assumed that my entries where getting cached when in fact they weren’t.
It doesn’t break the application (in most cases) but it certainly affects performance. I say 'in most cases' - I actually started looking into this because I had one request that is an image handler and uses the cache to pass values between the executing request and an HttpHandler. In this particular scenario I need to absolutely make sure that the value makes it into the cache and stays there. Even though this value is a single small ID value string (a guid) and the value was retrieved almost instantly this code failed intermittently. ASP.NET’s cache was dumping even this short value!
Luckily there’s a workaround:
Cache.Add(CacheKey,IdValue, null,
DateTime.UtcNow.AddSeconds(30), TimeSpan.Zero,
CacheItemPriority.NotRemovable,null);
Which ensures that the cache manager doesn’t remove the item from the cache. Only an explicit Context.Cache.Remove() or a timeout will remove the item.
So yeah, I know the solution to this problem is to upgrade the machine and add more memory <s>. I wasn’t planning on doing any updates to this box as I probably replace it when LongHorn server becomes available. In the meantime I guess I have to live without manual caching.
OutputCache
Incidentally, Page OutputCaching probably has a lot to do with the memory usage of these applications as well. I do notice that requests that are cached with OutputCache at the page level seem to cache fine. I can see this happening by looking at my internal request logging and seeing entries that come back with a 0 request time length. So that OutputCaching seems to work reasonably well.
The OutputCache is sort of a mystery black box to me <s>. It seems there’s no easy way to tell just what is cached and how it’s cached. Does anybody know if there’s a way to get at the OutputCache entries in anyway? Is ASP.NET even storing the cached content in the same cache (and hiding the entries from external iteration) or is it using a completely separate cache. And then there’s Kernel Caching where some content is cached by IIS. But again there’s no way to tell that I know of whether an item actually made it into the Kernel Cache. <shrug>
More common than you might think
The moral of this entry <g> is that this low memory and cache refusal might actually much more common than you expect. ISPs for example often run in tight memory environments as they keep the AppPool memory limits really low. And even with a lot of memory Caching can very quickly overrun memory. If you have large pages that get cached memory goes quick. Caching localized content - another quick way to load up cache memory excessively as each locale gets different cache entries.
Today for kicks I checked several of my customer's applications that are running in a hosted environments and 2 out of 4 exhibited the same behavior with the Context.Cache basically showing empty. Eeek...
Other Posts you might also like