Archive for May, 2008

May 27th 2008

Optimize UpdatePanel performance by avoiding unnecessary element disposal

I fought using update panels for a long time. Every time someone demonstrates the MS AJAX framework they skip the amazing features and go right to update panels. They’d say “Look how easy they are to use!”, “They integrate right into the code you have now.”, and my personal favorite, “This is how you can AJAX enabled your website”. I thought they were a heavy solution filled with problems like smartnav has in asp.net 1.1.
The MS AJAX framework is much more than just update panels. There’s more to writing AJAX enabled web sites than wrapping your dynamic content inside update panels. However, there’s something I failed to realize- for what they do, update panels are the best tool for the job. If the goal is to asynchronously update content in a page using the same code used to render the content in the first place, updates panels are it. Less obvious features include:

  • Only the data being refreshed is transmitted from the web server
  • UI code uses the same controls to render synchronously or asynchronously
  • The framework manages the loading of additional javascript and css references
  • The framework only renders update panels that have been triggered for refresh
  • There is a simple and effective way to specify which panels get updated on postback
  • Update panels work almost seamlessly with the other parts of the MS AJAX framework - components, controls, and behaviors

I have now accepted the fact that Microsoft has put more thought, time and money into making update panels better than any homegrown solution.

In the primer Asp.Net AJAX IN ACTION, the authors take great care to explain precisely how update panels work. They describe how the ajax framework creates and disposes components when an update panel’s contents are updated. For every element in the update panel being disposed, the ajax framework checks to see if there are any controls or behaviors bound to the element that needs to be disposed.

Note: any components not bound to elements are not disposed when update panels are refreshed. When the page_unload() event calls the dispose method of the components an error may occur because the DOM state has changed and the components don’t reflect it.

Why bother with object disposal? Why not just replace the innerHTML of the update panel’s div? Julien Lecompte (from Yahoo!) explains a few issues here. Basically, expando attributes on elements cause memory leaks when not disposed properly. Douglas Crockford (also from Yahoo!) describes the basic workaround. Fortunately for us, however, the Microsoft framework does this already in part through the component dispose method.

When the update panel is about to be loaded, the framework doesn’t know which objects need to be disposed. It has to iterate over all the elements about to be destroyed and check for objects to dispose. The following code is from MicrosoftAjaxWebForms.debug.js (comments in code are mine):

function Sys$WebForms$PageRequestManager$_updatePanel(updatePanelElement, rendering) {
	//some code not germane to this post has been omitted here
	//this is where the existing content is disposed
	this._destroyTree(updatePanelElement);
	//and the content is updated
	updatePanelElement.innerHTML = rendering;
}
function Sys$WebForms$PageRequestManager$_destroyTree(element) {
	if (element.nodeType === 1) {
		var childNodes = element.childNodes;
		for (var i = childNodes.length - 1; i >= 0; i--) {
			var node = childNodes[i];
			if (node.nodeType === 1) {
				//if the node has a dispose method, call it
				if (node.dispose && typeof(node.dispose) === “function”) {
					node.dispose();
				}
				//if the node has a sys.ui.control associated with it, call its dispose method
				else if (node.control && typeof(node.control.dispose) === “function”) {
					node.control.dispose();
				}
				//check for sys.ui.behaviors associated with the node and call dispose on each one
				var behaviors = Sys.UI.Behavior.getBehaviors(node);
				for (var j = behaviors.length - 1; j >= 0; j–) {
					behaviors[j].dispose();
				}
				//recurse the contents of this node to do it again
				this._destroyTree(node);
			}
		}
	}
}

If you know there are no objects bound to the content of the update panel, Asp.Net AJAX In Action suggests that you can avoid the performance penalty imposed by the framework’s memory leak prevention. They suggest removing the update panel div from the DOM after the asynchronous result is returned and before the framework applies the result to the DOM. We can use the pageLoading event to do this.

