Steve Smith, owner of ASP Alliance and Lake Quincy Media joins us today to teach us about some hidden gems in ASP.NET caching and performance. Steve’s expertise in this area comes from first-hand experience as Lake Quincy’s ad system serves over 60 requests per second and handles over 150 million requests per month. Steve is an ASP Insider, Regional Director, Microsoft MVP, INETA Speaker and fellow book author!
What performance metrics are worth tracking?
- Requests per second
- Time to last byte: How long it takes the user to see the page load
Indicators of performance problems include high requests per second, but very long page load times. Alternatively you may notice quick load times, but only for a small number of users. The balance of these two metrics will help you even out the performance of your website.
How do you find bottlenecks?
- Recognize there is
only one bottleneck at a time: The rule of thumb is that there is always
one bottleneck – and changing anything else before addressing the bottleneck
will not improve performance. Most bottlenecks in web applications are external
resources, like a database, web service or the file system.
- Avoid optimizing too
early. Beware of pre-mature optimization as it may cause performance
problems. Without the benefit of production environment (large number of users
going to your site) you may make implementation mistakes.
- Measure: Run load tests and profilers to establish benchmarks
ASP.NET included output caching as a feature from the beginning. Output caching is easy to use and has many different features available.
One feature that many people don’t know about is the control’s ability to vary by browser. This is important because if you have markup optimized for a particular browser and then a user views your site with an un-optimized browser version, your site may look broken. Using the output cache’s vary by browser parameter can help you avoid this problem.
Do you have to be worried about your output cache getting to big?
Yes, but most servers can handle the load that caching will create on the server. Caching saves the rendered markup in memory and then serves it while the cache is still valid, so many cache entries will only be around 5 to 10KB of markup.
ASP.NET’s caching also automatically manages memory so the cache will purge when the server detects memory pressure. Caching uses a least-recently-used (LRU) algorithm to determine which cache entries to purge first. The worst case scenario is that your server will begin to "thrash" alternately caching pages and purging pages upon every request.
The substitution control gives you a way to display dynamic content on a cached page. The control uses a callback to generate a string to substitute in its place before serving the content to the client. For example if you wanted to display the login information for a user on the screen on a cached page, the substitution control can display the user’s name in the middle of cached content.
Even though the control requires a string, you are not limited to simple strings. You may still use rich UI controls - you would simply need to render the control’s HTML and pass it up to the substitution control.
For an example of how to tell a control to render its HTML, Steve has an example that even comes with a nice extension method that makes getting the markup easy for any control.
The only seeming drawback to using the substitution control is the lack of kernel-level caching support in IIS7.
State Bag Access Pattern Gotchas
Many developers will implement caching by using the state access pattern. While this approach is acceptable there is a gotcha you must be aware of: only make a call to the cache only ONCE during a request. If you make multiple calls to the cache an item may be expired or removed between the first and subsequent calls.
Naïve State Bag Access Pattern
if(Cache["customers"] == null)
myList = DAL.ListCustomers();
Cache.Insert("customers", mList, null,
Correct State Bag Access Pattern
string cacheKey = "customers";
Cache[cacheKey] as List
if(myList == null)
myList = DAL.ListCustomers();
Cache.Insert(cacheKey, mList, null,
The caching API lives in the System.Web namespace, but is not limited to a web application or IIS.
Benefits of using the API include:
- Manages memory itself
- Supports dependencies based on
- file system
Limitations of ASP.NET Caching
The built-in ASP.NET caching is app-domain specific, so caching on a web farm is problematic. If you put something into the cache on server "A" the same cache does not exist on server "B". A project in its infancy from Microsoft, codenamed Velocity, will help address this issue. Products available today that supports distributed cache and session are ScaleOut State Server or Alachisoft NCache.
Write caching is the practice of filling a buffer of commands and sending them to the server in infrequent intervals. A Common practice for using write caching is in logging scenarios. The implementation may include a short-lived session or cache object which is accessed by a separate process powered by a timer which takes whatever has built up in the buffer and then executes the commands against the server.
For a code examples read Steve’s article, Use Write Caching to Optimize High Volume Data Driven Applications. While Steve’s article is relevant for SQL 2000 environments, Jason Follas posed an update to the write caching technique that is optimized for SQL 2005 called, Coding in SQL Server: An Evolution. Jason’s article shows you how to implement write caching which requires less code and performs even faster than Steve’s SQL 2000 example.
Finally, you could go even further and make these write caching reliable by implementing a queuing solution.
Client-side caching includes:
- Configuring client cache headers will store static resources at the client and proxy servers
Side Effects of Caching: Logging
Web servers log traffic statistics by counting how many times a file is served off the file system. When you implement caching pages are served from memory and not off the server’s file system. Knowing this, you may need to alter your approach to reporting the traffic on your site.
Most of the caching techniques discussed here include storing the rendered HTML of a page or a control, so request to images are still served as normal even on a cached page. Therefore as you begin to make use caching, you may want to include web beacons (or the infamous clear pixel image) to help you track statistics for your site.
Further, logging implemented with custom logic is also affected. Cached pages do not exist as control objects and therefore cannot execute the code-behind while in cache. If you have logging setup on a page the logic will only run the first time and when the cache expires.
Debugging Cached Applications
Debugging cached applications can be hard if you are expecting code to execute that never runs. A few ways you can help debugging are to:
- Make it easy to turn off caching: Turning off caching in a
development environment will allow you to go straight to problem areas
- An object's rendered HTML is what is cached: Be aware that cached controls exist as the rendered HTML
string in the cache and not the original object. If you try to run a method of
a cached user control you will encounter an exception
Tips and Tricks
- Using an Amazon S3 service for a content delivery network can help relieve
the stress on your server
- Create custom CNAMEs for static resources
- Steve Smith's blog
- ASP Alliance Cache Manager
- Black Belt ASP.NET Performance Session
- Caching in ASP.NET Show on dnrTV