Sunday 21 December 2014

.Net XDocument ToString() Deadlock

I have been working on a little project recently in which we was using some heavy .Net Runtime Caching of objects to reduce the strain on server resources as users access the same page over and over. We also implemented the same approach for caching the sites RSS feed. Crudely we was doing something similar to the following:

public ActionResult Feed() {
    XDocument document = null;

    if (Cache["feed"] == null) {
        document  = new XDocument(...);
        // Fill the document here...
        Cache["feed"] = document;
    } else {
        document = Cache["feed"] as XDocument;
    }

    return Content(document.ToString(), 
        "application/rss+xml");
}

The above is a stripped down example and is prone to errors in that if many users hit the method at the same time, there is no inherent locking, also the cache feed value could be ejected before it is read the second time. But we aren't here to work on that.

The issue here is the line:

return Content(document.ToString(), 
    "application/rss+xml");

Because we were caching the XDocument and for each request calling ToString() this would lead to some untimely deadlocks on the liver servers where requests would run for hours never ending. The only solution was to kill the process and start the web site again.

After a little digging it was clear that the ToString() call being an instance method was to blame. I trudged through the source code for it and found it using several internals to iterate over the nodes to produce the string result. There is also a hint in the documentation for XDocument http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument%28v=vs.110%29.aspx which states:
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
 So the solution was to simply cache the resulting ToString() rather than the XDocument, this way there is no further processing required every time a user requests this method.

public ActionResult Feed() {
    string xml = null;

    if (Cache["feed"] == null) {
        XDocument document  = new XDocument(...);
        // Fill the document here...
        Cache["feed"] = document.ToString();
    } else {
        xml = (string) Cache["feed"];
    }

    return Content(xml, 
        "application/rss+xml");
}

Sunday 17 August 2014

MySQL Entity Framework 6 Include Performance Woes

Just a short post today, but after some pretty exhaustive performance testing with Entity Framework 6 and MySQL .NET Connector I am pretty much in the camp that .Include(...) is only ever detrimental to performance. This is down to the really poor query generation with MySQL and Entity Framework.
I have tested so far 8 parts of our current site, all different scopes of query including single record lookups, listings for a directory and more advanced filters to query on specific parameters and in each case adding Includes has increased the query times as opposed to Entity Frameworks lazy loading strategy. This seems really foreign to me but after looking at the SQL generated it is clear why MySQL has a tough time running the queries quickly. In short, stick to Lazy Loading with MySQL until the query generation in Entity Framework improves.