I'm working on implementing data caching for commonly used database objects and I'm having some difficulty deciding the best strategy for caching the following scenario:
I have an table called Campaigns with 1423 records. A Campaign contains all the texts and settings that describes a landingpage. When a landingpage is generated the corresponding Campaign is fetched from the database. Some landingpages are viewed more than others.
(Output caching is my next step so no need to comment on that.)
I'm thinking loudly:
A) Load all campaigns upfront into an Dictionary when the server starts and put it in cache.
I've calculated that this would take about 3.8 MB of server memory (OK) and would take about 8 seconds (OK) on first page view with current amount of campaigns. Only problem is that this doesn't scale very well. Tomorrow I might have 10 times the amount of campaigns. Also we need to update and add new campaigns on a daily basis, and dropping the entire Campaigns cache every time seems a bit overkill.
The nice part of this is that all campaigns are allways cached so that there's no startup time when a landingpage that hasn't been visited is accessed (by for example a search engine).
B) Lazyloading: Cache each campaign indiviudally with a key like "Campaigns.[id]"
This would make it very easy to kill or add induvidual campaigns, and I can use the Caching features such as SlidingExpiration for campaigns less used. Only problem is that when a search engine visits a seldomly visited page, that's when the campaign is loaded. Another problem is that the Cache is cluttered with loads of keys.
C) Lazyloading into Dictionary same bennefins as B) and cache is not cluttered with loads of keys but can't use cache features, and if memory is tight the entire dictionary is dumped.
What do you think? I'm leaning towards B).
Also, I have the scenario where I need to get Campaigns either by id or by a code (string).
In the following:
Campaigns campaign = //Get campaign from database:
Cache["Campaigns.Id."+campaign.Id.ToString()] = campaign; //Id is Guid
Cache["Campaigns.Code."+campaigns.Code] = campaign;
Would this cost me twice the memory or just for another index reference?
Hmmm, off the top of my head I would look at caching grouped items (similar to your B idea), but place a time-out on them. This means the seldom used items will cache, but eventually flush. The time-out should be relevant to the frequency of normal usage. For example, if a normal campaign gets hit 10 times an hour, make the time-out quite low, 5-10 minutes perhaps.
ASP.NET Cache has this time-out stuff built into it:
http://quickstarts.asp.net/QuickStartv20/aspnet/doc/caching/data.aspx
Memory is cheap, but still needs maintaining. As a first-pass implementation, I think a basic time-out set up is not a bad start. This will need monitoring, as any caching implementation would, to see if it helps or hinders your usage patterns.
I wouldn't lazy load as granularly as every single item. Usage should show you common items, or your can select batches (top 10, top 20, etc). Intelligently selecting groups of commonly accessed items and commonly infrequent items will help improve the health of the cache (not many stale items, not much churn, etc).
As for overall memory usage, I think the ASP.NET Cache can be configured to limit the number of bytes taken:
http://msdn.microsoft.com/en-us/library/ms228248.aspx
So you shouldn't need to worry about that too much.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With