Friday, April 22, 2011

MVC RouteConstraint is the culprite

Today, several of our live product sites got a serious issue. The page that renders a list of businesses took over 1 minutes to render. We couldn't find out the problem in serveral hours. After a while keep investigating, we found that the call of below method took about 6 seconds to run:

UrlHelper.Action("action", "controller", RouteValueCollection)  
That means on a page that contains 10 links to the business detail would take about 1 minutes to render. It was really strange because this method is the built-in method of ASP.NET MVC so we didn't think it's our database issus. Another reason helped us beleave in our assumtion was that we tried to debug the service to get the list of businesses from database and saw that it was fast although we have over 1 mil of records in database, just the rendering step was so slow. I kept trying and stepped into MVC source code. It led me to this method which took 6 seconds to run:
RouteCollection.GetVirtualPath(RequestContext, RouteValueDictionary)  
RouteCollection is a class in namespace System.Web.Routing and it was very strange because I couldn't continue to step into this method to debug. But anyways, it gave me another hint, there could be something wrong with our Routing table. So I opened the route registration code in our project and realized that we were using a RouteConstraint for the business route:
  new { controller = "Business", action = "Detail" },
  new { slug = new BusinessSlugConstraint()}
That means whenever it renders the link like "/Business/business-slug" it will check whether the slug "business-slug" exists. We have over 1 mil of businesses in database so it could took a lot of time for this check. For now, what we can do to make a quick fix is rendering the detail links manually without using UrlHelper. It could save us some time and we'll address this issue later. Anyway, from now on, we should be careful when using the RouteConstraint cos it would never jump into it's code when we press F11 unless we put a break point in the RouteConstraint implementation. Cheers


Le Presta said...

In MVC 4 (maybe even in MVC 3, didn't check), you can use the RouteDirection parameter of the IRouteConstraint.Match method to filter whether the route was created by some helper like Url.Action or by an input request. Thus you don't database-check routes that are obviously well formed by you own developpements ;)

if (routeDirection == RouteDirection.UrlGeneration)
return true;

Post a Comment