private static async Task ExecuteQueryImplementation(ClientRuntimeContext clientContext, int retryCount = 10, int delay = 500, string userAgent = null)
        {
            await new SynchronizationContextRemover();

            // Set the TLS preference. Needed on some server os's to work when Office 365 removes support for TLS 1.0
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

            var clientTag = string.Empty;

            if (clientContext is PnPClientContext)
            {
                retryCount = (clientContext as PnPClientContext).RetryCount;
                delay      = (clientContext as PnPClientContext).Delay;
                clientTag  = (clientContext as PnPClientContext).ClientTag;
            }

            int  retryAttempts      = 0;
            int  backoffInterval    = delay;
            int  retryAfterInterval = 0;
            bool retry = false;
            ClientRequestWrapper wrapper = null;

            if (retryCount <= 0)
            {
                throw new ArgumentException("Provide a retry count greater than zero.");
            }

            if (delay <= 0)
            {
                throw new ArgumentException("Provide a delay greater than zero.");
            }

            // Do while retry attempt is less than retry count
            while (retryAttempts < retryCount)
            {
                try
                {
                    clientContext.ClientTag = SetClientTag(clientTag);

                    // Make CSOM request more reliable by disabling the return value cache. Given we
                    // often clone context objects and the default value is
                    clientContext.DisableReturnValueCache = true;
                    // Add event handler to "insert" app decoration header to mark the PnP Sites Core library as a known application
                    EventHandler <WebRequestEventArgs> appDecorationHandler = AttachRequestUserAgent(userAgent);

                    clientContext.ExecutingWebRequest += appDecorationHandler;

                    // DO NOT CHANGE THIS TO EXECUTEQUERYRETRY
                    if (!retry)
                    {
                        await clientContext.ExecuteQueryAsync();
                    }
                    else
                    {
                        if (wrapper != null && wrapper.Value != null)
                        {
                            await clientContext.RetryQueryAsync(wrapper.Value);
                        }
                    }

                    // Remove the app decoration event handler after the executequery
                    clientContext.ExecutingWebRequest -= appDecorationHandler;

                    return;
                }
                catch (WebException wex)
                {
                    var response = wex.Response as HttpWebResponse;
                    // Check if request was throttled - http status code 429
                    // Check is request failed due to server unavailable - http status code 503
                    if (response != null &&
                        (response.StatusCode == (HttpStatusCode)429 ||
                         response.StatusCode == (HttpStatusCode)503
                         // || response.StatusCode == (HttpStatusCode)500
                        ))
                    {
                        Log.Warning(Constants.LOGGING_SOURCE, CoreResources.ClientContextExtensions_ExecuteQueryRetry, backoffInterval);

                        wrapper            = (ClientRequestWrapper)wex.Data["ClientRequest"];
                        retry              = true;
                        retryAfterInterval = backoffInterval;

                        //Add delay for retry, retry-after header is specified in seconds
                        await Task.Delay(retryAfterInterval);

                        //Add to retry count and increase delay.
                        retryAttempts++;
                        backoffInterval = backoffInterval * 2;
                    }
                    else
                    {
                        var errorSb = new System.Text.StringBuilder();

                        errorSb.AppendLine(wex.ToString());

                        if (response != null)
                        {
                            //if(response.Headers["SPRequestGuid"] != null)
                            if (response.Headers.AllKeys.Any(k => string.Equals(k, "SPRequestGuid", StringComparison.InvariantCultureIgnoreCase)))
                            {
                                var spRequestGuid = response.Headers["SPRequestGuid"];
                                errorSb.AppendLine($"ServerErrorTraceCorrelationId: {spRequestGuid}");
                            }
                        }

                        Log.Error(Constants.LOGGING_SOURCE, CoreResources.ClientContextExtensions_ExecuteQueryRetryException, errorSb.ToString());
                        throw;
                    }
                }
                catch (Microsoft.SharePoint.Client.ServerException serverEx)
                {
                    var errorSb = new System.Text.StringBuilder();

                    errorSb.AppendLine(serverEx.ToString());
                    errorSb.AppendLine($"ServerErrorCode: {serverEx.ServerErrorCode}");
                    errorSb.AppendLine($"ServerErrorTypeName: {serverEx.ServerErrorTypeName}");
                    errorSb.AppendLine($"ServerErrorTraceCorrelationId: {serverEx.ServerErrorTraceCorrelationId}");
                    errorSb.AppendLine($"ServerErrorValue: {serverEx.ServerErrorValue}");
                    errorSb.AppendLine($"ServerErrorDetails: {serverEx.ServerErrorDetails}");

                    Log.Error(Constants.LOGGING_SOURCE, CoreResources.ClientContextExtensions_ExecuteQueryRetryException, errorSb.ToString());

                    throw;
                }
            }

            throw new MaximumRetryAttemptedException($"Maximum retry attempts {retryCount}, has be attempted.");
        }
        private static void ExecuteQueryImplementation(ClientRuntimeContext clientContext, int retryCount = 10, int delay = 500, string userAgent = null)
