<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Jonathan George&#039;s Blog &#187; performance</title>
	<atom:link href="http://jonathangeorge.co.uk/tag/performance/feed/" rel="self" type="application/rss+xml" />
	<link>http://jonathangeorge.co.uk</link>
	<description>True confessions of a technical architect</description>
	<lastBuildDate>Fri, 30 Sep 2011 07:28:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='jonathangeorge.co.uk' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://0.gravatar.com/blavatar/c6670528bd320dfa4e609e09f50e5df4?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title>Jonathan George&#039;s Blog &#187; performance</title>
		<link>http://jonathangeorge.co.uk</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://jonathangeorge.co.uk/osd.xml" title="Jonathan George&#039;s Blog" />
	<atom:link rel='hub' href='http://jonathangeorge.co.uk/?pushpress=hub'/>
		<item>
		<title>Static Resource Versioning in IIS7</title>
		<link>http://jonathangeorge.co.uk/2010/01/13/static-resource-versioning-in-iis7/</link>
		<comments>http://jonathangeorge.co.uk/2010/01/13/static-resource-versioning-in-iis7/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 22:45:51 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Technical Stuff]]></category>
		<category><![CDATA[iis7]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[yslow]]></category>

		<guid isPermaLink="false">http://jonathangeorge.wordpress.com/2010/01/13/static-resource-versioning-in-iis7/</guid>
		<description><![CDATA[A couple of months ago, I blogged about how to improve your YSlow score under IIS7. One of the recommendations from the Yahoo! best practices guide against which YSlow rates your site is that you should set far-future expiry dates for resources such as scripts, images and stylesheets. This is good advice, and straightforward to [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=47&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>A couple of months ago, I blogged about <a href="http://jonathangeorge.co.uk/2009/09/09/how-to-improve-your-yslow-score-under-iis7/" target="_blank">how to improve your YSlow score under IIS7</a>. One of the recommendations from the <a href="http://developer.yahoo.com/performance/rules.html">Yahoo! best practices guide</a> against which <a href="http://developer.yahoo.com/yslow/" target="_blank">YSlow</a> rates your site is that you should set far-future expiry dates for resources such as scripts, images and stylesheets.</p>
<p>This is good advice, and straightforward to implement under IIS7, but can leave you with a bit of a problem – once you’ve released your site with all of your scripts and stylesheets set to expire next millenium, you can no longer edit them as you please. If you change them and release them with the same name, returning visitors to your site will not see the changes – they will use the cached version that you told them was good for another 1000 years.</p>
<p>Instead, if you need to change one of your files, you need to give it a new name so that clients who downloaded and cached the original version are sure to get the update when it’s deployed. This means you need to update any references to point to the new file, which has the potential to be a bit of a management nightmare.</p>
<p>Obviously, if you’ve only got one or two of each type of file and you’ve referenced them from a master page or similar shared component then you can handle this manually. However, if you’ve got more than that, or if you’re following the recommendation to combine and pack files, then life can get more fiddly.</p>
<p>On a previous project, I’ve used the <a href="http://www.aptimize.com/" target="_blank">Aptimize Website Accelerator</a>, which handles all this stuff for you in an extremely slick way. However, for projects that don’t use this, I wanted to come up with a way of making the process easier.</p>
<p>In order to do this I decided to take advantage of ASP.NET’s internal URL rewriting feature, exposed via the <a href="http://msdn.microsoft.com/en-us/library/system.web.httpcontext.rewritepath.aspx" target="_blank">HttpContext.RewritePath</a> method. What I ended up with – which you can <a href="http://jonathangeorge.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=37347" target="_blank">download from Codeplex</a> if you’d like to give it a try – has two parts, the logic to add and remove version numbers from file names and an HttpModule to apply this logic to incoming Urls. The idea is that the code writing out links to the JS/CSS files can pass those filenames through the provider to add a version number, and the HttpModule will intercept incoming requests for those files and rewrite them to point to the correct filename.</p>
<h2>The resourcing versioning provider</h2>
<p>Whilst I’m not a massive fan of the ASP.NET provider model, I’ve recently been feeling the pain of using a variety of different open source projects with overlapping dependencies on different versions of the same tools. I therefore decided to use the provider model to allow me to swap out different ways of generating the version number.</p>
<p>The first important class here is ResourceVersioningProvider. This defines the two methods that providers need to implement.</p>
<div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:e0369582-8a53-49ae-834b-dce31de8a033" class="wlWriterEditableSmartContent" style="padding:5px;">
<div style="border:#000080 1px solid;font-family:'Courier New', Courier, Monospace;font-size:10pt;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;white-space:wrap;margin:0 0 0 25px;">
<li> <span style="color:#0000ff;">public</span> <span style="color:#0000ff;">abstract</span> <span style="color:#0000ff;">class</span> <span style="color:#2b91af;">ResourceVersioningProvider</span> : <span style="color:#2b91af;">ProviderBase</span></li>
<li style="background:#f3f3f3;"> {</li>
<li> <span style="color:#0000ff;">public</span> <span style="color:#0000ff;">abstract</span> <span style="color:#0000ff;">string</span> AddVersionNumberToFileName(<span style="color:#0000ff;">string</span> fileName);</li>
<li style="background:#f3f3f3;"></li>
<li> <span style="color:#0000ff;">public</span> <span style="color:#0000ff;">abstract</span> <span style="color:#0000ff;">string</span> RemoveVersionNumberFromFileName(<span style="color:#0000ff;">string</span> fileName);</li>
<li style="background:#f3f3f3;"> }</li>
<li></li>
</ol>
</div>
</div>
</div>
<p>It should be pretty obvious what they do from their names.</p>
<p>I’ve then got a base class,  ResourceVersioningProviderBase which contains the code that’s independent of how the version number is generated (basically the filename parsing/modifying logic as well as some caching.) This class is abstract and defines a single abstract method, GetVersionNumber(). This should return the version number that’s going to be used to make the filename unique.</p>
<p>The final relevent class is AssemblyBasedResourceVersioningProvider, which inherits ResourceVersioningProviderBase and implements GetVersionNumber by returning the full version number of an assembly specified in the configuration. The configuration itself looks like this:</p>
<div id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:e8861d1e-825e-41ec-bd82-2938097a1f2c" class="wlWriterEditableSmartContent" style="padding:5px;">
<div style="border:#000080 1px solid;font-family:'Courier New', Courier, Monospace;font-size:10pt;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;white-space:wrap;margin:0 0 0 35px;">
<li> <span style="color:#0000ff;">&lt;</span><span style="color:#a31515;">resourceVersioning</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">default</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">assemblyVersion</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;">&gt;</span></li>
<li style="background:#f3f3f3;"> &lt;<span style="color:#a31515;">providers</span><span style="color:#0000ff;">&gt;</span></li>
<li> &lt;<span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">name</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">assemblyVersion</span>&#8220;<span style="color:#0000ff;"> </span></li>
<li style="background:#f3f3f3;"> <span style="color:#ff0000;">extensionsToVersion</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">.js,.css,.less</span>&#8220;<span style="color:#0000ff;"> </span></li>
<li> <span style="color:#ff0000;">assemblyName</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">WhoCanHelpMe.Web</span>&#8220;<span style="color:#0000ff;"> </span></li>
<li style="background:#f3f3f3;"> <span style="color:#ff0000;">type</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">Cadenza.Web.ResourceVersioning.AssemblyBasedResourceVersioningProvider,</span></li>
<li> Cadenza.Web.ResourceVersioning&#8221;<span style="color:#0000ff;"> /&gt;</span></li>
<li style="background:#f3f3f3;"> &lt;/<span style="color:#a31515;">providers</span><span style="color:#0000ff;">&gt;</span></li>
<li> &lt;/<span style="color:#a31515;">resourceVersioning</span><span style="color:#0000ff;">&gt;</span></li>
<li style="background:#f3f3f3;"></li>
</ol>
</div>
</div>
</div>
<p>As you can see, there’s also an attribute that controls which file extensions the provider will add version numbers to. This means that if you call “AddVersionToFileName” and pass in an a filename with a different extension, no version number will be added.</p>
<h2>The resource versioning HttpModule</h2>
<p>The HttpModule that forms the other half of the solution is pretty straightforward. Every incoming Url is cracked open using the Uri.Segments property, and the final segment (assumed to be the requested file name) is passed into the provider to remove the version number, if present. If the provider returns a filename that differs from the one in the request url, it is used to build a new Url that can be passed to HttpContext.RewritePath. Processed Urls are cached (regardless of whether or not they require a rewrite) to improve performance.</p>
<h2>In Practice</h2>
<p>The module is in use on the <a href="http://who-can-help.me/" target="_blank">Who Can Help Me?</a> app I introduced in my previous post. If you have a look at the source for the homepage, you’ll see that all the CSS, JavaScript and <a href="http://dotlesscss.com/" target="_blank">DotLess</a> files have the current WhoCanHelpMe.Web assembly version number as part of their name. You can get at <a href="http://whocanhelpme.codeplex.com/" target="_blank">the Who Can Help Me? source via it’s Codeplex site</a> to see how it’s implemented.</p>
<h2>An Alternative Approach</h2>
<p>Despite having written this post a month or so ago, I was only spurred into posting it by reading about <a href="http://codebetter.com/blogs/karlseguin/archive/2010/01/11/asp-net-performance-part-3-cache-busting.aspx" target="_blank">this alternative approach</a> from <a href="http://codebetter.com/blogs/karlseguin/default.aspx" target="_blank">Karl Seguin</a>. He’s got an interesting series going on at the moment covering ASP.NET performance – it’s well worth a read. I can see that it would be fairly straightforward to replace my AssemblyBasedResourceVersioningProvider with a FileHashBasedResourceVersioningProvider, although given that no. 1 on my to do list at the moment is learning  NHibernate, it might be a while before I get round to it!</p>
<p>As I mentioned above, <a href="http://jonathangeorge.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=37347" target="_blank">the compiled assemblies</a> and the source code are available from <a href="http://jonathangeorge.codeplex.com/" target="_blank">my Codeplex site</a>. Please have a play and let me know what you think.</p>
<p><a href="http://twitter.com/jon_george1" target="_blank">@jon_george1</a></p>
<br /> Tagged: iis7, performance, yslow <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathangeorge.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathangeorge.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathangeorge.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathangeorge.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/jonathangeorge.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/jonathangeorge.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/jonathangeorge.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/jonathangeorge.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathangeorge.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathangeorge.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathangeorge.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathangeorge.wordpress.com/47/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathangeorge.wordpress.com/47/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathangeorge.wordpress.com/47/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=47&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://jonathangeorge.co.uk/2010/01/13/static-resource-versioning-in-iis7/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6038fabf4927137a504608f99b48f91f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Jonathan</media:title>
		</media:content>
	</item>
		<item>
		<title>Optimising an ASP.NET MVC web site, part 5: Putting your money where your mouth is</title>
		<link>http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-5-putting-your-money-where-your-mouth-is/</link>
		<comments>http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-5-putting-your-money-where-your-mouth-is/#comments</comments>
		<pubDate>Tue, 03 Nov 2009 17:51:00 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Technical Stuff]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[asp.net mvc]]></category>
		<category><![CDATA[n2cms]]></category>
		<category><![CDATA[nhibernate]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[s#arp architecture]]></category>
		<category><![CDATA[solr]]></category>
		<category><![CDATA[spark]]></category>

		<guid isPermaLink="false">http://jonathangeorge.wordpress.com/2009/11/03/optimising-an-asp-net-mvc-web-site-part-5-putting-your-money-where-your-mouth-is/</guid>
		<description><![CDATA[Note: This was originally posted on my old blog at the EMC Consulting Blogs site. This is the final part of a series of posts on optimisation work we carried out on my last project, www.fancydressoutfitters.co.uk – an ASP.NET MVC web site built using S#arp Architecture, NHibernate, the Spark view engine and Solr. There’s not [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=27&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>Note: This was originally posted on </strong><a href="http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/09/how-to-improve-your-yslow-score-under-iis7.aspx"><strong>my old blog</strong></a><strong> at the </strong><a href="http://consultingblogs.emc.com/"><strong>EMC Consulting Blogs site</strong></a><strong>.</strong></p>
<p><i>This is the final part of a series of posts on optimisation work we carried out on my last project, <a href="http://www.fancydressoutfitters.co.uk">www.fancydressoutfitters.co.uk</a> – an ASP.NET MVC web site built using </i><a href="http://code.google.com/p/sharp-architecture/"><i>S#arp Architecture</i></a><i>, </i><a href="https://www.hibernate.org/343.html"><i>NHibernate</i></a><i>, the </i><a href="http://sparkviewengine.com/"><i>Spark view engine</i></a><i> and </i><a href="http://lucene.apache.org/solr/"><i>Solr</i></a><i>. There’s not much point starting here – please have a look at parts <a href="http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-1-introduction/">1</a>, <a href="http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-2-database-and-nhibernate/">2</a>, <a href="http://jonathangeorge.co.uk/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/">3</a> and <a href="http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-4-output-caching-in-the-brave-new-world-of-mvc/" target="_blank">4</a>, as well as <a href="http://jonathangeorge.co.uk/2009/09/09/how-to-improve-your-yslow-score-under-iis7/">my post on improving YSlow scores for IIS7 sites</a>, for the full picture.</i></p>
<p>In the posts on this series, I’ve reflected the separation of concerns inherent in ASP.NET MVC applications by talking about how we optimised each layer of the application independently. Good separation of concerns is by no means unique to applications built using the MVC pattern, but what stood out for me as I became familiar with the project was that for the first time it seemed like I hardly had to think to achieve it, because it’s so baked into the framework. I know I share this feeling with <a href="http://consultingblogs.emc.com/howardvanrooijen/default.aspx" target="_blank">Howard</a> and <a href="http://consultingblogs.emc.com/jamesbroome/" target="_blank">James</a> (respectively architect and developer on the project), who’ve both talked about it in their own blogs.</p>
<p>The MVC pattern also makes it much easier to apply optimisations in the code. For example, it’s much easier to identify the points where caching will be effective, as the Model-View-ViewModel pattern makes it straightforward to apply a <a href="http://jonathangeorge.co.uk/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/" target="_blank">simple and highly effective caching pattern</a> within the controllers. I know that this kind of thing isn’t limited to performance work – for example, our team security guru certainly felt that it was easier to carry out his threat modelling for this site than it would have been in a WebForms equivalent.</p>
<p>On the flip side, this process also brought home to me some of the dangers of using NHibernate. It’s an absolutely awesome product, and has totally converted me to the use of an ORM (be it NHib or Entity Framework). However, the relatively high learning curve and the fact that most of the setup was done before I joined the project made it easy for me to ignore what it was doing under the covers and code away against my domain objects in a state of blissful ignorance. Obviously this is not a brilliant idea, and properly getting to grips with NH it now jostling for first place on my to-do list (up against <a href="http://www.postsharp.org/" target="_blank">PostSharp 2</a> and <a href="http://haacked.com/archive/2009/10/01/asp.net-mvc-preview-2-released.aspx" target="_blank">ASP.NET MVC 2.0</a>, amongst other things.)</p>
<p>My big challenge for future projects is ensuring that the optimisations I’ve talked about are baked in from the start instead of being bolted on at the end. The problem with this is that it’s not always clear where to stop. The goal of optimising the site is to get it to the point where it performs as we need it to, not to get it to the point where we can’t optimise any more. The process of optimisation is one of diminishing returns, so it’s essential to cover issues you know need to be covered and to then use testing tools to uncover any further areas to work on.</p>
<p>That said, in an ideal world I’d like to be able to build performance tests early and use them to benchmark pages on a regular basis. Assuming you work in short iterations, this can be done on an iteration by iteration basis, with results feeding into the plan for the next iteration. My next series of posts will be on performance and load testing, and as well as covering what we did for this project I will be looking at ways of building these processes into the core engineering practices of a project.</p>
<h2>Was it all worth it?</h2>
<p>I’ll be talking separately about the performance and load testing we carried out on the site prior to go live, but in order to put these posts into some context I thought it might be interesting to include some final numbers. For our soak testing, we built a load profile based on 6 user journeys through the site:</p>
<ul>
<li>Homepage: 20% of total concurrent user load </li>
<li>Browse (Home -&gt; Category Landing -&gt; Category Listing -&gt; Product): 30% </li>
<li>Search (Home -&gt; Search Results): 30% </li>
<li>News (Home -&gt; News list -&gt; News story): 10% </li>
<li>Static Pages (Home -&gt; Static page): 5% </li>
<li>Checkout (As for browse journey, then -&gt; Add to basket -&gt; View Basket -&gt; Checkout): 5% </li>
</ul>
<p>With a random think time of 8 – 12 seconds between each step of each journey, we demonstrated that each of the web servers in the farm could sustainably support 1000 concurrent users and generate 90 pages per second. Given the hardware in question, this far exceeds any project I’ve worked on recently.</p>
<p>In the end, we put <a href="http://www.fancydressoutfitters.co.uk">www.fancydressoutfitters.co.uk</a> live in the run up to Halloween, the busiest time of the year for the fancy dress industry. We did this with no late nights and enough confidence to go to the pub for a celebratory pint within the hour. It was also interesting that the majority of technical colleagues who responded to our go-live announcement commented on how fast it runs (which given the machinations of our corporate network’s internet routing is even more remarkable.) And best of all, we’ve had no major shocks since the site went live.</p>
<h2>A final note</h2>
<p>If you’ve read this series of posts, I hope you’ve got something out of it. I’d certainly be interested in any feedback that you might have – as always, please feel free to leave a comment or contact me on <a href="http://twitter.com/jon_george1" target="_blank">Twitter</a>.&#160; In addition, the EMC Consulting blog site has been nominated in the <a href="http://www.computerweekly.com/Articles/2009/09/14/237679/it-blog-awards-2009-name-your-favourite-blogger.htm" target="_blank">Computer Weekly IT Blog Awards 2009</a>, under the <a href="http://www.computerweekly.com/Articles/2009/09/20/237826/it-blog-awards-2009-company-corporate-large-enterprise.htm" target="_blank">“Corporate/Large Enterprise” category</a> – please consider <a href="http://www.computerweekly.com/Articles/2009/11/03/238190/vote-in-the-computer-weekly-it-blog-awards-2009.htm" target="_blank">voting for us</a>.</p>
<p>I’d also like to extend a final thanks to <a href="http://consultingblogs.emc.com/howardvanrooijen/default.aspx" target="_blank">Howard</a> for proof reading the first draft of these posts and giving me valuable feedback, as well as for actually doing a lot of the work I’ve talked about here.</p>
<p><a href="http://twitter.com/jon_george1" target="_blank">@jon_george1</a></p>
<br /> Tagged: asp.net, asp.net mvc, n2cms, nhibernate, performance, s#arp architecture, solr, spark <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathangeorge.wordpress.com/27/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathangeorge.wordpress.com/27/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathangeorge.wordpress.com/27/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathangeorge.wordpress.com/27/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/jonathangeorge.wordpress.com/27/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/jonathangeorge.wordpress.com/27/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/jonathangeorge.wordpress.com/27/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/jonathangeorge.wordpress.com/27/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathangeorge.wordpress.com/27/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathangeorge.wordpress.com/27/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathangeorge.wordpress.com/27/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathangeorge.wordpress.com/27/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathangeorge.wordpress.com/27/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathangeorge.wordpress.com/27/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=27&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-5-putting-your-money-where-your-mouth-is/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6038fabf4927137a504608f99b48f91f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Jonathan</media:title>
		</media:content>
	</item>
		<item>
		<title>Optimising an ASP.NET MVC web site, part 4: Output caching in the brave new world of MVC</title>
		<link>http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-4-output-caching-in-the-brave-new-world-of-mvc/</link>
		<comments>http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-4-output-caching-in-the-brave-new-world-of-mvc/#comments</comments>
		<pubDate>Tue, 03 Nov 2009 16:11:00 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Technical Stuff]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[asp.net mvc]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[s#arp architecture]]></category>
		<category><![CDATA[spark]]></category>

		<guid isPermaLink="false">http://jonathangeorge.wordpress.com/2009/10/12/optimising-an-asp-net-mvc-web-site-part-4-output-caching-in-the-brave-new-world-of-mvc/</guid>
		<description><![CDATA[Note: This was originally posted on my old blog at the EMC Consulting Blogs site. This is the penultimate part in a series of posts on optimisation work we carried out on my last project, www.fancydressoutfitters.co.uk – an ASP.NET MVC web site built using S#arp Architecture, NHibernate, the Spark view engine and Solr. Please also [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=25&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>Note: This was originally posted on </strong><a href="http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/09/how-to-improve-your-yslow-score-under-iis7.aspx"><strong>my old blog</strong></a><strong> at the </strong><a href="http://consultingblogs.emc.com/"><strong>EMC Consulting Blogs site</strong></a><strong>.</strong></p>
<p><em>This is the penultimate part in a series of posts on optimisation work we carried out on my last project, <a href="http://www.fancydressoutfitters.co.uk">www.fancydressoutfitters.co.uk</a> – an ASP.NET MVC web site built using </em><a href="http://code.google.com/p/sharp-architecture/"><em>S#arp Architecture</em></a><em>, </em><a href="https://www.hibernate.org/343.html"><em>NHibernate</em></a><em>, the </em><a href="http://sparkviewengine.com/"><em>Spark view engine</em></a><em> and </em><a href="http://lucene.apache.org/solr/"><em>Solr</em></a><em>. Please also take the time to read parts <a href="http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-1-introduction/" target="_blank">1</a>, <a href="http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-2-database-and-nhibernate/" target="_blank">2</a> and <a href="http://jonathangeorge.co.uk/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/" target="_blank">3</a> for the full picture.</em></p>
<p>Output caching is something I’ve used extensively on previous ASP.NET WebForm projects, and I was surprised when I found out that it’s relatively immature in ASP.NET MVC. The core library contains the <a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.outputcacheattribute.aspx" target="_blank">OutputCache</a> filter, which does the same under the covers as the <a href="http://msdn.microsoft.com/en-us/library/hdxfb6cy.aspx" target="_blank">@OutputCache</a> declaration in ASP.NET Web Forms. However, caching a full page is something that rarely suits in the current climate of dynamic and personalised websites – for example (and in common with most ecommerce websites) our site includes a summary of the user’s basket on every page. In the world of web forms, I’d use the <a href="http://weblogs.asp.net/scottgu/archive/2006/11/28/tip-trick-implement-donut-caching-with-the-asp-net-2-0-output-cache-substitution-feature.aspx" target="_blank">asp:substitution control to implement donut caching</a>, or implement <a href="http://support.microsoft.com/kb/308378" target="_blank">fragment caching</a>. Unfortunately neither of those options exist in the brave new world of ASP.NET MVC (if you’re a Java/Ruby/etc developer… stop sniggering, we’re catching you up <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  ) </p>
<p>So there are a few ways round this that I’m aware of. The first involves using the partial views to do something similar to the old style ASP.NET Fragment Caching approach. In the WebForms world, this is implemented by splitting your page up into multiple user controls, and applying output caching as appropriate to the individual controls. Here’s a pretty basic example of how a product page on an ecommerce website might be laid out.</p>
<p><img style="display:block;float:none;margin-left:auto;margin-right:auto;border-width:0;" title="Page layout" border="0" alt="Page layout" src="http://jonathangeorge.files.wordpress.com/2009/11/pagelayout.png?w=527&#038;h=318" width="527" height="318" /> </p>
<p>The boxes coloured blue, green and purple can all be cached – albeit with different cache lifetimes and keys. The red box, which displays my user&#160; basket total is unlikely to be cachable – at least not at the rendering level. Using the classic ASP.NET fragment caching approach, each of my coloured boxes would be implemented as a separate user control with appropriate OutputCache directives set. The ASPX page itself would contain very little other than the instances of these controls.</p>
<p>So how does this translate into MVC? It turns out that it’s pretty much the same. You create a number of partial views and add an OutputCache directive to their markup. Phil Haack <a href="http://haacked.com/archive/2009/05/12/donut-hole-caching.aspx" target="_blank">covers the approach in this blog post.</a> There’s just one downside: that approach only works with the standard WebForms view engine, and we’re using Spark, so we can’t go down this route.</p>
<p>I did hear a suggestion that making use of the RenderAction method from ASP.NET MVC 1.0 Futures could be the way forward. Essentially, each of my coloured boxes from the diagram would end up corresponding to a separate controller action, each of which would have an OutputCache action filter applied. These would then be pulled together by a “dumb” controller action whose corresponding view had multiple calls to Html.RenderAction to compose the chunks of the view together.</p>
<p>On the face of it – and assuming you were willing to accept the overhead involved in repeatedly invoking the full MVC request pipeline – it would work. However, there has been <a href="http://stackoverflow.com/questions/606962/outputcache-and-renderaction-cache-whole-page" target="_blank">mention of a bug with MVC 1.0</a> which causes the OutputCache filter on an action method called with RenderAction to be applied to the overall request, not just the chunk being dealt with. This kind of thing is probably why the RenderAction method was bumped into MVC Futures instead of being shipped as part of the core MVC 1.0 release.</p>
<p>Phil Haack <a href="http://haacked.com/archive/2008/11/05/donut-caching-in-asp.net-mvc.aspx" target="_blank">blogged something else that didn’t quite make it to MVC 1.0</a> which looked good on the surface. Essentially, it’s an HtmlHelper extension that hooks into the API used by the asp:substitution control. However, I had a bit of a headache in trying to work out how to use it. The problem is that within the callback you supply to the Substitute method, you don’t have access to your ViewData – not a massive surprised once you understand how the post-cache substitution mechanism in ASP.NET works. This means that you need to code some other method – which is going to be a static method you’ve stashed alongside your controllers – that will do all the necessary work, pulling bits of data out of the supplied HttpContext and returning a string to be dumped directly into the view.</p>
<p>There’s no doubt that this would work, and with some thought could be done without losing the testability and separation of concerns that makes the MVC pattern great. However, it’s not an ideal approach for me because it does mean that the pattern and conventions are broken to support what’s essentially an optimisation step. Because of this it will make it harder for people to see what’s going on in the code. I’ve already covered the caching we applied in the service layer; to me, output caching should be contained within the View Engine layer and should not leak beyond that. After all, there’s nothing else in my controller layer or below that couples my code to Spark, so I have no desire to introduce something that does.</p>
<p>Fortunately it turns out that the <a href="http://sparkviewengine.com/documentation/caching" target="_blank">1.1 version of the Spark view engine will contain pretty comprehensive output caching</a>. This isn’t part of the v1.0 release, but has been in the development builds for several months now and is stable. It’s absolutely perfect for what I wanted as it allowed me to implement output caching with very few changes outside the views.</p>
<p>Unlike the ASP.NET WebForms fragment caching approach, you aren’t required to split your view into partials – you simply add &lt;cache&gt; elements around the sections of the page you want to cache. These sections can be dependent on different keys and cached for different durations, and there’s also a <a href="http://sparkviewengine.com/documentation/caching#Signalingdependentdatachanged" target="_blank">signalling mechanism</a> that allows cached content to be expired on demand. In our case, we had already tackled the issue of cache keys for a ViewModels when we looked at <a href="http://jonathangeorge.co.uk/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/" target="_blank">caching in the controller layer</a>, so it was a simple matter to use these same cache keys to control our output caching.</p>
<p>Spark also contains something called the <a href="http://sparkviewengine.com/documentation/caching#UsingValueHolderforcleanviews" target="_blank">ValueHolder</a> which effectively allows you to defer the gathering of model data until it’s needed. This means that rather than build up the model for every request, only to pass it to a view which doesn’t need it because it’s entirely output cached, you can build your model using ValueHolder objects containing lambda functions that will only be executed if the data is needed. This seems like an interesting approach, but it’s not one I explored in detail because the caching we’d already implemented on the controllers made it less relevant.</p>
<p>One of my major hopes, which was unfortunately not realised, was that we’d be able to connect Spark’s output caching service to our distributed cache, <a href="http://msdn.microsoft.com/en-us/data/cc655792.aspx" target="_blank">Velocity</a>. This would further reduce the workload across the web farm because it would mean that once a page was served from one webhead, it would be available pre-built to all of the others. However the output caching mechanism in Spark places unserializable objects into the cache, making it difficult to use with an out-of-process caching mechanism. This is a shame but by no means a deal breaker.</p>
<p>I’ve seen a few discussions around the value of output caching in the MVC world, with some saying that because the views are essentially just writing text to a stream, there is little gain to be had from caching. On a purely subjective level, the output caching did seem to make the site faster. It’s difficult to be sure because there is no way of enabling/disabling output caching via config in Spark, so it’s not easy to do comparative tests in a realistic environment. I can see the argument, and I’d certainly agree that out of the different focus areas output caching made the least difference to overall performance, but I believe it did make a difference and for the minimal effort involved in implementing it, was worth it.</p>
<p>In the final section, I’ll talk about my views on optimising this MVC based site compared to my experiences in the WebForms world, and share some hard numbers gained from our performance testing.</p>
<p><a href="http://twitter.com/jon_george1" target="_blank">@jon_george1</a></p>
<br /> Tagged: asp.net, asp.net mvc, performance, s#arp architecture, spark <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathangeorge.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathangeorge.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathangeorge.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathangeorge.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/jonathangeorge.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/jonathangeorge.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/jonathangeorge.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/jonathangeorge.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathangeorge.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathangeorge.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathangeorge.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathangeorge.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathangeorge.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathangeorge.wordpress.com/25/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=25&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-4-output-caching-in-the-brave-new-world-of-mvc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6038fabf4927137a504608f99b48f91f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Jonathan</media:title>
		</media:content>

		<media:content url="http://jonathangeorge.files.wordpress.com/2009/11/pagelayout.png" medium="image">
			<media:title type="html">Page layout</media:title>
		</media:content>
	</item>
		<item>
		<title>Optimising an ASP.NET MVC web site, part 3: Application Caching</title>
		<link>http://jonathangeorge.co.uk/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/</link>
		<comments>http://jonathangeorge.co.uk/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/#comments</comments>
		<pubDate>Mon, 12 Oct 2009 21:46:00 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Technical Stuff]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[asp.net mvc]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[s#arp architecture]]></category>

		<guid isPermaLink="false">http://jonathangeorge.wordpress.com/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/</guid>
		<description><![CDATA[Note: This was originally posted on my old blog at the EMC Consulting Blogs site. This is part 3 in a series of posts on optimisation work we carried out on my current project, www.fancydressoutfitters.co.uk – an ASP.NET MVC web site built using S#arp Architecture, NHibernate, the Spark view engine and Solr. Please see&#160; part [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=23&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>Note: This was originally posted on </strong><a href="http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/09/how-to-improve-your-yslow-score-under-iis7.aspx"><strong>my old blog</strong></a><strong> at the </strong><a href="http://consultingblogs.emc.com/"><strong>EMC Consulting Blogs site</strong></a><strong>.</strong></p>
<p><i>This is part 3 in a series of posts on optimisation work we carried out on my current project, <a href="http://www.fancydressoutfitters.co.uk">www.fancydressoutfitters.co.uk</a> – an ASP.NET MVC web site built using <a href="http://code.google.com/p/sharp-architecture/" target="_blank">S#arp Architecture</a>, <a href="https://www.hibernate.org/343.html" target="_blank">NHibernate</a>, the <a href="http://sparkviewengine.com/" target="_blank">Spark view engine</a> and <a href="http://lucene.apache.org/solr/" target="_blank">Solr</a>. Please see&#160; <a href="http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-1-introduction/">part 1</a> and <a href="http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-2-database-and-nhibernate/">part 2</a> for the background.</i></p>
<p>I doubt many people will disagree with me when I say that one sure-fire way of making a piece of code run faster and consume less of your precious processing power is to do less work in that piece of code. Optimising your code is part of the answer, but however quickly your pages run when it’s just you on the site you will struggle to serve large numbers of users unless you have a good caching strategy in place.</p>
<p>There are a number of places that caching can be applied in an application built using S#arp Architecture. Here are a few:</p>
<ul>
<li>You can use the NHibernate second-level cache to store the results of database queries. </li>
<li>You can apply caching wherever it’s appropriate in your model layer. </li>
<li>You can cache the results of controller actions; and </li>
<li>You can apply output caching to the rendered views (to be covered in part 4). </li>
</ul>
<p>However the whole thing is something of a balancing act. You have to understand how often things are going change and weigh up the benefits of longer caching durations against the need to reflect updates in a timely fashion. You need to understand how your platform architecture affects your caching strategy – for example, what happens if you have multiple web servers each maintaining it’s own local cache? You also need to make sure that once you’ve decided on an approach, people stick to it. I’ve never been a big fan of using the standard HTTP Cache directly – it requires you to specify the caching interval every time you add an object to the cache. Even if you implement some constants to hold the intervals, you still run the risk of people choosing the wrong one.</p>
<h2>How we approach caching</h2>
<p>Both my current S#arp Architecture-based project and my previous WebForms one make extensive use of Dependency Injection to decouple our assemblies and increase testability. It follows that we needed to create an abstraction for the cache anyway, so we took the opportunity to kill two birds with one stone and introduce a more convention-based approach to the cache. <a href="http://consultingblogs.emc.com/jamesbroome/" target="_blank">James</a> has recently blogged about <a href="http://consultingblogs.emc.com/jamesbroome/archive/2009/10/06/the-importance-of-conventions-from-asp-net-mvc-to-a-successful-project-team.aspx" target="_blank">the benefits of removing decisions from our day-to-day coding activities</a>, and this is another example. In our abstraction, we treat the cache as a set of named areas and assign cache duration to those areas instead of to the individual objects. A few examples of the named areas we use are:</p>
<ul>
<li>Content – medium lifespan, intended for content pulled from the CMS; </li>
<li>Resources – very long lifespan, containing values from our SQL-based resource provider (used instead of the standard RESX file-based provider); and </li>
<li>Site Furniture – long lifespan, containing less frequently changing data used in the site header and footer. </li>
</ul>
<p>(<i>Note: If this approach interests you at all, let me know – the specifics of the interface and implementation are two much detail to include here, but I can cover them in a separate post if there is interest.)</i></p>
<p>A nice bonus from using this abstraction on the current project was that it allowed us to use the current CTP of Velocity, <a href="http://msdn.microsoft.com/en-us/data/cc655792.aspx" target="_blank">Microsoft’s new distributed cache</a>. The abstraction – together with our use of the <a href="http://www.castleproject.org/container/index.html" target="_blank">Windsor IoC container</a> – provided us with a handy safety net: if at any point we found an issue with using Velocity, switching back to the standard HTTP cache would be a simple configuration change. As I’ll cover in a future post our soak testing showed that Velocity is fast and stable, but even post go-live, it would still be a simple matter to make the switch if necessary. One of the tenets of <a href="http://www.amazon.co.uk/Lean-Software-Development-Agile-Toolkit/dp/0321150783/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1255378929&amp;sr=8-1" target="_blank">lean development</a> is that you should delay decision making until the <a href="http://www.codinghorror.com/blog/archives/000705.html" target="_blank">last responsible moment</a> – for us, the last responsible moment for deciding on a caching solution could be deferred until go-live, by which point we’d been using Velocity for around 5 months.</p>
<h2>Applying caching in the controllers</h2>
<p>Our first area of focus was to look at caching the results of controller actions. One of the great things about ASP.NET MVC is that things are so much simpler than in the web forms world (<a href="http://blog.codeville.net/2009/10/08/aspnet-mvc-learning-resource-request-handling-pipeline-poster/" target="_blank">check this out for a pretty picture of the request handling pipeline</a>). It’s far easier to understand where you do and don’t need to apply caching in your application code, and we realised that the most effective thing to do was to put the majority of ours in the controllers.</p>
<p><a href="http://consultingblogs.emc.com/howardvanrooijen/" target="_blank">Howard</a> posted a diagram of our architecture on his <a href="http://consultingblogs.emc.com/howardvanrooijen/archive/2009/10/11/Simplify-Separation-of-Concerns-in-ASP.NET-MVC-with-AutoMapper.aspx" target="_blank">recent blog post</a> about <a href="http://www.codeplex.com/AutoMapper" target="_blank">AutoMapper</a>. From this you might be able to infer that our controllers tend to follow a pretty simple pattern:</p>
<ol>
<li>Input view model is populated from request data by a model binder. </li>
<li>Input view model is mapped to domain objects. </li>
<li>Application services layer is used to do work with those domain objects. </li>
<li>Resulting domain objects are translated back into a view model that can be passed to the view. </li>
<li>Appropriate ActionResult is returned. </li>
</ol>
<p>This is the most complex scenario – not all steps are required for every action.</p>
<p>In an ideal world we’d be caching the ActionResults returned by the controllers but this isn’t something we can do because they aren’t serializable and therefore can’t be cached in Velocity. We therefore have to settle for caching the ViewModel for each action, which gives us this pattern:</p>
<div style="padding:5px;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:01a6bc52-484b-4c69-98a4-eb04ecea906c" class="wlWriterSmartContent">
<div style="border-bottom:#000080 1px solid;border-left:#000080 1px solid;font-family:&#39;font-size:10pt;border-top:#000080 1px solid;border-right:#000080 1px solid;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;margin:0 0 0 35px;">
<li>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:#0000ff;">public</span> ActionResult Index() </li>
<li style="background:#f3f3f3;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; { </li>
<li>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:#0000ff;">return</span> View(Views.Index, MasterPages.Default, <span style="color:#0000ff;">this</span>.IndexInner()); </li>
<li style="background:#f3f3f3;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </li>
<li></li>
<li style="background:#f3f3f3;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; [Cached] </li>
<li>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:#0000ff;">private</span> MyViewModel IndexInner() </li>
<li style="background:#f3f3f3;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; { </li>
<li>&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </li>
<li style="background:#f3f3f3;"></li>
</ol></div>
</p></div>
</p></div>
<p>The [Cached] attribute is actually a <a href="http://www.postsharp.org/" target="_blank">PostSharp aspect</a> that caches the result of the method call, and I have both good intentions and half finished blog posts on this subject. </p>
<p>If the action method takes parameters, these are passed straight through to the ActionInner method – we do the minimum amount of work possible in the action method itself.</p>
<h2>Dealing with changing content in the CMS</h2>
<p>In an ideal world, we would set most of the caches to have an extremely long life, then implement a mechanism to allow individual caches to be flushed as and when required. Anyone who has ever used Microsoft Commerce Server will be familiar with the <a href="http://msdn.microsoft.com/en-us/library/microsoft.commerceserver.runtime.sitecacherefresh.aspx" target="_blank">SiteCacheRefresh</a> handler which allows caches to be cleared in response to a web request. However we encountered an issue here: the current release of Velocity (CTP3), does not support enumeration or flushing of named caches &#8211; these operations are limited to cache regions. The downside with cache regions is that they are tied to a single server, which pretty much removes the benefits of using a distributed cache.</p>
<p>As I’ve previously mentioned, our system uses N2 for Content Management and the majority of our pages are represented in the CMS. We therefore cached the ViewModel for each page using a key based on the unique ID of the page and the last updated date of the page. This works well because it means that pages can be cached indefinitely, but as soon as someone makes a change to the page then a new version will be created and cached. Obviously this means that the old version will be lingering in the background – the downside to this approach is that you may well fill up the cache more quickly and require Velocity to evict more frequently.</p>
<p>This approach isn’t perfect – it doesn’t cover the product pages, and doesn’t take care of changes in the site hierarchy that would mean we need to regenerate the navigation.&#160; As a result, we’ve implemented an additional hack: all cache keys used for content pages include the last updated time of a single page within the site. Touching that page will cause every cache key used at the controller level to change. I don’t particularly like it, but it does work.</p>
<p>The longer term plan would be to look at this again as and when Velocity moves on. With any luck, the next CTP (<a href="http://blogs.msdn.com/velocity/archive/2009/06/10/velocity-next-release-ctp4.aspx" target="_blank">now overdue</a>) will make some changes in this area. The ideal plan would be to:</p>
<ul>
<li>Hook into N2’s persistence mechanism – from reading the documentation and looking at the code, it should be possible to receive a notification when things change.&#160; This could then be used to remove stale objects from the cache, or flush an entire named area as required. </li>
<li>Implement a restricted controller that allows clearing of individual caches (in the style of the CS SiteCacheRefresh handler mentioned above.) This would be useful if we needed to clear a cache in response to an external action – such as an ETL process pulling data from a back office system. </li>
</ul>
<h2>Caching elsewhere in the application</h2>
<p>Once we’d implemented the caching in the controllers and configured the NHibernate second-level cache in both our custom code and N2, there were very few places in our model layer that we needed to apply caching. Once again we hit the point of diminishing returns – we could spend time profiling the application and identifying any remaining bottlenecks, or we could move on and look at what we expected to be the final big win: output caching, which I’ll be talking about in the next post of this series.</p>
<h2>A final word about caching</h2>
<p>On a lot of projects I’ve worked on, caching is something that tends to be implemented at the end. There are advantages to this – it can help to avoid premature optimisation, and you will (hopefully) have a good understanding of the moving parts in your codebase. However, there is one major downside that anyone who’s done this will be familiar with: once you check in the changes, every single bug that is raised for the application will be blamed on your caching. Accept it, live with it, and be prepared to be known as a) grumpy, and b) easy to wind up. Just take satisfaction in the knowledge that when you get it right, you will be setting yourself up for success when you finally put your site live.</p>
<p>As always, I’d appreciate feedback on these posts – either by leaving a comment, dropping me an email or via Twitter.</p>
<p><a href="http://twitter.com/jon_george1" target="_blank">@jon_george1</a></p>
<br /> Tagged: asp.net, asp.net mvc, performance, s#arp architecture <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathangeorge.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathangeorge.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathangeorge.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathangeorge.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/jonathangeorge.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/jonathangeorge.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/jonathangeorge.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/jonathangeorge.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathangeorge.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathangeorge.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathangeorge.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathangeorge.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathangeorge.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathangeorge.wordpress.com/23/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=23&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://jonathangeorge.co.uk/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6038fabf4927137a504608f99b48f91f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Jonathan</media:title>
		</media:content>
	</item>
		<item>
		<title>Optimising an ASP.NET MVC web site, part 2: Database and NHibernate</title>
		<link>http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-2-database-and-nhibernate/</link>
		<comments>http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-2-database-and-nhibernate/#comments</comments>
		<pubDate>Sat, 03 Oct 2009 23:28:00 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Technical Stuff]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[asp.net mvc]]></category>
		<category><![CDATA[nhibernate]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[s#arp architecture]]></category>
		<category><![CDATA[solr]]></category>

		<guid isPermaLink="false">http://jonathangeorge.wordpress.com/2009/10/03/optimising-an-asp-net-mvc-web-site-part-2-database-and-nhibernate/</guid>
		<description><![CDATA[Note: This was originally posted on my old blog at the EMC Consulting Blogs site. This is part 2 in a series of posts on optimisation work we carried out on my current project – an ASP.NET MVC web site built using S#arp Architecture, NHibernate, the Spark view engine and Solr. Please see “Optimising an [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=22&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>Note: This was originally posted on </strong><a href="http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/09/how-to-improve-your-yslow-score-under-iis7.aspx"><strong>my old blog</strong></a><strong> at the </strong><a href="http://consultingblogs.emc.com/"><strong>EMC Consulting Blogs site</strong></a><strong>.</strong></p>
<p><i>This is part 2 in a series of posts on optimisation work we carried out on my current project – an ASP.NET MVC web site built using <a href="http://code.google.com/p/sharp-architecture/" target="_blank">S#arp Architecture</a>, <a href="https://www.hibernate.org/343.html" target="_blank">NHibernate</a>, the <a href="http://sparkviewengine.com/" target="_blank">Spark view engine</a> and <a href="http://lucene.apache.org/solr/" target="_blank">Solr</a>. Please see <a href="http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-1-introduction/">“Optimising an ASP.NET MVC web site part 1 – Introduction”</a> for the background.</i></p>
<p>Whilst there are many different things you can do to a web application to kill performance, having a badly implemented database &#8211; or a well implemented database that you access in a bad way – has got to be right up there with the best. We therefore made the database our first port of call, and started off with a pretty simple approach – fire up&#160; SQL Server Profiler, start a new trace and see what the pain points are in each database. </p>
<h2>Using SQL Server Profiler to find data access mistakes</h2>
<p>As I mentioned in the introduction, the goal of our optimisation wasn’t to do everything, just to get the main things we thought were causing problems before we began our load testing. If you use the TSQL_Duration template in SQL Profiler for your trace, you can hit your site and quickly see what the most expensive queries are. </p>
<p><a href="http://jonathangeorge.files.wordpress.com/2009/11/sampletsql_durationtrace.png"><img style="display:inline;border-width:0;" title="Sample TSQL_Duration Trace" border="0" alt="Sample TSQL_Duration Trace" src="http://jonathangeorge.files.wordpress.com/2009/11/sampletsql_durationtrace_thumb.png?w=680&#038;h=345" width="680" height="345" /></a> </p>
<p>The screenshot above shows the results of repeatedly executing three different stored procedures in a random order. As you can see, the results are organised in ascending order of cost, with the interesting column being “Duration” – which shows the execution time in milliseonds.</p>
<p>When <a href="http://blogs.conchango.com/howardvanrooijen/default.aspx" target="_blank">Howard</a> first ran this against some of our landing and search result pages he quickly noticed a large nugget of smelliness at the bottom of the list, in the form of repeated calls to a stored procedure that was taking a whopping 400ms to execute. Whilst 400ms is not a long time – I’m sure it’s taken longer than that for you to read this sentence so far – when you call it 20 times you’ve suddenly spent 8 seconds just on database calls before any other page activity.</p>
<p>Digging into this identified three separate, but related issues.</p>
<p><b>1. One of our NHibernate mappings included a view based on a recursive query</b></p>
<p>Of course, this was something we’d told NHibernate to do. Our entities are organised hierarchically, and an entity can appear in multiple positions in the hierarchy. When we pass entity data to Solr for indexing, we work out the full list of paths for an entity and pass that through (see item 3 for more details.) This was done by creating a CTE to generate the list of hierarchy paths for each entity. </p>
<p>For the uninitiated, a <a href="http://msdn.microsoft.com/en-us/library/ms190766.aspx" target="_blank">Common Table Expression</a> (or CTE) is a T-SQL construct that (amongst other things) <a href="http://msdn.microsoft.com/en-us/library/ms186243.aspx" target="_blank">enables you to execute recursive queries</a>. They are very handy when dealing with hierarchical datasets, but aren’t the most efficient queries you can execute. Some time after the initial creation of the view, we’d realised that it would be useful to have the data it contained as part of our entity so we added a mapping into our NHibernate configuration. This meant that accessing that data would cause NHibernate to go off and execute a SELECT statement which included a join from the main entity table to the view. This query took in the region of 400ms. </p>
<p>We have two main types of page on the site landing pages and search/browse pages. The landing pages were causing this query to be executed 13 times and the browse pages were causing it to be executed 20 times, so it’s no wonder that performance had dropped. Whilst the view was never intended for use in this way, the requirement to have the data as part of our entity was still valid. </p>
<p>The simple solution to the problem was essentially to materialize the view. SQL Server can do this by <a href="http://msdn.microsoft.com/en-us/library/dd171921.aspx" target="_blank">turning the view into an indexed view</a> – adding a unique clustered index to it does this. However, this approach isn’t applicable when the SELECT statement for the view uses a CTE, so we went with a more basic approach – since our product catalogue is actually managed in the back office and populated via ETL, we replaced the view with a table (complete with all the appropriate indexes) and tweaked the ETL to populate this table at the same time as all the rest.</p>
<p>For the pages in question, load time dropped from around 8 seconds to under 2, at which point we all got quite excited. However, this wasn’t solely to do with the materialisation of the view, as the investigation also showed up that…</p>
<p><b>2. Everything was being lazy loaded </b></p>
<p>By default, NHibernate uses lazy loading across the board. Depending on your object model and how it is used, this can lead to massive inefficiences. The classic example is the &quot;SELECT N+1” anti-pattern, in which you retrieve an entity from a repository then iterate over a collection on that entity. If you’ve left NHibernate lazy loading the values, then this results in a SELECT statement being issued for every iteration of the loop. Have a look at <a href="http://nhprof.com/Learn/Alert?name=SelectNPlusOne" target="_blank">this page on the NHibernate Profiler site</a> for more information.</p>
<p><a href="http://blogs.conchango.com/howardvanrooijen/default.aspx" target="_blank">Howard</a> spent some time using the <a href="http://nhprof.com/" target="_blank">NHibernate profiler</a> to better understand what our app was doing, and made a number of tweaks to our mapping configuration to apply eager loading where it made sense. This provided another significant improvement, dramatically reducing the number of database calls made by the application.</p>
<p><b>3. An architectural rule imposed for performance reasons had been broken</b></p>
<p>We’d made an architectural decision to drive the search and browse functions on our website exclusively from Solr. We did this because it gives us more options for scalability. Since all the data in question comes from a single database, pulling the data directly out of that database would mean that as website load increases then so does the load on the database. The problem with that is that it’s difficult to scale a single database out &#8211; you can throw CPUs and RAM at your database server, but you’re always going to hit a limit, at which point you face some architectural challenges. This kind of basic architecture is shown in the following diagram:</p>
<p><a href="http://jonathangeorge.files.wordpress.com/2009/11/simplearchitecture.png"><img style="display:inline;border-width:0;" title="Simple architecture" border="0" alt="Simple architecture" src="http://jonathangeorge.files.wordpress.com/2009/11/simplearchitecture_thumb.png?w=432&#038;h=432" width="432" height="432" /></a> </p>
<p>Even though the main entity database is static, meaning that it would be possible to have a number of replicas of this database (potentially even one per webhead), this would require architectural change and would bring with it a new set of issues around data consistency. By pushing that load onto Solr, which has a far more straightforward scale-out story, we can grow far further without requiring a redesign. Solr basically stores a flat set of data in a form optimised for searching, and provides access via a web service. This means it is straightforward to have multiple instances of Solr running behind a load balancer. Solr makes this kind of setup even easier, as it supports a master-slave configuration as shown in the following diagram (I apologise now for the proliferation of connecting lines – I’m more of a <a href="http://simonmunro.com/2009/09/08/it-architecture-the-usual-suspects/" target="_blank">reluctant architect than a Powerpoint architect</a>):</p>
<p><a href="http://jonathangeorge.files.wordpress.com/2009/11/withsolrtier.png"><img style="display:inline;border-width:0;" title="With solr tier" border="0" alt="With solr tier" src="http://jonathangeorge.files.wordpress.com/2009/11/withsolrtier_thumb.png?w=431&#038;h=486" width="431" height="486" /></a> </p>
<p>In this example, the web tier will still talk direct to the data tier for some tasks – it’s unavoidable. However, we introduce the search tier which consists of a set of load balanced search servers, each of which contains an identical copy of the search index. In order to build that index, we push data from the database into the Solr master server, and the Solr master server indexes it and pushes the result out to each slave. If you can see past the nasty mess of lines, it should be obvious that as load grows, adding more webheads and/or Solr slaves is a relatively trivial operation. </p>
<p>However, you can have the best intentions in the world when you design your solution, but if you then go on to break the rules then it’s not going to happen. In our case, the code had ended up in a state where for each result retrieved from a Solr search, a database query would be made. Not only that, but the query in question was the horribly expensive one I mentioned in point 1.</p>
<p>This will no doubt cause some brow-wrinkling activity if you’re paying attention, as I mentioned that the original intended purpose of the view being used for the expensive query was to push data into Solr – so why, if the data was already in there, would we be accessing it again from the database once we’d pulled it out of Solr?</p>
<p>I can think of a number of good explanations, the best of which might be “My name’s Jon, and <a href="http://subwindow.com/articles/2" target="_blank">I’m a terrible programmer</a>”. The day I get it right 100% of the time is quite probably the day I get to decide which of my Ferrari’s I drive to work, as well as the day that my projects no longer require testers, and I doubt that will ever happen. Maybe I just missed the <a href="http://xkcd.com/323/" target="_blank">Ballmer Peak</a> on that day, but whatever happened, I’m just happy when the mistakes I make are as easy to identify and fix as this one was.</p>
<h2>Using the SQL Server Database Engine Tuning Advisor</h2>
<p>In tandem with this, we also made use of the SQL Server Database Engine Tuning Advisor. This is the next stage of evolution for the Index Tuning Wizard that (I believe) first appeared in SQL Server 2000. The <a href="http://msdn.microsoft.com/en-us/library/ms173494.aspx" target="_blank">MSDN page for it is here</a>; the short (aka .NET developer) version is that you put a trace file into one end, and out of the other comes a variety of changes you can make to your database to make it go faster.</p>
<p>In order to generate the input, you use SQL Server Profiler with the “Tuning” template. Once this is running, and saving the trace to a file, you need to generate a “typical” load against your application. There are various ways to do this, ranging from fully manual to fully automated. We were fortunate on our project that we had a complete set of <a href="http://seleniumhq.org/" target="_blank">Selenium</a> tests for the site and a way of running them as part of the build. I’m hoping that Tom, the consultant responsible for this, will start blogging about it at some point as it’s really interesting stuff. It meant that to generate our trace, all we had to do was start the profiler, kick of the test suite and go for a beer. The next morning, we pushed the resultant file through the tuning advisor and received our recommendations out of the other end. There was little we disagreed with, and most of the recommendations were subsequently applied to the database. </p>
<h2>The rest?</h2>
<p>You may have noticed that the main focus in this post has been around the way the application accessed the data, rather than the way the database was built. Over the duration of the project, and in addition to the fantastic data architect we had in the team, we’ve had guest appearances by <a href="http://consultingblogs.emc.com/jamesrowlandjones/" target="_blank">JRJ</a>, <a href="http://sqlblog.com/blogs/jamie_thomson/" target="_blank">Jamie</a> and <a href="http://consultingblogs.emc.com/simonmunro/" target="_blank">Simon</a>, so it shouldn’t come as a massive surprise that the database works well. EMC Consulting is rightly proud of the database guys we have on the team and whilst I’m sure that there are many further tweaks that could be made to our databases to add further go-faster stripes, they aren’t needed at the moment.&#160; Optimisation is one of many things that doesn’t benefit by having the word “premature” put in front of it – it’s basically another form of&#160; <a href="http://en.wikipedia.org/wiki/You_ain't_gonna_need_it" target="_blank">YAGNI</a>. So, until testing proved otherwise, we were happy to stop at this point and move on to something else.</p>
<p><i>In the next exciting epsiode of “Optimising an ASP.NET MVC web site”, we look at a pattern for applying application layer caching. Don’t touch that dial!</i></p>
<p>Please let me know what you think of my blog posts, either by commenting, dropping me an email, or via Twitter.</p>
<p><a href="http://twitter.com/jon_george1" target="_blank">@jon_george1</a></p>
<br /> Tagged: asp.net, asp.net mvc, nhibernate, performance, s#arp architecture, solr <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathangeorge.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathangeorge.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathangeorge.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathangeorge.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/jonathangeorge.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/jonathangeorge.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/jonathangeorge.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/jonathangeorge.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathangeorge.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathangeorge.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathangeorge.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathangeorge.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathangeorge.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathangeorge.wordpress.com/22/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=22&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-2-database-and-nhibernate/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6038fabf4927137a504608f99b48f91f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Jonathan</media:title>
		</media:content>

		<media:content url="http://jonathangeorge.files.wordpress.com/2009/11/sampletsql_durationtrace_thumb.png" medium="image">
			<media:title type="html">Sample TSQL_Duration Trace</media:title>
		</media:content>

		<media:content url="http://jonathangeorge.files.wordpress.com/2009/11/simplearchitecture_thumb.png" medium="image">
			<media:title type="html">Simple architecture</media:title>
		</media:content>

		<media:content url="http://jonathangeorge.files.wordpress.com/2009/11/withsolrtier_thumb.png" medium="image">
			<media:title type="html">With solr tier</media:title>
		</media:content>
	</item>
		<item>
		<title>Optimising an ASP.NET MVC web site part 1 &#8211; Introduction</title>
		<link>http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-1-introduction/</link>
		<comments>http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-1-introduction/#comments</comments>
		<pubDate>Sat, 03 Oct 2009 23:25:00 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Technical Stuff]]></category>
		<category><![CDATA[asp.net]]></category>
		<category><![CDATA[asp.net mvc]]></category>
		<category><![CDATA[n2cms]]></category>
		<category><![CDATA[nhibernate]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[s#arp architecture]]></category>
		<category><![CDATA[solr]]></category>
		<category><![CDATA[spark]]></category>

		<guid isPermaLink="false">http://jonathangeorge.wordpress.com/2009/10/03/optimising-an-asp-net-mvc-web-site-part-1-introduction/</guid>
		<description><![CDATA[Note: This was originally posted on my old blog at the EMC Consulting Blogs site. One of the things I’ve been involved in over the past couple of months is performance tuning work for my current project (now live at www.fancydressoutfitters.co.uk). One of my EMC Consulting colleagues, Marcin Kaluza, has recently started posting on this [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=15&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>Note: This was originally posted on </strong><a href="http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/09/how-to-improve-your-yslow-score-under-iis7.aspx"><strong>my old blog</strong></a><strong> at the </strong><a href="http://consultingblogs.emc.com/"><strong>EMC Consulting Blogs site</strong></a><strong>.</strong></p>
<p>One of the things I’ve been involved in over the past couple of months is performance tuning work for my current project (now live at <a href="http://www.fancydressoutfitters.co.uk">www.fancydressoutfitters.co.uk</a>). One of my EMC Consulting colleagues, <a href="http://blogs.conchango.com/marcinkaluza/default.aspx" target="_blank">Marcin Kaluza</a>, has recently started posting on this subject and I’ve been encouraged by <a href="http://blogs.conchango.com/howardvanrooijen/" target="_blank">Howard</a> to post some “war stories” of the kind of things I’ve encountered whilst doing this on projects, starting with the most recent.</p>
<p>So first, some background. It’s an public facing website project, and is based on <a href="http://devlicio.us/blogs/billy_mccafferty/" target="_blank">Billy McCafferty’s</a> excellent <a href="http://code.google.com/p/sharp-architecture/" target="_blank">S#arp Architecture</a> &#8211; which means it’s ASP.NET MVC with <a href="https://www.hibernate.org/343.html" target="_blank">NHibernate</a> talking to SQL Server 2008 databases. We’re using the <a href="http://sparkviewengine.com/" target="_blank">Spark View Engine</a> instead of the out of the box one, and the site uses <a href="http://n2cms.com/" target="_blank">N2 CMS</a> to provide content management capabilities (<a href="http://blogs.conchango.com/jamesbroome/default.aspx" target="_blank">James</a> posted a while back on <a href="http://blogs.conchango.com/jamesbroome/archive/2009/04/24/why-i-like-n2-cms.aspx" target="_blank">the reasons for choosing N2</a>.) Finally, we use <a href="http://lucene.apache.org/solr/" target="_blank">Solr</a> to provide our search functionality, integrated using <a href="http://code.google.com/p/solrnet/" target="_blank">Solrnet</a>. I joined the team a few months into the project, by which point they had laid a firm foundation and were about to be abandoned for 6 weeks by their technical lead who had inconsiderately booked his wedding and an extended honeymoon right in the middle of the project.</p>
<p>When the project was set up it was done so in strictly in accordance with agile principles. A small team was given a fixed date for go-live and the directive to spend the client’s money as if it were their own. One of the first things that happened was the adoption of a number of principles from the excellent <a href="http://37signals.com/" target="_blank">37signals</a> e-book <a href="http://gettingreal.37signals.com/toc.php" target="_blank">“Getting&#160; Real”.</a> A product backlog was assembled, and then – in accordance with the “build less” maxim – divided into “core” and “non-core” user stories. The core stories were what was essential for go live – things the client couldn’t live without, such as basic search and content management. The non-core stories are things that might enhance the site but aren’t essential – for example, advanced search features such as <a href="http://en.wikipedia.org/wiki/Faceted_search" target="_blank">faceted navigation</a>.</p>
<p>The absolute focus the team maintained on the core functionality and target delivery date has made this one of the best and most successful agile projects I’ve worked on – we reached our go live date on budget and were able to substantially over deliver on functionality. Whilst the site is relatively basic compared to some I’ve worked on, it stands out amongst its peers and provides a great platform for new functionality to be built on.</p>
<p>However, now I’ve extolled the virtues of the approach that was taken, I should talk about the performance optimisation and testing work we did. Since I have some experience from previous projects, I took on the task of testing the site to make sure it could handle an acceptable level of load without falling over in an embarrassing heap. However, before I started on that, we did some optimisation work on the site. </p>
<p>The aim was to hit the major pain points, since we knew performance had degraded over the previous few sprints. Once this was done, we could run some load testing and perform further tuning and optimisation work as required. I was originally intending to write a single post covering the optimisation process, then follow that up with one about the load testing process. However, that resulted in a rather lengthy post, so I’m splitting it up into several parts that I will post over the next week or two:</p>
<ul>
<li>Part 1: Introduction (this post) </li>
<li><a href="http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-2-database-and-nhibernate/">Part 2: Database and NHibernate-based repositories</a> </li>
<li><a href="http://jonathangeorge.co.uk/2009/10/12/optimising-an-asp-net-mvc-web-site-part-3-application-caching/">Part 3: Application caching</a> </li>
<li><a href="http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-4-output-caching-in-the-brave-new-world-of-mvc/">Part 4: View optimisation and output caching</a> </li>
<li><a href="http://jonathangeorge.co.uk/2009/11/03/optimising-an-asp-net-mvc-web-site-part-5-putting-your-money-where-your-mouth-is/">Part 5: Putting your money where your mouth is</a>&#160; </li>
</ul>
<p>In addition, I’ve already covered the work we did to correctly configure IIS in my post <a href="http://jonathangeorge.co.uk/2009/09/09/how-to-improve-your-yslow-score-under-iis7/">How to improve your YSlow score under IIS7</a>.</p>
<p>I hope you find these posts interesting – please let me know what you think by leaving a comment.</p>
<p><a href="http://twitter.com/jon_george1" target="_blank">@jon_george1</a></p>
<br /> Tagged: asp.net, asp.net mvc, n2cms, nhibernate, performance, s#arp architecture, solr, spark <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathangeorge.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathangeorge.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathangeorge.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathangeorge.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/jonathangeorge.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/jonathangeorge.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/jonathangeorge.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/jonathangeorge.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathangeorge.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathangeorge.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathangeorge.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathangeorge.wordpress.com/15/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathangeorge.wordpress.com/15/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathangeorge.wordpress.com/15/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=15&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://jonathangeorge.co.uk/2009/10/03/optimising-an-asp-net-mvc-web-site-part-1-introduction/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6038fabf4927137a504608f99b48f91f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Jonathan</media:title>
		</media:content>
	</item>
		<item>
		<title>How to improve your YSlow score under IIS7</title>
		<link>http://jonathangeorge.co.uk/2009/09/09/how-to-improve-your-yslow-score-under-iis7/</link>
		<comments>http://jonathangeorge.co.uk/2009/09/09/how-to-improve-your-yslow-score-under-iis7/#comments</comments>
		<pubDate>Wed, 09 Sep 2009 00:00:13 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Technical Stuff]]></category>
		<category><![CDATA[iis7]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[yslow]]></category>

		<guid isPermaLink="false">http://jonathangeorge.wordpress.com/2009/11/19/how-to-improve-your-yslow-score-under-iis7/</guid>
		<description><![CDATA[Note: This was originally posted on my old blog at the EMC Consulting Blogs site. On my last two projects, I’ve been involved in various bits of performance tweaking and testing. One of the common tools for checking that your web pages are optimised for delivery to the user is YSlow, which rates your pages [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=6&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><strong>Note: This was originally posted on <a href="http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/09/how-to-improve-your-yslow-score-under-iis7.aspx" target="_blank">my old blog</a> at the <a href="http://consultingblogs.emc.com/" target="_blank">EMC Consulting Blogs site</a>.</strong></p>
<p>On my last two projects, I’ve been involved in various bits of performance tweaking and testing. One of the common tools for checking that your web pages are optimised for delivery to the user is YSlow, which rates your pages against the <a href="http://developer.yahoo.com/performance/rules.html" target="_blank">Yahoo! best practices guide</a>.</p>
<p>Since I’ve now done this twice, I thought it would be useful to document what I know about the subject. The examples I’ve given are from my current project, which is built using ASP.NET MVC, the <a href="http://sparkviewengine.com/" target="_blank">Spark View Engine</a> and hosted on IIS7.</p>
<h3>Make Fewer Http Requests</h3>
<p>There are two sides to this. Firstly and most obviously, the lighter your pages the faster the user can download them. This is something that has to be addressed at an early stage in the design process – you need to ensure you balance up the need for a compelling and rich user experience against the requirements for fast download and page render times. Having well defined non-functional&#160; requirements up front, and ensuring that page designs are technically reviewed with those SLAs in mind is an important part of this. As I have learned from painful experience, having a conversation about slow page download times after you’ve implemented the fantastic designs that the client loved is not nice.</p>
<p>The second part of this is that all but the sparsest of pages can still be optimised by reducing the number of individual files downloaded. You can do this by combining your CSS, Javascript and image files into single files. CSS and JS combining and <a href="http://en.wikipedia.org/wiki/Minification_%28programming%29" target="_blank">minification</a> is pretty straightforward these days, especially with things like the <a href="http://yuicompressor.codeplex.com/" target="_blank">Yahoo! UI Library: YUI Compressor for .NET</a>. On my current project, we use the MSBuild task from that library to combine, minify and obfuscate JS and CSS as part of our release builds. To control which files are referenced in our views, we add the list of files to our base ViewModel class (from which all the other page ViewModels inherit) using conditional compilation statements.</p>
<div style="display:block;float:none;margin-left:auto;margin-right:auto;padding:5px;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:16e69d12-73a0-4ae6-866c-5b0f3412ca90" class="wlWriterEditableSmartContent">
<div style="border:#000080 1px solid;font-family:'Courier New', Courier, Monospace;font-size:10pt;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;white-space:wrap;margin:0 0 0 35px;">
<li> <span style="color:#0000ff;">private</span> <span style="color:#0000ff;">static</span> <span style="color:#0000ff;">void</span> ConfigureJavaScriptIncludes(<span style="color:#2b91af;">PageViewModel</span> viewModel)</li>
<li style="background:#f3f3f3;"> {</li>
<li> <span style="color:#0000ff;">#if</span> DEBUG</li>
<li style="background:#f3f3f3;">     viewModel.JavaScriptFiles.Add(<span style="color:#a31515;">&#8220;Libraries/jquery-1.3.2.min.js&#8221;</span>);</li>
<li>     viewModel.JavaScriptFiles.Add(<span style="color:#a31515;">&#8220;Libraries/jquery.query.js&#8221;</span>);</li>
<li style="background:#f3f3f3;">     viewModel.JavaScriptFiles.Add(<span style="color:#a31515;">&#8220;Libraries/jquery.validate.js&#8221;</span>);</li>
<li>     viewModel.JavaScriptFiles.Add(<span style="color:#a31515;">&#8220;Libraries/xVal.jquery.validate.js&#8221;</span>);</li>
<li style="background:#f3f3f3;">     viewModel.JavaScriptFiles.Add(<span style="color:#a31515;">&#8220;resources.js&#8221;</span>);</li>
<li>     viewModel.JavaScriptFiles.Add(<span style="color:#a31515;">&#8220;infoFlyout.js&#8221;</span>);</li>
<li style="background:#f3f3f3;">     viewModel.JavaScriptFiles.Add(<span style="color:#a31515;">&#8220;bespoke.js&#8221;</span>);</li>
<li> <span style="color:#0000ff;">#else</li>
<li style="background:#f3f3f3;"> </span><span style="color:#808080;">    viewModel.JavaScriptFiles.Add(&#8220;js-min.js&#8221;);</li>
<li> </span><span style="color:#0000ff;">#endif</span></li>
<li style="background:#f3f3f3;"> }</li>
<li> </li>
<li style="background:#f3f3f3;"> <span style="color:#0000ff;">private</span> <span style="color:#0000ff;">static</span> <span style="color:#0000ff;">void</span> ConfigureStyleSheetIncludes(<span style="color:#2b91af;">PageViewModel</span> viewModel)</li>
<li> {</li>
<li style="background:#f3f3f3;"> <span style="color:#0000ff;">#if</span> DEBUG</li>
<li>     viewModel.CssFile = <span style="color:#a31515;">&#8220;styles.css&#8221;</span>;</li>
<li style="background:#f3f3f3;"> <span style="color:#0000ff;">#else</li>
<li> </span><span style="color:#808080;">    viewModel.CssFile = &#8220;css-min.css&#8221;;</li>
<li style="background:#f3f3f3;"> </span><span style="color:#0000ff;">#endif</span></li>
<li> }</li>
<li style="background:#f3f3f3;"> </li>
</ol>
</div>
</div>
</div>
<p>Images are more tricky, and we’ve made the decision to not bother using <a href="http://css-tricks.com/css-sprites/" target="_blank">CSS Sprites</a> since most of the ways to do it are manual. If this is important to you, see the end of this post for a tool that can do it on the fly.</p>
<h3>Use a Content Delivery Network</h3>
<p>This isn’t something you can’t really address through configuration. In case you’re not aware, the idea is to farm off the hosting of static files onto a network of geographically distributed servers, and ensure that each user receives that content from the closest server to them. There are a number of CDN providers around – Akamai, Amazon Cloud Front and Highwinds, to name but a few.</p>
<p>The biggest problem I’ve encountered with the use of CDNs is for sites which have secure areas. If you want to avoid annoying browser popups telling you that your secure page contains insecure content, you need to ensure that your CDN has both a http and https URLs, and that you have a good way of switching between them as needed. This is a real pain for things like background images in CSS and I haven’t found a good solution for it yet.</p>
<p>On the plus side I was pleased to see that Spark provides support for CDNs in the form of it’s &lt;resources&gt; section, which allows you to define a path to match against an a full URL to substitute – so for example, you could tell it to map all files referenced in the path “~/content/css/” to “http://yourcdnprovider.com/youraccount/allstyles/css/”. Pretty cool – if <a href="http://whereslou.com/" target="_blank">Louis</a> could extend that to address the secure/insecure path issue, it would be perfect.</p>
<h3>Add an Expires or Cache-Control Header</h3>
<p>The best practice recommendation is to set far future expiry headers on all static content (JS/CSS/images/etc). This means that once the files are pushed to production you can never change them, because users may have already downloaded and cached an old version. Instead, you change the content by creating new files and referencing those instead (<a href="http://developer.yahoo.com/performance/rules.html#expires" target="_blank">read more here</a>.) This is fine for most of our resources, but sometimes you will have imagery which has to follow a set naming convention – this is the case for my current project, where the product imagery is named in a specific way. In this scenario, you just have to make sure that the different types of images are located in separate paths so you can configure them independently, and then choose an appropriate expiration policy based on how often you think the files will change.</p>
<p>To set the headers, all you need is a web.config in the relevant folder.&#160; You can either create this manually, or using the IIS Manager, which will create the file for you. To do it using IIS Manager, select the folder you need to set the headers for, choose the “HTTP Response Headers” option and then click the “Set Common Headers” option in the right hand menu. I personally favour including the web.config in my project and in source control as then it gets deployed with the rest of the project (a great improvement over IIS6).</p>
<p>Here’s the one we use for the Content folder.</p>
<div style="display:block;float:none;margin-left:auto;margin-right:auto;padding:5px;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:ae1f72a4-baff-49f9-b8ae-96ab006d702c" class="wlWriterEditableSmartContent">
<div style="border:#000080 1px solid;font-family:'Courier New', Courier, Monospace;font-size:10pt;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;white-space:wrap;margin:0 0 0 35px;">
<li> <span style="color:#0000ff;">&lt;?</span><span style="color:#a31515;">xml</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">version</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">1.0</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">encoding</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">UTF-8</span>&#8220;<span style="color:#0000ff;">?&gt;</li>
<li style="background:#f3f3f3;"> &lt;</span><span style="color:#a31515;">configuration</span><span style="color:#0000ff;">&gt;</li>
<li>   &lt;</span><span style="color:#a31515;">system.webServer</span><span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;">     &lt;</span><span style="color:#a31515;">staticContent</span><span style="color:#0000ff;">&gt;</li>
<li>       &lt;</span><span style="color:#a31515;">clientCache</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">cacheControlMode</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">UseExpires</span>&#8220;<span style="color:#0000ff;"> </li>
<li style="background:#f3f3f3;">                    </span><span style="color:#ff0000;">httpExpires</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">Sat, 31 Dec 2050 00:00:00 GMT</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li>     &lt;/</span><span style="color:#a31515;">staticContent</span><span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;">   &lt;/</span><span style="color:#a31515;">system.webServer</span><span style="color:#0000ff;">&gt;</li>
<li> &lt;/</span><span style="color:#a31515;">configuration</span><span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;"> </span></li>
</ol>
</div>
</div>
</div>
<p>This has the effect of setting the HTTP Expires header to the date specified.</p>
<p>And here’s what we use for the product images folder.</p>
<div style="display:block;float:none;margin-left:auto;margin-right:auto;padding:5px;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:e4d96b59-c420-4fd4-90a3-f07a7cea6e7a" class="wlWriterEditableSmartContent">
<div style="border:#000080 1px solid;font-family:'Courier New', Courier, Monospace;font-size:10pt;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;white-space:wrap;margin:0 0 0 35px;">
<li> <span style="color:#0000ff;">&lt;?</span><span style="color:#a31515;">xml</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">version</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">1.0</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">encoding</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">UTF-8</span>&#8220;<span style="color:#0000ff;">?&gt;</li>
<li style="background:#f3f3f3;"> &lt;</span><span style="color:#a31515;">configuration</span><span style="color:#0000ff;">&gt;</li>
<li>   &lt;</span><span style="color:#a31515;">system.webServer</span><span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;">     &lt;</span><span style="color:#a31515;">staticContent</span><span style="color:#0000ff;">&gt;</li>
<li>       &lt;</span><span style="color:#a31515;">clientCache</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">cacheControlMode</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">UseMaxAge</span>&#8220;<span style="color:#0000ff;"> </li>
<li style="background:#f3f3f3;">                    </span><span style="color:#ff0000;">cacheControlMaxAge</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">7.00:00:00</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li>     &lt;/</span><span style="color:#a31515;">staticContent</span><span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;">   &lt;/</span><span style="color:#a31515;">system.webServer</span><span style="color:#0000ff;">&gt;</li>
<li> &lt;/</span><span style="color:#a31515;">configuration</span><span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;"> </span></li>
</ol>
</div>
</div>
</div>
<p>This writes max-age=604800 (7 days worth of seconds) into the Cache-Control header in the response. The browser will use this in conjunction with the Date header to determine whether the cached file is still valid.</p>
<p>Our HTML pages all have max-age=0 in the cache-control header, and have the expires header set to the same as the Date and Last-Modified headers, as we don’t want these cached – it’s an ecommerce website and we have user-specific infomation (e.g. basket summary, recently viewed items) on each page.</p>
<h3>Compress components with Gzip</h3>
<p>IIS7 does this for you out of the box but it’s not always 100% clear how to configure it, so, here’s the details.</p>
<p>Static compression is for files that are identical every time they are requested, e.g. Javascript and CSS. Dynamic compression is for files that differ per request, like the HTML pages generated by our app. The content types that actually fall under each heading are controlled in the IIS7 metabase, which resides in the file C:\Windows\System32\inetsrv\config\applicationHost.config. In that file, assuming you have both static and dynamic compression features enabled, you’ll be able to find the following section:</p>
<div style="display:block;float:none;margin-left:auto;margin-right:auto;padding:5px;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:b775a157-c16b-4d9c-bf58-5298526f752a" class="wlWriterEditableSmartContent">
<div style="border:#000080 1px solid;font-family:'Courier New', Courier, Monospace;font-size:10pt;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;white-space:wrap;margin:0 0 0 35px;">
<li> <span style="color:#0000ff;">&lt;</span><span style="color:#a31515;">httpCompression</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">directory</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">%SystemDrive%&#92;inetpub&#92;temp&#92;IIS Temporary Compressed Files</span>&#8220;<span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;">   &lt;</span><span style="color:#a31515;">scheme</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">name</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">gzip</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">dll</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">%Windir%&#92;system32&#92;inetsrv&#92;gzip.dll</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li>   &lt;</span><span style="color:#a31515;">dynamicTypes</span><span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;">     &lt;</span><span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">mimeType</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">text/*</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li>     &lt;</span><span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">mimeType</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">message/*</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li style="background:#f3f3f3;">     &lt;</span><span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">mimeType</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">application/x-javascript</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li>     &lt;</span><span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">mimeType</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">*/*</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">false</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li style="background:#f3f3f3;">   &lt;/</span><span style="color:#a31515;">dynamicTypes</span><span style="color:#0000ff;">&gt;</li>
<li>   &lt;</span><span style="color:#a31515;">staticTypes</span><span style="color:#0000ff;">&gt;</li>
<li style="background:#f3f3f3;">     &lt;</span><span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">mimeType</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">text/*</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li>     &lt;</span><span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">mimeType</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">message/*</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li style="background:#f3f3f3;">     &lt;</span><span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">mimeType</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">application/javascript</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li>     &lt;</span><span style="color:#a31515;">add</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">mimeType</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">*/*</span>&#8220;<span style="color:#0000ff;"> </span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">false</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li style="background:#f3f3f3;">   &lt;/</span><span style="color:#a31515;">staticTypes</span><span style="color:#0000ff;">&gt;</li>
<li> &lt;/</span><span style="color:#a31515;">httpCompression</span><span style="color:#0000ff;">&gt;</span></li>
</ol>
</div>
</div>
</div>
<p>As you can see, configuration is done by response MIME type, making it pretty straightforward to set up…</p>
<table border="0" cellspacing="0" cellpadding="2" width="400">
<tbody>
<tr>
<td valign="top" width="200"><strong>Item</strong></td>
<td valign="top" width="200"><strong>MIME type</strong></td>
</tr>
<tr>
<td valign="top" width="200">Views (i.e. the HTML pages)</td>
<td valign="top" width="200">text/html</td>
</tr>
<tr>
<td valign="top" width="200">CSS</td>
<td valign="top" width="200">text/css</td>
</tr>
<tr>
<td valign="top" width="200">JS</td>
<td valign="top" width="200">application/x-javascript</td>
</tr>
<tr>
<td valign="top" width="200">Images</td>
<td valign="top" width="200">image/gif, image/jpeg, image/png</td>
</tr>
</tbody>
</table>
<p>One gotcha there is that IIS7 is configured out of the box to return a content type of application/x-javascript for .JS files, which results in them coming under the configuration for dynamic compression.This is not great – dynamic compression is intended for frequently changing content so dynamically compressed files are not cached for future requests. Our JS files don’t change outside of deployments, so we really need them to be in the static compression category.</p>
<p>There’s a fair bit of discussion online as to what the correct MIME type for Javascript should be, with the three choices being:</p>
<ul>
<li>text/javascript </li>
<li>application/x-javascript </li>
<li>application/javascript </li>
</ul>
<p>So you have two choices – you can change your config, either in web.config or applicationHost.config to map the .js extension to text/javascript or application/javascript, which are both already registered for static compression, or you can move application/x-javascript into the static configuration section in your web.config or applicationHost.config. I went with the latter option and modified applicationHost.config. I look forward to a comment from anyone who can explain whether that was the correct thing to do or not.</p>
<p>Finally, in the &lt;system.webserver&gt; element of your app’s main web.config, add this:</p>
<div style="display:block;float:none;margin-left:auto;margin-right:auto;padding:5px;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:c85c5401-fce1-4b5a-9534-c1d535a46c33" class="wlWriterEditableSmartContent">
<div style="border:#000080 1px solid;font-family:'Courier New', Courier, Monospace;font-size:10pt;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;white-space:wrap;margin:0 0 0 25px;">
<li> <span style="color:#0000ff;">&lt;</span><span style="color:#a31515;">urlCompression</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">doStaticCompression</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;"> </li>
<li style="background:#f3f3f3;">                 </span><span style="color:#ff0000;">doDynamicCompression</span><span style="color:#0000ff;">=</span>&#8220;<span style="color:#0000ff;">true</span>&#8220;<span style="color:#0000ff;"> /&gt;</li>
<li> </span></li>
</ol>
</div>
</div>
</div>
<p>Once you’ve got this configured, you’ll probably open up Firebug or Fiddler, hit your app, and wonder why your static files don’t have the Content-Encoding: gzip header. The reason is that IIS7 is being clever. If you return to applicationHost.config, one thing you <strong>won’t </strong>see is this:</p>
<div style="padding:5px;" id="scid:9ce6104f-a9aa-4a17-a79f-3a39532ebf7c:40f60212-4e58-4210-8c8e-138acf719588" class="wlWriterSmartContent">
<div style="font-family:&#39;">
<div style="background:#ddd;max-height:300px;overflow:scroll;padding:0;">
<ol style="background:#ffffff;margin:0 0 0 25px;">
<li><span style="color:#0000ff;">&lt;</span><span style="color:#a31515;">serverRuntime</span><span style="color:#0000ff;"> </span><span style="color:#ff0000;">alternateHostName</span><span style="color:#0000ff;">=</span>&quot;&quot; </li>
<li style="background:#f3f3f3;"><span style="color:#0000ff;"></span><span style="color:#ff0000;">appConcurrentRequestLimit</span><span style="color:#0000ff;">=</span>&quot;<span style="color:#0000ff;">5000</span>&quot; </li>
<li><span style="color:#0000ff;"></span><span style="color:#ff0000;">enabled</span><span style="color:#0000ff;">=</span>&quot;<span style="color:#0000ff;">true</span>&quot; </li>
<li style="background:#f3f3f3;"><span style="color:#0000ff;"></span><span style="color:#ff0000;">enableNagling</span><span style="color:#0000ff;">=</span>&quot;<span style="color:#0000ff;">false</span>&quot; </li>
<li><span style="color:#0000ff;"></span><span style="color:#ff0000;">frequentHitThreshold</span><span style="color:#0000ff;">=</span>&quot;<span style="color:#0000ff;">2</span>&quot; </li>
<li style="background:#f3f3f3;"><span style="color:#0000ff;"></span><span style="color:#ff0000;">frequentHitTimePeriod</span><span style="color:#0000ff;">=</span>&quot;<span style="color:#0000ff;">00:00:10</span>&quot; </li>
<li><span style="color:#0000ff;"></span><span style="color:#ff0000;">maxRequestEntityAllowed</span><span style="color:#0000ff;">=</span>&quot;<span style="color:#0000ff;">4294967295</span>&quot; </li>
<li style="background:#f3f3f3;"><span style="color:#0000ff;"></span><span style="color:#ff0000;">uploadReadAheadSize</span><span style="color:#0000ff;">=</span>&quot;<span style="color:#0000ff;">49152</span>&quot;<span style="color:#0000ff;">&gt; </span></li>
</ol></div>
</p></div>
</p></div>
<p>You’ll just have an empty &lt;serverRuntime /&gt; tag. The values I’ve shown above are the defaults, and the interesting ones are “frequentHitThreshold” and “frequentHitTimePeriod”. Essentially, a file is only a candidate for static compression if it’s a frequently hit file, and those two attributes control the definition of “frequently hit”. So first time round, you won’t see the Content-Encoding: gzip header, but if you’re quick with the refresh button, it should appear. If you spend some time Googling, you’ll find some discussion around setting frequentHitThreshold to 1 – some say it’s a bad idea, some say do it.&#160; Personally, I decided to trust the people who built IIS7 since in all likelihood they have larger brains than me, and move on.</p>
<p>There are a couple of other interesting configuration values in this area – the <a href="http://msdn.microsoft.com/en-us/library/ms690689.aspx" target="_blank">MSDN page covers them in detail</a>.</p>
<h3>Minify Javascript and CSS</h3>
<p>The MSBuild task I mentioned under “Make fewer HTTP requests” minifies the CSS and JS as well as combining the files.</p>
<h3>Configure Entity tags (etags)</h3>
<p>ETags are a bit of a pain, with a lot of confusion and discussion around them. If anyone has the full picture, I’d love to hear it but my current understanding is that when running IIS, the generated etags vary by server, and will also change if the app restarts. Since this effectively renders them useless, we’re removing them. Howard has already blogged <a href="http://blogs.conchango.com/howardvanrooijen/archive/2009/08/25/cloaking-your-asp-net-mvc-web-application-on-iis-7.aspx" target="_blank">this technique for removing unwanted response headers in IIS7</a>. Some people have had luck with <a href="http://serverfault.com/questions/55919/yslow-says-etags-are-misconfigured-how-to-configure-etags-properly-on-iis7" target="_blank">adding a new, blank response header called ETag</a> in their web.config file, but that didn’t work for me.</p>
<h3>Split Components Across Domains</h3>
<p>The HTTP1.1 protocol states that browsers should not make more than two simultaneous connections to the same domain. Whilst the most recent browser versions (IE8, Chrome and Firefox 3+) ignore this, older browsers will normally still be tied to that figure. By creating separate subdomains for your images, JS and CSS you can get these browsers to download more of your content in parallel. It’s worth noting that using a CDN also helps here. Doing this will also help with the “Use Cookie Free Domains For Components” rule, since any domain cookies you use won’t be sent for requests to the subdomains/CDN. However, you will probably fall foul of the issue I mentioned earlier around mixing secure and insecure content on a single page.</p>
<p>A great tool for visualising how your browser is downloading content is <a href="http://www.microsoft.com/Downloads/details.aspx?FamilyID=119f3477-dced-41e3-a0e7-d8b5cae893a3&amp;displaylang=en" target="_blank">Microsoft Visual Roundtrip Analyzer</a>. Fire it up and request a page, and you will be bombarded with all sorts of information (too much to cover here) about how well the site performs for that request.</p>
<h3>Optimise Images</h3>
<p>Sounds obvious, doesn’t it? There are some good tools out there to help with this, such as <a href="http://developer.yahoo.com/yslow/smushit/" target="_blank">Smush.it</a>, now part of <a href="https://addons.mozilla.org/en-US/firefox/addon/5369" target="_blank">YSlow</a>. If you have an image heavy website, you can make a massive difference to overall page weight by doing this.</p>
<h3>Make favicon.ico Small and Cacheable</h3>
<p>If you don’t have a favicon.ico, you’re missing a trick – not least because the browser is going to ask for it anyway, so you’re better returning&#160; one than returning a 404 response. The recommendation is that it’s kept under 1Kb and that it has a fairly long expiration period.</p>
<h2>WAX – Best practice by default</h2>
<p>On my last project, I used the <a href="http://www.aptimize.com/" target="_blank">Aptimize Website Accelerator</a> to handle the majority of the things I’ve mentioned above (my current project has a tight budget, so it’s not part of the solution for v1). It’s a fantastic tool and I’d strongly recommend anyone wishing to speed their site up gives it a try. I was interested to learn recently that Microsoft have started running it on <a href="http://sharepoint.microsoft.com/" target="_blank">sharepoint.microsoft.com</a>, and are seeing a 40-60% reduction in page load times. It handles combination and minification of CSS, Javascript and images, as well as a number of other cool tricks such as inlining images into CSS files where the requesting browser supports it, setting expiration dates appropriately, and so on.</p>
<p><strong>Update: </strong>Microsoft’s SharePoint team have <a href="http://blogs.msdn.com/sharepoint/archive/2009/09/28/how-we-did-it-speeding-up-sharepoint-microsoft-com.aspx" target="_blank">blogged about using WAX on sharepoint.microsoft.com</a>, with the detail being provided by <a href="http://www.aptimize.com/contact-us" target="_blank">Ed Robinson</a>, the CEO of <a href="http://www.aptimize.com/" target="_blank">Aptimize</a>.</p>
<p><a href="http://twitter.com/jon_george1" target="_blank">@jon_george1</a></p>
<br /> Tagged: iis7, performance, yslow <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/jonathangeorge.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/jonathangeorge.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/jonathangeorge.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/jonathangeorge.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/jonathangeorge.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/jonathangeorge.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/jonathangeorge.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/jonathangeorge.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/jonathangeorge.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/jonathangeorge.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/jonathangeorge.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/jonathangeorge.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/jonathangeorge.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/jonathangeorge.wordpress.com/6/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=jonathangeorge.co.uk&amp;blog=10577589&amp;post=6&amp;subd=jonathangeorge&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://jonathangeorge.co.uk/2009/09/09/how-to-improve-your-yslow-score-under-iis7/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6038fabf4927137a504608f99b48f91f?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">Jonathan</media:title>
		</media:content>
	</item>
	</channel>
</rss>
