It’s hard to believe that nearly two months have already passed since opening the doors on the Username Availability Validator project. I made a few mistakes in how I ran the project, but I’m happy with the overall outcome.

However, one thing that disappointed me throughout the entire contest was how slow CodePlex can be. Even as the single person most invested in the success of the project, I often found myself leaving the site in the middle of tasks, unwilling to endure the slowness. What’s worse, I also found myself procrastinating to avoid returning to CodePlex and dealing with these waits.

I’m not one to complain without offering some constructive criticism though. Especially since we’re talking about a completely free service.

I think the lesson to be learned here is something that many ASP.NET developers could benefit from. So, I’d like to take a look at one ASP.NET AJAX related inefficiency that I found on CodePlex and one potential alternative to it.


A specific example

To get a concrete handle on this, I’m going to focus on one function that’s in need of optimization: The Issue Tracker’s voting feature. In fact, there’s already a work item on CodePlex about this specific issue.

For example, this is the Issue Tracker for the ASP.NET project:

codeplex-issue-voting

Its operation is straightforward. When you click a “vote” link, a partial postback is triggered to register your vote in the TFS backend, and then update the displayed votes tally.

You may have written similar code yourself, using UpdatePanels. I have too.

Unfortunately, this performs extremely poorly on a complex page. I’ve personally seen the round-trip time for a single CodePlex vote range anywhere from several seconds up to more than a minute.

I can’t imagine an argument for this being acceptable. When a system is that slow, your users learn not to bother attempting a potentially slow operation, and you’re left with pages of mostly single voted items, as seen on most CodePlex projects.

The Request: A big base64 blob

It seems like simple enough functionality, so why does it run so slowly?

A quick look at the HTTP traffic between the client and server is enlightening. This is the HTTP request that is generated when clicking a “vote” button:

codeplex-request-detail

17,358 bytes. Note that this is just the request to the server, not including the server’s response. You would hope that “vote++ for this issue” could be expressed more succinctly than 17k of base64 gibberish!

Even worse than actually transferring this bulk of data around, the server must also re-instantiate the Page and all of its controls based on this ViewState data, then run them all through their lifecycles.

In the end, all of these gyrations add up to a tremendous amount of overhead. In CodePlex’s case, these unnecessary Page instantiations are likely the true culprit.

The Response: Bigger is not better

Things only get uglier when the response comes back down from the server:

codeplex-response-detail

Not only does the response contain HTML markup to completely replace every one of the voting blocks, it also contains an updated ViewState field just as large as the request’s. So, this response is roughly as large as the entire request and the HTML in each UpdatePanel.

In this case, that weighs in at hefty 28,830 bytes.

Compared to transferring the ViewState up to the server and waiting on the server to completely re-instantiate and re-render the Page, the response’s payload isn’t as big a problem as its size implies. However, it certainly adds overhead.

It doesn’t have to be this way

There are significantly better ways to do this. Let’s take a look at how another site, Stack Overflow, handles the voting scenario.

For example, take a look at this Stack Overflow answer that I posted recently. If you click its up-vote arrow, an asynchronous POST request will be sent to this URL:

http://stackoverflow.com/questions/171014/vote

This POST request would contain only this data:

voteTypeId=2

The resulting response? A compact JSON object indicating the result:

{"Success":true,"NewScore":2,"Message":"","LastVoteTypeId":2}

That’s it.

No HTML. No monstrous ViewState. No bloat. Oh, and the entire round-trip takes a fraction of a second instead of several to dozens of seconds.

Is this the answer?

Could CodePlex’s issue voting work like Stack Overflow’s? Absolutely!

The Stack Overflow vote feature is a request against an ASP.NET MVC controller action, but it could just as easily be implemented without MVC by using a web service. The ASP.NET AJAX client library (which CodePlex is already using) provides excellent support for calling ASMX or WCF services and handling their JSON result.

Allowing for testing and the inevitable complications that arise, CodePlex’s slow voting could probably be fixed in just a few hours.

Conclusion

We are generally presented with a wide array of methods for accomplishing a given task. Unfortunately, somewhere along the way we’ve acquired this faulty attitude that, as long as they get the job done, all methods are roughly equivalent.

These two voting implementations are a great example of the fallacy in that line of thinking. Both solutions are similarly complex and function correctly, yet differ in performance by orders of magnitude.

I hope seeing the massive difference in black and white might help you avoid this pitfall in the future yourself. While the problem with CodePlex’s issue voting lends itself to an easy, superficial fix, it’s just as easy to paint yourself into an corner that isn’t as trivial to escape from.