using System; using System.Collections.Generic; using System.Threading; using VCommon.Logging; namespace VCommon.Diagnostics { public abstract class Retry { public static void Do(Action action, int maxTry = 3, int retryIntervalMs = 500 , Action onAttempting = null , Action onSuccess = null , Func onFail = null , Action> onAbort = null) { Do(() => { action(); return null; }, maxTry, retryIntervalMs, onAttempting, onSuccess, onFail, onAbort); } public static T Do(Func func, int maxTry = 3, int retryIntervalMs = 500 , Action onAttempting = null , Action onSuccess = null , Func onFail = null , Action> onAbort = null) { var exceptions = new List(maxTry); var attempt = 0; while (++attempt < maxTry) { try { onAttempting?.Invoke(attempt); var r = func(); onSuccess?.Invoke(attempt); return r; } catch (Exception e) { exceptions.Add(e); if (false == (onFail?.Invoke(attempt, e) ?? true)) return default(T); Thread.Sleep(retryIntervalMs); } } var arr = exceptions.ToArray(); onAbort?.Invoke(arr); throw new AggregateException(arr); } } public class RetryPolicy { public int MaxTry { get; } public int RetryIntervalMs { get; } public string Tag { get; } public bool ThrowOnAbort { get; set; } public RetryPolicy(string tag = null, int maxTry = 3, int retryIntervalMs = 500) { if (maxTry < 1) throw new ArgumentOutOfRangeException(nameof(maxTry)); if (retryIntervalMs < 0) throw new ArgumentOutOfRangeException(nameof(maxTry)); MaxTry = maxTry; RetryIntervalMs = retryIntervalMs; Tag = tag; } public void Do(Action action) => Retry.Do(action, MaxTry, RetryIntervalMs, OnAttempting, OnSuccess, OnFail, OnAbort); public T Do(Func func) => Retry.Do(func, MaxTry, RetryIntervalMs, OnAttempting, OnSuccess, OnFail, OnAbort); protected virtual void OnAttempting(int attempted) { //Logger.Debug($"Attempting {Tag}", new { attempted }); } protected virtual void OnSuccess(int attempted) { //Logger.Debug($"Success {Tag}", new { attempted }); } protected virtual bool OnFail(int attempted, Exception exception) { Logger.Warn($"Fail {Tag}", new { attempted }); return true; } protected virtual void OnAbort(IReadOnlyList exceptions) { Logger.Error($"Abort {Tag}", new { exceptions }); if (ThrowOnAbort) throw new AggregateException(exceptions); } } }