'''
''' Exposes methods to increase update panel performance when the panel contains content without sys.ui.controls associated with it
'''
'''
Public NotInheritable Class UpdatePanelRemoveNodeOnPostback         

	'''
	''' returns string for script to remove the specified control on the client DOM before the page is loaded after an asyncronous postback
	''' this saves the atlas framework from having to traverse the existing dom elements and remove all behaviors, thereby increasing performance
	''' NOTE: THIS CAN ONLY BE USED WHEN THE CONTROL HAS NO CLIENT SIDE CONTROLS ASSOCIATED WITH IT, specifically, as is the case with some of our
	''' gridviews
	'''
	'''

	'''
	'''
	Public Shared Function CreateScriptToRemoveExistingNodeFromUpdatePanelBeforePageLoad(ByVal control As Control) As String
		Return String.Format(System.Globalization.CultureInfo.InvariantCulture, Resources.UpdatePanelRemoveNodeOnPageLoading.Script, control.ClientID, control.ClientID, control.ClientID)
	End Function
End Class

The function references a script included as a resource:

<script type="text/javascript">
	var pageRequestManager = Sys.WebForms.PageRequestManager.getInstance();
	pageRequestManager.add_pageLoading(onPageLoading{0});        

	function onPageLoading{1}(sender, e) {
		var itemToRemove = $('{2}');
		if (itemToRemove != null) {
			itemToRemove.remove();
		}
	}
</script>

One common place I use this is when an update panel contains a gridview. This is an ideal candidate because the amount of html being disposed can be quite large. In the gridview’s container’s preRender event:

Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
	MyBase.OnPreRender(e)
	If GridView.Visible Then
		Dim script As String = UpdatePanelRemoveNodeOnPostback.CreateScriptToRemoveExistingNodeFromUpdatePanelBeforePageLoad(GridView)
		Me.Page.ClientScript.RegisterStartupScript(Me.GetType, "RemoveGridViewPrePostBackLoad", script)
	End If
End Sub

When the update panel is loaded, the javascript code will fire removing the gridview element from the DOM before the disposal code is fired.

2 Comments »

May 27th 2008

Time based Caching in a Load Balanced ASP.Net Application

When using the asp.net application cache a common problem occurs when the application scales from a single web server to a web farm. This problem and more are more broadly discussed here.

One common approach to keep cache items in sync across web servers is use of file dependencies on a central file server. When a depenency file is modified, all web servers in the farm remove the dependent items from their cache.

This works well until one tries to cache an item for a specified duration without a specific file dependency. Consider a web site that shows “recent blog posts”. The list may be constantly changing and although some latency of the data is allowed(hence the caching), the data must always be consistent no matter which web server serves the response.

We can use the same file dependency strategy to keep the cache items in sync between web servers. The “trick” is to produce a hash of the data to be cached and include that information when synchronizing the servers.

If a web server receives a request and does not have the data in its application cache, it must retrieve it.  The server then compares a hash of the data against the contents of a dependency file (unique to that cache item) accessible to all web servers. If the contents of the file are different than the hash generated, the latest results retrieved for the cache item do not match the cache items on other web servers. By replacing the contents of the cache item dependency file with the value of the new hash we can guarantee that the cache items on other web servers will be invalidated and removed.

A sample project demonstrating this is here.

 

No Comments yet »

May 27th 2008

Class Table Inheritance: An Investigation into SQL Performance

Class Table Inheritance is a powerful tool when persisting a strong object oriented business model, but what happens when the data size grows? If we know the type of object we’re selecting, the query is straightforward and performs well:

select
	 base_class.id
	,base_class.sub_type
	,inheriting_class.column1
..
	,inheriting_class.columnn
from
	base_class inner join inheriting_class on base_class.id = inheriting_class.base_class_id

Using multiple tables, requires an inner join to select data and multiple updates to persist it. Outside high volume transaction applications, however, this cost this is negligible.
The problem begins when we need to select a potentially heterogeneous set of records. The longer the inheritance chain and the larger number of sub-classes the less performant our queries will be.
Suppose we have a relatively simple object model as follows:

click the links for scripts to create and populate.

In order to select a set of records “inheriting” from base_class, the business layer or OR/M will generate a query similar to the following (note I’m not selecting all the records, just a subset akin to the filters one would use in a real application):

select
	 base_class.id
	,A.base_class_id
	,B.base_class_id
	,C.base_class_id
	,case when A.base_class_id is not null then 1 when B.base_class_id is not null then 2 when C.base_class_id is not null then 3 end as clazz_
from
	base_class left join inheriting_class_a as A on base_class.id = A.base_class_id
	left join inheriting_class_b as B on base_class.id = B.base_class_id
	left join inheriting_class_c as C on base_class.id = C.base_class_id
where base_class.id % 7 = 1

On average the query takes 700ms on my machine (dual quad-core x64 xp). If we add in our knowledge of the sub-type into the join, the performance does not improve.

select
	 base_class.id
	,A.base_class_id
	,B.base_class_id
	,C.base_class_id
	,case when A.base_class_id is not null then 1 when B.base_class_id is not null then 2 when C.base_class_id is not null then 3 end as clazz_
from
	base_class as base_class left join inheriting_class_a as A on base_class.id = A.base_class_id and base_class.sub_type = 2
	left join inheriting_class_b as B on base_class.id = B.base_class_id and base_class.sub_type = 0
	left join inheriting_class_c as C on base_class.id = C.base_class_id and base_class.sub_type = 1
where base_class.id % 7 = 1

In fact the query execution plan is still the same despite a unique composite index on id and sub_type. The left joins are simply expensive. We need them to return heterogeneous results but the cost has become prohibitive. The longer the inheritance chain and the larger the number of sub-types, the more drastically the performance will degrade beyond this simple example.
One workaround is to perform the expensive outer joins on a smaller subset of date. If we execute the same query using a Common Table Expression and filter the rows before doing the outer joins, the cost decreases by 50% to an average of 371ms.

select
	 base_class.id
	,base_class.sub_type
	,C.base_class_id
	,B.base_class_id
	,C.base_class_id
	,case when A.base_class_id is not null then 1 when B.base_class_id is not null then 2 when C.base_class_id is not null then 3 end as clazz_
from
	base_class as base_class left join inheriting_class_a as C on base_class.id = C.base_class_id
	left join inheriting_class_b as B on base_class.id = B.base_class_id
	left join inheriting_class_c as C on base_class.id = C.base_class_id
where base_class.id % 7 = 1

By comparison, using Single Table inheritance (click links for create and populate scripts), the performace on the following:

	select id, 1 as clazz_
	from sub_class_a
	where id %7 = 1
union all
	select id, 2 as clazz_
	from sub_class_b
	where id %7 = 1
union all
	select id, 3 as clazz_
	from sub_class_c
	where id %7 = 1

is far better than that of the Class Table Inheritance at an average of 140ms. It’s important to note that performance will vary dramatically depending on the size of your data, the “selectiveness” of your where clause and other specifc circumstances.
From the perspective of query performance, Single Table Inheritance is far more efficient than Class Table Inheritance. Within the context of Class Table Inheritance, what is not so obvious is that the cost of returning heterogenous lists or simply not knowing the sub-type at query time can be quite high. Filtering the records (if possible) before applying the outer joins can drastically improve performance.

No Comments yet »

May 27th 2008

Howto: avoid binding the same eventhandler multiple times to an element event in the MS AJAX framework

The Microsoft AJAX $addHandler method is a wrapper to the browser specific implementations of the w3c standard e.g. $addHandler(element, eventName, handler).

Once you’ve bound an event handler to an element, there is no standard way to retrieve the binding information. Javascript frameworks work around this limitation by setting custom properties on the html elements to track handler bindings. As long as handlers are added and removed through the framework things will work as expected with one minor exception. If $addHandler is called twice with the same handler, when the event fires, the handler fires twice.

The Microsoft AJAX framework doesn’t check whether or not a delegate is already bound before binding it a second time, nor does it provide a means to determine what handlers are already bound. The best way to avoid this condition is by following the MS AJAX pattern for binding behaviors and elements. The pattern dictates that elements are “wrapped” in a one-to-one relationship with controls. When a control is initialized it binds element events to its own internal delegates. When other behaviors need to listen for events, they use the event handling architecture for controls and behaviors by binding against the control, not the element. Mike Ormond’s blog provides a good explanation.

Occasionally, you have the problem of adding the same handler multiple times. To work around this we need to know a little more about how the Microsoft framework tracks element event bindings.

When $addHandler is called, Microsoft AJAX adds an expando attribute to the element called “_events”. This is a hash table of the event bindings for the element. Element._events["eventName"] returns an array of delegates bound to the specified element event. The code below simply uses this internal implementation of the MS AJAX framework to avoid duplicate bindings. To use it, simply replace calls to $addHandler and $removeHandler with calls to $addHandlerIfNotDefined and $removeHandlerIfNotDefined.

$handlerDefined = function(elt, eventName, handler) {
	// returns true if an eventHandler has been defined for the given element and the given event
	if ( ( typeof( elt._events ) !== 'object' ) ||
		 ( elt._events === null ) ) {
		return false;
	}
	var cache = elt._events[eventName];
	if ( !(cache instanceof Array ) ) {
		return false;
	} else {
		for (var i=0, l = cache.length; i < l; i+=1) {
				if (cache[i].handler === handler) {
					return true;
				}
		}
	}
}    

$addHandlerIfNotDefined = function(elt, eventName, handler) {
	// ensures the given handler is bound to the given element for the given event
	if ( !$handlerDefined( elt, eventName, handler ) ) {
		$addHandler(elt, eventName, handler);
	}
}    

$removeHandlerIfNotDefined = function(elt, eventName, handler) {
	// ensures the given handler is bound to the given element for the given event
	if ( $handlerDefined( elt, eventName, handler ) ) {
		$removeHandler(elt, eventName, handler);
	}
}

Note: this solution does not handle new instances of anonymous delegates e.g.
$addHandlerIfNotDefined(elt, “onclick”, new Function(evt){some code here});
Since a new anonymous delegate is created each time the handler is added, we would need to compare both the “state” and “code of the delegate for instance equivalence. Determining delegate object equivalence is a difficult thing, the code above compares only delegate instance equivalence.

No Comments yet »