【问题标题】:Polly fallback action did not throw the specified exception. What happened?Polly 回退操作未引发指定的异常。发生了什么?
【发布时间】:2021-05-21 11:46:38
【问题描述】:

鉴于此代码尝试调用 AuthenticationManager.Authenticate() 10 次,然后在 10 次尝试失败后抛出 CustomException

string res = Policy
    .Handle<Exception>() // Handles any exception
    .OrResult<string>(result => string.IsNullOrWhiteSpace(result)) // or if result is null
    .Fallback(() => throw new CustomException("Exception!")) // Not being thrown after 10 unsuccessful attempts
    .Wrap(
        Policy
            .Handle<Exception>()
            .OrResult<string>(result => string.IsNullOrWhiteSpace(result))
            .WaitAndRetry(
                10,
                retryAttempt => TimeSpan.FromSeconds(60),
                onRetry: (response, delay, retryCount, context) => Trace.WriteLine($"[{DateTime.UtcNow}] Authentication failed. Retrying after 60 seconds...(Attempt {retryCount} of 10)")))
    .ExecuteAndCapture(() => AuthenticationManager.Authenticate())
    .Result;

为什么CustomException 没有从回退操作中抛出?正确的做法应该是什么?

【问题讨论】:

  • 您是否对您的策略进行单元测试? "请注意,.ExecuteAndCapture/Async(...) 会捕获整个执行结果是否是被策略视为失败的结果。因此,如果您的 FallbackPolicy 将失败结果替换为将被视为失败的结果成功(正如许多后备策略所做的那样 - “优雅降级”模式),然后 .ExecuteAndCapture/Async(...) 自然会报告 PolicyResult.Outcome == OutcomeType.Success。” - FallbackPolicy with ExecuteAndCapture()
  • ^^这可能是个问题吗?
  • 也发现了这个问题:github.com/App-vNext/Polly/issues/477
  • 我只是将您的代码放入 xUnit-Test 并发现它“有点”工作:OutcomeFailureResultnullFinalException 是 @987654333 @.
  • ExecuteAndCapture 不会抛出异常。它捕获委托的结果,即使是Tx 或任何类型的ExceptionOutcome 会告诉你它是成功还是失败。如果失败,您可以通过FinalExceptionReference访问异常

标签: c# exception polly resiliency transient-failure


【解决方案1】:

这些单元测试通过:(xUnit)

[Fact]
public void Test1()
{
    var policy = Policy<string>
        .Handle<Exception>()
        .OrResult(result => string.IsNullOrWhiteSpace(result))
        .Fallback(() => throw new CustomException())
        .Wrap(
              Policy<string>
                 .Handle<Exception>()
                 .OrResult(result => string.IsNullOrWhiteSpace(result))
                 .WaitAndRetry(
                  10,
                  _ => TimeSpan.FromSeconds(1),
                  onRetry: (response, delay, retryCount, context) => Trace.WriteLine($"[{DateTime.UtcNow}] Authentication failed. Retrying after 1 second...(Attempt {retryCount} of 10)")
                  )
        );

    var result = policy.ExecuteAndCapture(() => throw new Exception("Muh"));
    Assert.IsType<CustomException>(result.FinalException);
    Assert.Null(result.Result);
    Assert.Equal(OutcomeType.Failure, result.Outcome);
}

[Fact]
public void Test2()
{
    var policy = Policy<string>
        .Handle<Exception>()
        .OrResult(result => string.IsNullOrWhiteSpace(result))
        .Fallback(() => throw new CustomException())
        .Wrap(
              Policy<string>
                 .Handle<Exception>()
                 .OrResult(result => string.IsNullOrWhiteSpace(result))
                 .WaitAndRetry(
                  10,
                  _ => TimeSpan.FromSeconds(1),
                  onRetry: (response, delay, retryCount, context) => Trace.WriteLine($"[{DateTime.UtcNow}] Authentication failed. Retrying after 1 second...(Attempt {retryCount} of 10)")
                  )
        );

    Assert.Throws<CustomException>(() => policy.Execute(() => throw new Exception("Muh")));
}

所以,我想,您可以使用Execute,或者检查结果并重新抛出异常,如下所示:

var result = policy.ExecuteAndCapture(() => AuthenticationManager.Authenticate());
if( result.Outcome == OutcomeType.Failure && result.FinalException is not null)
{
    throw result.FinalException;
}
return result.Result;

【讨论】:

    【解决方案2】:

    发现仅使用 WaitAndRetry 并仅检查 Outcome 对我有用(以防有人遇到此问题):

    var policyResult = Policy
        .Handle<Exception>()
        .OrResult<AuthenticationResult>(result => result is null)
            .WaitAndRetry(
                10,
                retryAttempt => TimeSpan.FromSeconds(60),
                onRetry: (response, delay, retryCount, context) => Trace.WriteLine($"[{DateTime.UtcNow}] Call failed. Retrying after 60 seconds...(Attempt {retryCount} of 10)"))
        .ExecuteAndCapture(() => AuthenticationManager.Authenticate());
    
    if (policyResult.Outcome == OutcomeType.Failure)
    {
        throw new CustomException("FAILED", policyResult.FinalException);
    }
    
    else
    {
        string value = policyResult.FinalHandledResult;
    }
    

    【讨论】:

      猜你喜欢
      • 2017-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-22
      • 2012-05-29
      • 1970-01-01
      • 1970-01-01
      • 2010-09-26
      相关资源
      最近更新 更多