mirror of
https://github.com/LukeHagar/plexcsharp.git
synced 2025-12-09 20:47:50 +00:00
ci: regenerated with OpenAPI Doc , Speakeasy CLI 1.391.3
This commit is contained in:
47
LukeHagar/PlexAPI/SDK/Utils/Retries/BackoffStrategy.cs
Normal file
47
LukeHagar/PlexAPI/SDK/Utils/Retries/BackoffStrategy.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils.Retries
|
||||
{
|
||||
public class BackoffStrategy
|
||||
{
|
||||
public readonly long InitialIntervalMs;
|
||||
public readonly long MaxIntervalMs;
|
||||
public readonly long MaxElapsedTimeMs;
|
||||
public readonly double BaseFactor;
|
||||
public readonly double JitterFactor;
|
||||
|
||||
/// <summary>
|
||||
/// Configures the exponential backoff strategy.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The duration between consecutive attempts is calculated as follows:
|
||||
/// intervalMs = min(maxIntervalMs, initialIntervalMs*(baseFactor^attempts) +/- r)
|
||||
/// where baseFactor (also referred to as "exponent") is the multiplicative factor
|
||||
/// and r a random value between 0 and jitterFactor*intervalMs.
|
||||
/// </remarks>
|
||||
/// <param name="initialIntervalMs">The initial interval in milliseconds.</param>
|
||||
/// <param name="maxIntervalMs">The maximum interval in milliseconds.</param>
|
||||
/// <param name="maxElapsedTimeMs">The maximum elapsed time in milliseconds.</param>
|
||||
/// <param name="exponent">The base factor used to compute the exponential interval</param>
|
||||
/// <param name="jitterFactor">The jitter factor used to randomize the backoff interval</param>
|
||||
public BackoffStrategy(long initialIntervalMs,
|
||||
long maxIntervalMs,
|
||||
long maxElapsedTimeMs,
|
||||
double exponent,
|
||||
double jitterFactor = 0.5)
|
||||
{
|
||||
InitialIntervalMs = initialIntervalMs;
|
||||
MaxIntervalMs = maxIntervalMs;
|
||||
MaxElapsedTimeMs = maxElapsedTimeMs;
|
||||
BaseFactor = exponent;
|
||||
JitterFactor = jitterFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
156
LukeHagar/PlexAPI/SDK/Utils/Retries/Retries.cs
Normal file
156
LukeHagar/PlexAPI/SDK/Utils/Retries/Retries.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils.Retries
|
||||
{
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Models.Errors;
|
||||
|
||||
public class Retries
|
||||
{
|
||||
private Func<Task<HttpResponseMessage>> action;
|
||||
private RetryConfig retryConfig;
|
||||
private List<string> statusCodes;
|
||||
|
||||
public Retries(Func<Task<HttpResponseMessage>> action, RetryConfig retryConfig, List<string> statusCodes)
|
||||
{
|
||||
this.action = action;
|
||||
this.retryConfig = retryConfig;
|
||||
this.statusCodes = statusCodes;
|
||||
|
||||
if (statusCodes.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("statusCodes list cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PermanentException : Exception
|
||||
{
|
||||
public PermanentException(Exception innerException) : base("NonRetryable error.", innerException) { }
|
||||
}
|
||||
|
||||
public sealed class RetryableException : Exception
|
||||
{
|
||||
public HttpResponseMessage? Response = null;
|
||||
|
||||
public RetryableException(HttpResponseMessage response) {
|
||||
Response = response;
|
||||
}
|
||||
|
||||
public RetryableException(Exception innerException) : base("An error occurred.", innerException) { }
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> Run()
|
||||
{
|
||||
switch(retryConfig.Strategy) {
|
||||
case RetryConfig.RetryStrategy.BACKOFF:
|
||||
return await retryWithBackoff(retryConfig.RetryConnectionErrors);
|
||||
|
||||
case RetryConfig.RetryStrategy.NONE:
|
||||
return await action();
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Invalid retry strategy");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> GetResponseAsync(bool retryConnectionErrors)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await action();
|
||||
|
||||
foreach (var statusCode in statusCodes)
|
||||
{
|
||||
if (statusCode.ToUpper().Contains("X"))
|
||||
{
|
||||
var codeRange = int.Parse(statusCode.Substring(0, 1));
|
||||
var statusMajor = (int)response.StatusCode / 100;
|
||||
if (codeRange == statusMajor)
|
||||
{
|
||||
throw new RetryableException(response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var code = int.Parse(statusCode);
|
||||
if (code == (int)response.StatusCode)
|
||||
{
|
||||
throw new RetryableException(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
catch (RetryableException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if ((ex is HttpRequestException || ex is TaskCanceledException) && retryConfig.RetryConnectionErrors)
|
||||
{
|
||||
throw new RetryableException(ex);
|
||||
}
|
||||
throw new PermanentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> retryWithBackoff(bool retryConnectionErrors)
|
||||
{
|
||||
var backoff = retryConfig.Backoff;
|
||||
if(backoff == null){
|
||||
throw new ArgumentException("Backoff strategy is not defined");
|
||||
}
|
||||
|
||||
var startMs = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
var numAttempts = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await GetResponseAsync(retryConnectionErrors);
|
||||
}
|
||||
catch (PermanentException ex)
|
||||
{
|
||||
throw ex.InnerException!;
|
||||
}
|
||||
catch (RetryableException ex)
|
||||
{
|
||||
var nowMs = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
if (nowMs - startMs > backoff.MaxElapsedTimeMs)
|
||||
{
|
||||
if(ex.Response != null)
|
||||
{
|
||||
return ex.Response;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
var intervalMs = backoff.InitialIntervalMs * Math.Pow(backoff.BaseFactor, numAttempts);
|
||||
var jitterMs = backoff.JitterFactor * intervalMs;
|
||||
intervalMs = intervalMs - jitterMs + new Random().NextDouble() * (2 * jitterMs + 1);
|
||||
intervalMs = Math.Min(intervalMs, backoff.MaxIntervalMs);
|
||||
|
||||
await Task.Delay((int)intervalMs);
|
||||
numAttempts += 1;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new Exception("Unexpected error occurred.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
LukeHagar/PlexAPI/SDK/Utils/Retries/RetryConfig.cs
Normal file
46
LukeHagar/PlexAPI/SDK/Utils/Retries/RetryConfig.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils.Retries
|
||||
{
|
||||
public class RetryConfig
|
||||
{
|
||||
public enum RetryStrategy
|
||||
{
|
||||
BACKOFF,
|
||||
NONE
|
||||
}
|
||||
public readonly RetryStrategy Strategy;
|
||||
public readonly BackoffStrategy? Backoff;
|
||||
public readonly bool RetryConnectionErrors;
|
||||
|
||||
/// <summary>
|
||||
/// Selects the retry strategy.
|
||||
/// </summary>
|
||||
/// <param name="strategy">The retry strategy.</param>
|
||||
/// <param name="backoff">The backoff strategy configuration (if applicable)</param>
|
||||
/// <param name="retryConnectionErrors">Whether to retry on connection errors.</param>
|
||||
/// <remarks>
|
||||
/// The backoff strategy is only required if the retry strategy is set to BACKOFF.
|
||||
/// To disable retries, set the strategy to NONE.
|
||||
/// </remarks>
|
||||
public RetryConfig(RetryStrategy strategy, BackoffStrategy? backoff = null, bool retryConnectionErrors = false)
|
||||
{
|
||||
|
||||
if (strategy == RetryStrategy.BACKOFF && backoff == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("Backoff strategy configuration was not provided");
|
||||
}
|
||||
|
||||
Strategy = strategy;
|
||||
Backoff = backoff;
|
||||
RetryConnectionErrors = retryConnectionErrors;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user