Возврат общего типа политики

Я хочу повторить попытку, когда ошибка 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);
Другие вопросы по тегам