This is a bit of a long story, but I felt it necessary to post the detail just in case someone has seen similar behaviour (or this is a known issue with parts). I've cross posted because there's a general ASP.Net theme as well as a SharePoint flavour.
I've been helping a customer diagnose performance issues on their SharePoint 2010 site. Specifically, the w3wp.exe process goes to 100% CPU for for an hour or so. Before digging too deeply, I looked into some of the code. Now, I'm no SharePoint developer but some things were obvious and were clearly the result of System.NullReferenceException based ASP.Net warnings in the event log and other things scared me. Once was disposing of system created objects, particularly the SP RootWeb object because they were surrounded the reference by a using(). Another thing that did not sit well was the 'hack' necessary to get LINQ working on sites with anonymous access - this is a public Internet facing web site. Microsoft's own documentation states that one should use SPQuery in these circumstances (until they improve the functionality in Microsoft.SharePoint.Linq anyway). In particular, there's a need to elevate privileges because SPLists are secured but anonymous access is required. So my theory is they did software development by Google and found this: http://jcapka.blogspot.com/2010/05/making-linq-to-sharepoint-work-for.html And: http://blogs.msdn.com/b/sowmyancs/archive/2010/09/19/linq-to-sharepoint-and-runwithelevatedprivileges.aspx I wish they'd found the latter but, sadly, they chose the former code to implement as a *static *helper method. There's a very subtle but important difference - the former doesn't behave nicely when you get an exception in the anonymous delegate code passed in because it does not set the HttpContext back to the original value and you get a NullReferenceException in the logs. This was the cause of one of the issues. Obvious. Fixed that. Job done? Nope. *Lesson learnt* - don't "develop by Google" unless you know what the code is doing, line by line. I included this part because I think there's an issue with the *static helper* ,but I will come back to that. Oddly, the exception that is thrown while executing the anonymous delegate is "An item with the same key has already been added.". Huh? All they were doing was enumerating the list. This has been reported by others. eg: http://social.technet.microsoft.com/Forums/en-US/sharepoint2010programming/thread/ff3c4212-0372-4088-972b-108c1eda2ee6/ No idea what's going on there (at this point). So I finally get access to the server when the issue is happening and managed to get a full memory dump of w3wp.exe. Immediately upon looking at the CLR stack of long running threads the problem was obvious but the cause is not so. System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].*FindEntry*(System.__Canon) System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].TryGetValue(System.__Canon, System.__Canon ByRef) Microsoft.SharePoint.Linq.Rules.ToEnumerableProcessor.GetEnumerableOperator(System.Reflection.MethodInfo) Microsoft.SharePoint.Linq.Rules.ToEnumerableProcessor.ConvertMethod(System.Linq.Expressions.MethodCallExpression, Context) Microsoft.SharePoint.Linq.Rules.ToEnumerableProcessor.<.cctor>b__13(System.Linq.Expressions.MethodCallExpression, Context) Microsoft.SharePoint.Linq.Rules.GuardedRule`4+<>c__DisplayClass3[[System.__Canon, mscorlib],[System.__Canon, mscorlib],[Microsoft.SharePoint.Linq.Rules.ToEnumerableProcessor+Context, Microsoft.SharePoint.Linq],[System.__Canon, mscorlib]].<.ctor>b__1(System.__Canon, Context) Microsoft.SharePoint.Linq.Rules.SwitchRule`3[[System.__Canon, mscorlib],[Microsoft.SharePoint.Linq.Rules.ToEnumerableProcessor+Context, Microsoft.SharePoint.Linq],[System.__Canon, mscorlib]].Apply(System.__Canon, Context) Microsoft.SharePoint.Linq.Rules.ToEnumerableProcessor.Process(System.Linq.Expressions.Expression, System.Collections.Generic.List`1> ByRef) Microsoft.SharePoint.Linq.SPLinqProvider.Rewrite(System.Linq.Expressions.Expression, System.Collections.Generic.List`1> ByRef) Microsoft.SharePoint.Linq.SPLinqProvider.RewriteAndCompile[[System.__Canon, mscorlib]](System.Linq.Expressions.Expression, System.Collections.Generic.List`1> ByRef) Microsoft.SharePoint.Linq.LinqQuery`1[[System.__Canon, mscorlib]].GetEnumerator() Dictionary objects aren't thread safe (if you're modifying them) and you can read more about that from Tess' blog. http://blogs.msdn.com/b/tess/archive/2009/12/21/high-cpu-in-net-app-using-a-static-generic-dictionary.aspx But my customer's not using Dictionary objects, anywhere! So I pulled out Reflector to follow this through. Turns out that Microsoft.SharePoint.Linq is, and a *static *one at that. But why did this happen in the first place??? I can see from the CLR stack traces that 8 long running threads are all stuck doing the same think (above) and the dump of stack objects shows that each thread is referencing the same object. In fact it looks like they are all executing the same code. My suggested fix it to follow the MSDN guidance and NOT use LINQ for anonymous access and use SPQuery and write the CAML. They are doing this now and we will see if that stops the CPU "spikes" (plateaus). Hope so, because I'm sick of looking at My questions are: - Is this a known or previously observed issue? - The "An item with the same key has already been added" is caused by the use of the static Dictionary and static helper? - Can it be that the use of a static helper combined with anonymous delegate which might get compiled into a totally static method be the cause (I don't know enough about the innards of the IL to know or check)? You probably want to see code, but I thought the blog posts should do the trick as there's not much more to it. -- *Richard Carde* E: rich...@carde.id.au
_______________________________________________ ozmoss mailing list ozmoss@ozmoss.com http://prdlxvm0001.codify.net/mailman/listinfo/ozmoss