using Microsoft.SharePoint.Client;
using SharepointToolbox.Core.Models;
namespace SharepointToolbox.Core.Helpers;
public static class ExecuteQueryRetryHelper
{
private const int MaxRetries = 5;
///
/// Executes a SharePoint query with automatic retry on throttle (429/503).
/// Surfaces retry events via progress for user visibility ("Throttled ā retrying in 30sā¦").
///
public static async Task ExecuteQueryRetryAsync(
ClientContext ctx,
IProgress? progress = null,
CancellationToken ct = default)
{
int attempt = 0;
while (true)
{
ct.ThrowIfCancellationRequested();
try
{
await ctx.ExecuteQueryAsync();
return;
}
catch (Exception ex) when (IsThrottleException(ex) && attempt < MaxRetries)
{
attempt++;
int delaySeconds = (int)Math.Pow(2, attempt) * 5; // 10, 20, 40, 80, 160s
progress?.Report(OperationProgress.Indeterminate(
$"Throttled by SharePoint ā retrying in {delaySeconds}s (attempt {attempt}/{MaxRetries})ā¦"));
await Task.Delay(TimeSpan.FromSeconds(delaySeconds), ct);
}
}
}
private static bool IsThrottleException(Exception ex)
{
var msg = ex.Message;
return msg.Contains("429") || msg.Contains("503") ||
msg.Contains("throttl", StringComparison.OrdinalIgnoreCase);
}
}