I recently had a MonoRail project where client-side performance was becoming a problem. A Portlet (an extended View Component) in our master template was calling a web service multiple times during page generation, which often caused a delay in the browser download and therefore caused user complaints, which (naturally) led to our client complaining. For a number of reasons, there was no simple way to make the web service call asynchronous.
The “quick fix” solution was to implement as many client-side performance improvements as possible. I reviewed Yahoo’s “Best Practices for Speeding Up Your Website” and realized there was something I hadn’t tried. Yahoo suggests “flushing the response early”, recommending that you flush between the </head> and <body> tags. The example in PHP is great, if I were using PHP.
By default ASP.NET buffers the response until the whole page is generated (web service requests and all). This delay causes the browser to appear to freeze for a couple of seconds after the user clicks a link. Users would click a link and think it didn’t work, so they clicked it a second time, third time, etc. If I could flush the response after the </head> and after the header/main-menu HTML then the browser would show something happening faster and the users would stop being click-happy (or click-angry).
To disable response buffering for an individual page in ASP.NET you can add buffer=”false” to the page directive as shown here:
To disable response buffering for an entire web application you can add buffer=”false” to the <pages> setting under <system.web> as shown here:
<pages buffer="false" />
Since MonoRail is an httpHandler for ASP.NET, the same setting in web.config applies. It should also be possible to change the setting on a single MonoRail controller action by specifying the following inside the method code as shown here (on sample Index page):
public void Index()
Context.UnderlyingContext.Response.Buffer = false;
The next trick is to explicitly force the flush within the page inline with the HTML that should be broken up. In ASP.NET you could simply insert Response.Flush() into the page as shown here:
<% Response.Flush(); %>
In MonoRail the same idea applies but with slightly different syntax. The Response.Flush() method can still be accessed but requires a more complex path due to MonoRail’s namespace layout. With NVelocity, the code looks like the following:
After applying those changes to my project, response flushing appeared to work in MonoRail… until I rolled it out to our production environment. The final gotcha was that our HTTP Compression (GZip & Deflate) settings in IIS were interfering. I opened ZipEnable and told IIS to cease compressing dynamic content and suddenly response flushing worked. So apparently the HTTP compression in IIS buffers the entire page content before compressing and delivering to the client browser.
Lesson of the Day:
Even though I disabled HTTP compression for dynamic content and it technically took ~1 sec longer for total download on the client browser, users thought that the site was far faster. Actual speed is not as important as perceived speed. If the browser seems to immediately respond to a click, the user is more satisfied because something appears to be happening even if the page doesn’t completely fill-in right away.
Browser Performance Tip:
Response.Flush() can be used strategically multiple places within a page. I use it immediately after the </head> tag, also after the header/main-menu HTML and sometimes after large div containers like sidebar navigation.
I hope this post is helpful to you MonoRail fans out there. Happy coding!