为什么我的polly超时策略似乎没有触发



这是我的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);
}

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

  • 乐观:装饰方法可以与CancellationToken
  • 合作。
  • 悲观:装饰方法不能与CancellationToken合作

幸运的是,ExecuteReaderAsync支持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主题:1,2,3

相关内容

  • 没有找到相关文章

最新更新