Cache observation
Yesterday I did an update to our code on our public server. Earlier I had updated our cache wrapper to use the System.Runtime.Caching (new in .Net 4.0) instead of the old System.Web.Caching. For some reason the local cache didn't work so our site went down and it took me about an hour to realize that it was the cache and reverse the changes.
We have a Cache Wrapper to access the cache and it's simple. This is an example of how I retrieve our language file.
FronturCache Cache = FronturCache.Current;
var tmx = Cache.GetContract<Tmx>("Tmx_IS");
At this time the language file is about 230KB for each language. We are runningSharedCacheservers for remote caching, but storing a 230KB file on a remote server and requesting it every time there is a request is not an option. With hundreds of requests every second this kills the server. So the solution is to keep the object also in local cache.
var tmx = Cache.GetContract<Tmx>("Tmx_IS", true);
addingtruetells the cache wrapper to first look for the object in local cache (System.Web.Caching.Cache) if it doesn't exist there it goes to the SharedCache server to get the object.
While figuring out what had happened yesterday I noticed that I'm retrieving the language time about 30 times in every request. The reason I didn't realize this is because our language class handles the request. This is our language class
Tmx t = Tmx.Current; //retrieve the language for this user or website
string labelName = t["Name"]; //Get the translation for the Name keyword, in Icelandic = Nafn, English = Name
So each time I needed to access the language file I would callTmx.Currentwhich would then callCache["Tmx_IS"]. This morning when I came to work I thought about testing what the difference was between using the SharedCache server, local cache and then Context.Items.
This is the test code
FronturCache Cache = FronturCache.Current;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i=0; i < 1000; i++) {
//Get the object from remote cache server
var tmx = Cache.GetContract<Tmx>("Tmx_is-barnaland");
}
Response.Write("SharedCache:" + sw.ElapsedTicks + "<br />");
sw.Restart();
for (int i=0; i < 1000; i++) {
// Get the object from local cache
var tmx = Cache.GetContract<Tmx>("Tmx_is-barnaland", true);
}
Response.Write("Local:" + sw.ElapsedTicks + "<br />");
var tmx2 = (Tmx) Cache.GetContract<Tmx>("Tmx_is-barnaland");
Context.Items["Tmx_is-barnaland"] = tmx2;
sw.Restart();
for (int i=0; i < 1000; i++) {
var tmx = (Tmx) Context.Items["Tmx_is-barnaland"];
}
Response.Write("Context:" + sw.ElapsedTicks + "<br />");
These are the results
SharedCache:590505084
Local:16722
Context:1495
The most surprising thing is the big difference between the local cache and the context. Both are in memory storages. I don't know the reason and don't care that much.
So now that I know that some objects are requested multiple time for the same request I've added context caching to our wrapper.
It's as simple adding this to my wrapper
HttpContext.Current.Items[key] = obj;
So each time I request an object from the cache I first check to see if it's available in the context. The result is this
SharedCache:4300
Local:4189
Context:1530
The extra time between Context and the SharedCache and Local cache is because of an overhead the wrapper creates.