Возврат общего типа политики
Я хочу повторить попытку, когда ошибка NEST находится в диапазоне HttpCodeResponse, и у меня есть следующая общая политика:
public Policy<D> CreatePolicy<T, D>(
PolicyType policyType)
where T : Exception where D : IApiCallDetails
{
switch (policyType)
{
case PolicyType.WaitAndRetryAsync:
var httpStatusCodesWorthRetrying = new List<string>(this.policyConfiguration.HttpStatusCodesToRetrying.Split(','));
return Policy.Handle<T>()
.OrResult<D>(r => httpStatusCodesWorthRetrying.Select(int.Parse).ToList().Contains(r.HttpStatusCode.Value))
.WaitAndRetryAsync(
this.policyConfiguration.NumberOfRequestRetries,
retryAttempt => TimeSpan.FromSeconds(this.policyConfiguration.TimeInSecondsBetweenRetries),
onRetry: (exception, retryCount, context) =>
{
Log.Error($"[{context.PolicyKey}] Retry {retryCount} due to {exception.Exception.Message}.");
throw exception.Exception;
})
.WithPolicyKey(nameof(PolicyType.WaitAndRetryAsync));
default:
throw new ArgumentOutOfRangeException(nameof(policyType), policyType, null);
}
Но когда я пытаюсь применить к вызову asticClient, я получаю сообщение об ошибке:
cannot implicity convert System.Threading.Tasks.Task<Nest.ISearchResponse<Model.Product>> to System.Threading.Tasks.Task<Elasticsearch.Net.IApiCallDetails>
Policy<IApiCallDetails> policyWaintAndRetry = this.policyFactory.CreatePolicy<ElasticsearchClientException, IApiCallDetails>(PolicyType.WaitAndRetryAsync);
var searchResponse = await policyWaintAndRetry.ExecuteAsync
action: () =>
this.client.SearchAsync<Product>(s => s
.From((request.PageNumber - 1) * request.PageSize)
.Size(request.PageSize)
.Index(GetIndexName(request.TenantId))
.Query(q => tq), CancellationToken.None),
contextData: new Polly.Context("SearchProductSearchAsync"))
.ConfigureAwait(false);
3 ответа
Нет необходимости определять две отдельные политики для обработки исключений и результатов. Две отдельные политики в этом ответе могут быть объединены следующим образом:
public Policy<TResult> CreatePolicyForResultAndException<TResult, TException>(PolicyType policyType)
where TResult : HttpResponseMessage
where TException: Exception
{
switch (policyType)
{
case PolicyType.WaitAndRetryAsync:
var httpStatusCodesWorthRetrying = new List<string>(this.policyConfiguration.HttpStatusCodesToRetrying.Split(','));
return Policy.HandleResult<TResult>(r => httpStatusCodesWorthRetrying.Select(int.Parse).ToList().Contains((int)r.StatusCode))
.Or<TException>()
.WaitAndRetryAsync(
this.policyConfiguration.NumberOfRequestRetries,
retryAttempt => TimeSpan.FromSeconds(this.policyConfiguration.TimeInSecondsBetweenRetries),
onRetry: (outcome, retryCount, context) =>
{
Log.Error($"[{context.PolicyKey}] Retry {retryCount} due to {outcome.Result ?? outcome.Exception.Message}.");
if (outcome.Exception != null) throw outcome.Exception; // [*] if desired - see note after code sample
})
.WithPolicyKey(nameof(PolicyType.WaitAndRetryAsync));
default:
throw new ArgumentOutOfRangeException(nameof(policyType), policyType, null);
}
}
[*] Эта строка в приведенном выше примере кода сохраняет исключение внутри onRetry
из оригинального ответа. Тем не менее, было бы необычно сбросить исключение в рамках onRetry
, поскольку политика не будет обрабатывать исключение, переброшенное туда; бросать в onRetry
приведет к выходу политики без дальнейших попыток.
Я думаю, что для NEST 5.x D
должен иметь ограничение общего параметра IResponse
; каждый ответ в NEST реализует IResponse
и ApiCall
собственность, унаследованная от IBodyWithApiCallDetails
содержит IApiCallDetails
с кодом статуса HTTP.
На самом деле моя проблема заключалась в том, что я пытался создать единую политику для обработки исключений и результатов, в то время как на самом деле я должен создать одну для каждого, а затем объединить их с policyWrap.
1-й полис (для результата):
public Policy<T> CreatePolicyForResult<T>(
PolicyType policyType)
where T : HttpResponseMessage
{
switch (policyType)
{
case PolicyType.WaitAndRetryAsync:
var httpStatusCodesWorthRetrying = new List<string>(this.policyConfiguration.HttpStatusCodesToRetrying.Split(','));
return Policy.HandleResult<T>(r => httpStatusCodesWorthRetrying.Select(int.Parse).ToList().Contains((int)r.StatusCode))
.WaitAndRetryAsync(
this.policyConfiguration.NumberOfRequestRetries,
retryAttempt => TimeSpan.FromSeconds(this.policyConfiguration.TimeInSecondsBetweenRetries))
.WithPolicyKey(nameof(PolicyType.WaitAndRetryAsync));
default:
throw new ArgumentOutOfRangeException(nameof(policyType), policyType, null);
}
}
2-й полис (для исключения):
public Policy CreatePolicyForException<T>(
PolicyType policyType)
where T : Exception
{
switch (policyType)
{
case PolicyType.WaitAndRetryAsync:
return Policy.Handle<T>()
.WaitAndRetryAsync(
this.policyConfiguration.NumberOfRequestRetries,
retryAttempt => TimeSpan.FromSeconds(this.policyConfiguration.TimeInSecondsBetweenRetries),
onRetry: (exception, retryCount, context) =>
{
Log.Error($"[{context.PolicyKey}] Retry {retryCount} due to {exception.Message}.");
throw exception;
})
.WithPolicyKey(nameof(PolicyType.WaitAndRetryAsync));
default:
throw new ArgumentOutOfRangeException(nameof(policyType), policyType, null);
}
}
Использование:
var policyWaintAndRetryForExeption = this.policyFactory.CreatePolicyForException<ElasticsearchClientException>(PolicyType.WaitAndRetryAsync);
var policyWaintAndRetryForResult = this.policyFactory.CreatePolicyForResult<HttpResponseMessage>(PolicyType.WaitAndRetryAsync);
PolicyWrap<HttpResponseMessage> policyWrap = policyWaintAndRetryForExeption.WrapAsync(policyWaintAndRetryForResult);