【问题标题】:why my polly timeout policy seems not firing为什么我的 polly 超时政策似乎没有触发
【发布时间】:2022-09-29 23:52:47
【问题描述】:

这是我的 Polly 实现,它有两个策略,一个超时和一个重试。这个想法是当sql超时时,超时跨度会变长,所以sql server有更多的时间来做这项工作。

但是,当使用需要几分钟才能完成模拟超时的 sp 时,我没有看到超时策略触发了 3 次(附加调试器或仅搜索输出日志)。它触发一次,然后TimeoutRejectedException 将被抛出。

var timeoutPerTry = Policy
    .TimeoutAsync(context =>
    {
        ////enlarge timeout every time it happens

        taskTimeoutInSeconds = (int)(timeoutMs / 1000);

        Log.LogVerbose(
            $\"log something\");
        return TimeSpan.FromMilliseconds(timeoutMs);
    }, TimeoutStrategy.Optimistic);

// retry SqlException up to MaxRetries
var retryPolicy = Policy
    .Handle<SqlException>()
    .RetryAsync(Constants.MaxRetries,
        (response, calculatedWaitDuration, context) =>
        {
            Log.LogError(
                $\"Failed dynamic execution attempt. Retrying. {response.Message} - {response.StackTrace}\");
        });

try
{
    ////combine timeout policy and retry policy
    var combinedPolicy = retryPolicy.WrapAsync(timeoutPerTry);
    // ReSharper disable once AccessToDisposedClosure
    var results =
        await combinedPolicy.ExecuteAsync<IEnumerable<T>>(async () => {

            var connectionString = ConnectionStringHelper.GetConnectionString(warehouseId);
            using (var connection = new SqlConnection(connectionString))  // assumed no need for using block as closed by caller
            {
                await connection.OpenAsync();
                using (var cmd = new SqlCommand
                {
                    CommandType = commandType,
                    CommandTimeout = taskTimeoutInSeconds, // in secs
                    CommandText = \"JerrySimulateSlowSp\"
                })
                {
                    cmd.Parameters.AddRange(parameters.ToArray());
                    cmd.Connection = connection;

                    using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
                    {
                        return mapper.Map<IDataReader, IEnumerable<T>>(reader);
                    }
                }
            }
        });
    return results;
    //cmd.Connection = null;        
}
catch (SqlException ex) when (ex.Number == -2)  // -2 is a sql timeout
{
    throw new ThunderTimeoutException(Constants.HttpResponseTimeoutSql);
}
catch (TimeoutRejectedException)
{
    throw new ThunderTimeoutException(Constants.HttpResponseTimeoutTask);
}

    标签: c# .net ado.net timeout polly


    【解决方案1】:

    Polly 的超时策略支持两种类型的操作:

    • Optimistic:装饰方法可以与CancellationToken合作
    • Pessimistic: 装饰方法不能与 CancellationToken 合作

    幸运的是ExecuteReaderAsyncdoes support CancellationToken,所以我们可以在这里使用乐观超时策略。诀窍是你让你使用不同的重载ExecuteAsync

    .ExecuteAsync(async ct => 
    {
       ...
       var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection, ct);
       ...
    }, CancellationToken.None); 
    

    在这种情况下,ExecuteReaderAsync 将使用超时的CancellationToken。如果您有另一个 CancellationToken(例如允许基于用户交互的取消),那么您可以通过传递该令牌而不是 CancellationToken.None 将其与超时的一个结合起来

    .ExecuteAsync(async combinedToken => 
    {
       ...
       var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection, combinedToken);
       ...
    }, userCancellationToken); 
    

    旁注:请更喜欢PolicyWrap 而不是WrapAsync

    var combinedPolicy = PolicyWrap.WrapAsync(retryPolicy, timeoutPerTry);
    

    相关 SO 主题:123

    【讨论】:

    • 你好,对不起。我真的没看懂你的回复。您的方法肯定比我们的方法好得多,但我仍然不确定为什么没有触发超时
    • @daxu 这就是以下内容。将这一行:await combinedPolicy.ExecuteAsync<IEnumerable<T>>(async () => { 更改为:await combinedPolicy.ExecuteAsync<IEnumerable<T>>(async ct => {
    • 并在右括号更改此:});对此:}, CancellationToken.None);
    • @daxu 我的帖子的哪一部分需要进一步解释?
    • 认为我需要重新阅读文档并使用代码
    猜你喜欢
    • 2017-09-11
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多