Boy do I love when my SharePoint 2013 CSOM apps bomb with the following love note: "Request uses too many resources." It's like my code is literally asking too much of the API. Or if I'm really having a bad day, I'll encounter the maddening "429 Too many requests" HTTP error. Is IIS getting that stressed out? There are also timeout errors, complaints from the server about MaxObjectPaths being exceeded, and all the other havoc that WFC and its configuration files wreak on our apps.
CSOM is supposed to shield us from all of this, isn't it?
My first attempt to rid my life of these errors was code-based. I built a little helper method that initializes my ClientContexts for me. The listing is below. As you can see, it reads entries from the web.config and uses those values to populate the url and credentials of the context (Line #'s 4 - 9), as well as hacks the RequestTimeout (Line #11). Finally, on Line #13, I have an extension method that loads a bunch of properties on the root Web that I almost always use. (LoadWeb is out of scope for this post.)
Code Listing 1
- public static ClientContext GetContextFromWebDotConfig()
- ClientContext context = new ClientContext(ConfigurationManager.AppSettings["CSOM_URL"]);
- //get csom credentials
- context.Credentials = new NetworkCredential(
- //no timeout
- context.RequestTimeout = int.MaxValue;
- //load root web
- return context;
Despite setting this timeout, I still would get the above errors if my sluggish code took too long to perform expensive operations, such as provisioning webs or creating taxonomy. I recall having similar issues trying desperately to be able to control timeout periods in previous projects when working with WCF or SharePoint or, gasp, both.
Fortunately, for CSOM, I've been able to cobble together a PowerShell script that configures the timeouts (as well as other WCF-ish properties) once and for all. The big secret is that these settings are actually owned by the SPWebApplication versus the farm. This actually makes sense, since a SharePoint web app maps to an IIS web site (which is where the web.config file and WCF live). Let's take a look at the code:
Code Listing 2
- $url = $(Read-Host -Prompt "Web App Url"),
- $timeout = $(Read-Host -Prompt "Timeout (in minutes)"),
- $resourceSize = $(Read-Host -Prompt "Resource Size"))
- #ensure sharepoint
- if ((Get-PSSnapin -Name Microsoft.SharePoint.PowetrShell -ErrorAction SilentlyContinue) -eq $null)
- #load snapin
- Add-PSSnapIn Microsoft.SharePoint.PowerShell;
- #get web app
- $app = Get-SPWebApplication -Identity $url;
- #update settings
- $app.ClientCallableSettings.MaxObjectPaths = $resourceSize;
- $app.ClientCallableSettings.RequestXmlMaxDepth = $resourceSize;
- $app.ClientCallableSettings.MaxResourcesPerRequest = $resourceSize;
- $app.ClientCallableSettings.RequestUsageExecutionTimeThreshold = $resourceSize;
- $app.ClientCallableSettings.ExecutionTimeout = [System.Timespan]::FromMinutes($timeout);
Very straightforward. I used this script to set the SPClientCallableSettings to 30 (minutes) and 1024 for the timeout and resourceSize parameters, respectively, on a web app that hosts code that takes almost ten minutes to run, and it works great. Just remember to run this script against any SharePoint web application you're working with and to include it in your deployment logic (tuned for production of course). I'll leave you with some links to these properties so you can determine the proper values for your environment (I'm using the same value for each in this example for brevity):
Have fun running code for longer than thirty seconds!