#endif
        {
#if !ONPREMISES
            await new SynchronizationContextRemover();

            // Set the TLS preference. Needed on some server os's to work when Office 365 removes support for TLS 1.0
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif

            var clientTag = string.Empty;
            if (clientContext is PnPClientContext)
            {
                retryCount = (clientContext as PnPClientContext).RetryCount;
                delay      = (clientContext as PnPClientContext).Delay;
                clientTag  = (clientContext as PnPClientContext).ClientTag;
            }

            int retryAttempts   = 0;
            int backoffInterval = delay;

#if !ONPREMISES
            int  retryAfterInterval = 0;
            bool retry = false;
            ClientRequestWrapper wrapper = null;
#endif

            if (retryCount <= 0)
            {
                throw new ArgumentException("Provide a retry count greater than zero.");
            }

            if (delay <= 0)
            {
                throw new ArgumentException("Provide a delay greater than zero.");
            }

            // Do while retry attempt is less than retry count
            while (retryAttempts < retryCount)
            {
                try
                {
                    clientContext.ClientTag = SetClientTag(clientTag);

                    // Make CSOM request more reliable by disabling the return value cache. Given we
                    // often clone context objects and the default value is
#if !ONPREMISES || SP2016 || SP2019
                    clientContext.DisableReturnValueCache = true;
#endif
                    // Add event handler to "insert" app decoration header to mark the PnP Sites Core library as a known application
                    EventHandler <WebRequestEventArgs> appDecorationHandler = AttachRequestUserAgent(userAgent);

                    clientContext.ExecutingWebRequest += appDecorationHandler;

                    // DO NOT CHANGE THIS TO EXECUTEQUERYRETRY
#if !ONPREMISES
                    if (!retry)
                    {
#if !NETSTANDARD2_0
                        await clientContext.ExecuteQueryAsync();
#else
                        clientContext.ExecuteQuery();
#endif
                    }
                    else
                    {
                        if (wrapper != null && wrapper.Value != null)
                        {
#if !NETSTANDARD2_0
                            await clientContext.RetryQueryAsync(wrapper.Value);
#else
                            clientContext.RetryQuery(wrapper.Value);
#endif
                        }
                    }
#else
                    clientContext.ExecuteQuery();
#endif

                    // Remove the app decoration event handler after the executequery
                    clientContext.ExecutingWebRequest -= appDecorationHandler;

                    return;
                }
                catch (WebException wex)
                {
                    var response = wex.Response as HttpWebResponse;
                    // Check if request was throttled - http status code 429
                    // Check is request failed due to server unavailable - http status code 503
                    if (response != null && (response.StatusCode == (HttpStatusCode)429 || response.StatusCode == (HttpStatusCode)503))
                    {
                        Log.Warning(Constants.LOGGING_SOURCE, CoreResources.ClientContextExtensions_ExecuteQueryRetry, backoffInterval);

#if !ONPREMISES
                        wrapper = (ClientRequestWrapper)wex.Data["ClientRequest"];
                        retry   = true;

                        // Determine the retry after value - use the retry-after header when available
                        // Retry-After seems to default to a fixed 120 seconds in most cases, let's revert to our
                        // previous logic
                        //string retryAfterHeader = response.GetResponseHeader("Retry-After");
                        //if (!string.IsNullOrEmpty(retryAfterHeader))
                        //{
                        //    if (!Int32.TryParse(retryAfterHeader, out retryAfterInterval))
                        //    {
                        //        retryAfterInterval = backoffInterval;
                        //    }
                        //}
                        //else
                        //{
                        retryAfterInterval = backoffInterval;
                        //}

                        //Add delay for retry, retry-after header is specified in seconds
                        await Task.Delay(retryAfterInterval);
#else
                        Thread.Sleep(backoffInterval);
#endif

                        //Add to retry count and increase delay.
                        retryAttempts++;
                        backoffInterval = backoffInterval * 2;
                    }
                    else
                    {
                        Log.Error(Constants.LOGGING_SOURCE, CoreResources.ClientContextExtensions_ExecuteQueryRetryException, wex.ToString());
                        throw;
                    }
                }
            }
            throw new MaximumRetryAttemptedException($"Maximum retry attempts {retryCount}, has been reached.");
        }
        /// <summary>
        /// Executes the query with incremental retry.
        /// </summary>
        /// <param name="clientContext">The client context.</param>
        /// <param name="retryCount">The retry count.</param>
        /// <param name="delay">The delay.</param>
        /// <exception cref="ArgumentException">
        /// Provide a retry count greater than zero.
        /// or
        /// Provide a delay greater than zero.
        /// </exception>
        /// <exception cref="MaximumRetryAttemptedException">Maximum retry attempts {retryCount}</exception>
        public static void ExecuteQueryWithIncrementalRetry(this ClientContext clientContext, int retryCount, int delay)
        {
            int  retryAttempts      = 0;
            int  backoffInterval    = delay;
            int  retryAfterInterval = 0;
            bool retry = false;
            ClientRequestWrapper wrapper = null;

            if (retryCount <= 0)
            {
                throw new ArgumentException("Provide a retry count greater than zero.");
            }
            if (delay <= 0)
            {
                throw new ArgumentException("Provide a delay greater than zero.");
            }

            clientContext.RequestTimeout = Timeout.Infinite;

            // Do while retry attempt is less than retry count
            while (retryAttempts < retryCount)
            {
                try
                {
                    if (!retry)
                    {
                        clientContext.ExecuteQuery();
                        return;
                    }
                    else
                    {
                        // retry the previous request
                        if (wrapper != null && wrapper.Value != null)
                        {
                            clientContext.RetryQuery(wrapper.Value);
                            return;
                        }
                    }
                }
                catch (WebException ex)
                {
                    var response = ex.Response as HttpWebResponse;
                    // Check if request was throttled - http status code 429
                    // Check is request failed due to server unavailable - http status code 503
                    if (response != null && (response.StatusCode == (HttpStatusCode)429 || response.StatusCode == (HttpStatusCode)503))
                    {
                        wrapper = (ClientRequestWrapper)ex.Data["ClientRequest"];
                        retry   = true;

                        // Determine the retry after value - use the retry-after header when available
                        string retryAfterHeader = response.GetResponseHeader("Retry-After");
                        if (!string.IsNullOrEmpty(retryAfterHeader))
                        {
                            if (!Int32.TryParse(retryAfterHeader, out retryAfterInterval))
                            {
                                retryAfterInterval = backoffInterval;
                            }
                        }
                        else
                        {
                            retryAfterInterval = backoffInterval;
                        }

                        // Delay for the requested seconds
                        Thread.Sleep(retryAfterInterval * 1000);

                        // Increase counters
                        retryAttempts++;
                        backoffInterval = backoffInterval * 2;
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            throw new MaximumRetryAttemptedException($"Maximum retry attempts {retryCount}, has be attempted.");
        }