Skip to content

Commit 99a9ec3

Browse files
committed
Updated CSOM throttling guidance
1 parent f73cd38 commit 99a9ec3

File tree

1 file changed

+74
-42
lines changed

1 file changed

+74
-42
lines changed

docs/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online.md

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ What are the recommendations?
130130
> Format of the user agent string is expected to follow [RFC2616](http://www.ietf.org/rfc/rfc2616.txt), so please follow up on the above guidance on the right separators. It is also fine to append existing user agent string with the requested information.
131131
132132
> [!NOTE]
133-
> If you are developing front end components executing in the browser, most of modern browsers don't allow overwritting the user agent string and you don't need to implement this.
133+
> If you are developing front end components executing in the browser, most of modern browsers don't allow overwriting the user agent string and you don't need to implement this.
134134
135135
### Example of decorating traffic with User agent when using Client Side Object Model (CSOM)
136136

@@ -148,7 +148,7 @@ using (var ctx = new ClientContext("https://contoso.sharepoint.com/sites/team"))
148148
{
149149
e.WebRequestExecutor.WebRequest.UserAgent = "NONISV|Contoso|GovernanceCheck/1.0";
150150
};
151-
151+
152152
// Normal CSOM Call with custom User-Agent information
153153
Web site = ctx.Web;
154154
ctx.Load(site);
@@ -169,62 +169,94 @@ endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken);
169169
HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse();
170170
```
171171

172+
### CSOM Code sample: ExecuteQueryWithIncrementalRetry extension method
172173

173-
### CSOM Code sample: ExecuteQueryWithIncrementalRetry method
174-
174+
> [!NOTE]
175+
> You'll need to use SharePoint Online CSOM version 16.1.8316.1200 (December 2018 version) or higher.
175176
176-
```
177+
Add this extension method in a static class and use `ExecuteQueryWithIncrementalRetry` instead of `ExecuteQuery` to make your code handle throttling requests.
177178

178-
public static void ExecuteQueryWithIncrementalRetry(this ClientContext context, int retryCount, int delay)
179+
```cs
180+
public static void ExecuteQueryWithIncrementalRetry(this ClientContext clientContext, int retryCount, int delay)
181+
{
182+
int retryAttempts = 0;
183+
int backoffInterval = delay;
184+
int retryAfterInterval = 0;
185+
bool retry = false;
186+
ClientRequestWrapper wrapper = null;
187+
if (retryCount <= 0)
188+
throw new ArgumentException("Provide a retry count greater than zero.");
189+
if (delay <= 0)
190+
throw new ArgumentException("Provide a delay greater than zero.");
191+
192+
// Do while retry attempt is less than retry count
193+
while (retryAttempts < retryCount)
194+
{
195+
try
179196
{
180-
int retryAttempts = 0;
181-
int backoffInterval = delay;
182-
if (retryCount <= 0)
183-
throw new ArgumentException("Provide a retry count greater than zero.");
184-
if (delay <= 0)
185-
throw new ArgumentException("Provide a delay greater than zero.");
186-
while (retryAttempts < retryCount)
197+
if (!retry)
187198
{
188-
try
199+
clientContext.ExecuteQuery();
200+
return;
201+
}
202+
else
203+
{
204+
// retry the previous request
205+
if (wrapper.Value != null)
189206
{
190-
context.ExecuteQuery();
207+
clientContext.RetryQuery(wrapper.Value);
191208
return;
192209
}
193-
catch (WebException wex)
194-
{
195-
var response = wex.Response as HttpWebResponse;
196-
if (response != null &amp;&amp; response.StatusCode == (HttpStatusCode)429)
197-
{
198-
int retryAfterInMs = DefaultRetryAfterInMs;
199-
string retryAfter = response.GetResponseHeader(RetryAfterHeaderName);
200-
if (!string.IsNullOrEmpty(retryAfter * 1000))
201-
{
202-
if (!int.TryParse(retryAfter, out retryAfterInMs))
203-
{
204-
retryAfterInMs = DefaultRetryAfterInMs;
205-
}
206-
}
207-
208-
Console.WriteLine(string.Format("CSOM request exceeded usage limits. Sleeping for {0} milliseconds before retrying.", retryAfterInMs));
209-
//Add delay.
210-
System.Threading.Thread.Sleep(retryAfterInMs);
211-
//Add to retry count.
212-
retryAttempts++;
213-
}
210+
}
211+
}
212+
catch (WebException ex)
213+
{
214+
var response = ex.Response as HttpWebResponse;
215+
// Check if request was throttled - http status code 429
216+
// Check is request failed due to server unavailable - http status code 503
217+
if (response != null && (response.StatusCode == (HttpStatusCode)429 || response.StatusCode == (HttpStatusCode)503))
218+
{
219+
wrapper = (ClientRequestWrapper)ex.Data["ClientRequest"];
220+
retry = true;
214221

215-
else
222+
// Determine the retry after value - use the retry-after header when available
223+
string retryAfterHeader = response.GetResponseHeader("Retry-After");
224+
if (!string.IsNullOrEmpty(retryAfterHeader))
225+
{
226+
if (!Int32.TryParse(retryAfterHeader, out retryAfterInterval))
216227
{
217-
throw;
228+
retryAfterInterval = backoffInterval;
218229
}
219230
}
231+
else
232+
{
233+
retryAfterInterval = backoffInterval;
234+
}
235+
236+
// Delay for the requested milliseconds
237+
Thread.Sleep(retryAfterInterval);
238+
239+
// Increase counters
240+
retryAttempts++;
241+
backoffInterval = backoffInterval * 2;
220242
}
221-
throw new MaximumRetryAttemptedException(string.Format("Maximum retry attempts {0}, have been attempted.", retryCount));
222-
}
243+
else
244+
{
245+
throw;
246+
}
247+
}
248+
}
249+
throw new MaximumRetryAttemptedException($"Maximum retry attempts {retryCount}, has be attempted.");
250+
}
223251

252+
[Serializable]
253+
public class MaximumRetryAttemptedException : Exception
254+
{
255+
public MaximumRetryAttemptedException(string message) : base(message) { }
256+
}
224257
```
225258

226-
227-
## What should you do if you get blocked in SharePoint Online?
259+
# What should you do if you get blocked in SharePoint Online?
228260
<a name="BKMK_Whatshouldyoudoifyougetblocked"> </a>
229261

230262
Blocking is the most extreme form of throttling. We rarely ever block a tenant, unless we detect long-term, extremely excessive traffic that may threaten the overall health of the SharePoint Online service. We apply blocks to prevent excessive traffic from degrading the performance and reliability of SharePoint Online. A block - which is usually placed at the app or user level - prevents the offending process from running until you fix the problem. If we block your subscription, you must take action to modify the offending processes before the block can be removed.

0 commit comments

Comments
 (0)