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); } } } internal static bool IsThrottleException(Exception ex) { var msg = ex.Message; return msg.Contains("429") || msg.Contains("503") || msg.Contains("throttl", StringComparison.OrdinalIgnoreCase); } }