Отменить политику WaitAndRetryAsync? - PullRequest
0 голосов
/ 28 июня 2018

Я хотел бы использовать WaitAndRetryAsync, чтобы помочь повторить ошибки http 429 (регулирование). Задержка повтора возвращается как свойство самого исключения. Но мне нужно добавить накопленное время и отказаться от цикла повторных попыток, если общая продолжительность превышает определенную величину.

policy = Policy.Handle<DocumentClientException>(ex => ex.StatusCode == (HttpStatusCode)429)
    .WaitAndRetryAsync(
        retryCount: retries,
        sleepDurationProvider: (retryCount, exception, context) => {
            DocumentClientException dce = exception as DocumentClientException;

            // Here I would like to check the total time and NOT return a RetryAfter value if my overall time is exceeded. Instead re-throw the 'exception'.

            return dce.RetryAfter;
    },
        onRetryAsync: async (res, timespan, retryCount, context) => {
    });

Когда общее время будет превышено, я хотел бы повторно выдать «исключение», обработанное в sleepDurationProvider.

Есть ли лучший способ справиться с этим?

Спасибо

-John

1 Ответ

0 голосов
/ 29 июня 2018

Этот первый пример ниже ограничивает общее время ожидания между повторными попытками общим временем myWaitLimit, но не учитывает, сколько времени тратят вызовы на CosmosDB перед возвратом DocumentClientException. Поскольку Polly Context ограничена выполнением, это потокобезопасно. Что-то вроде:

policy = Policy.Handle<DocumentClientException>(ex => ex.StatusCode == (HttpStatusCode)429)
.WaitAndRetryAsync(
    retryCount: retries,
    sleepDurationProvider: (retryCount, exception, context) => {
        DocumentClientException dce = exception as DocumentClientException;

        TimeSpan toWait = dce.RetryAfter;
        TimeSpan waitedSoFar;
        if (!Context.TryGetValue("WaitedSoFar", out waitedSoFar)) waitedSoFar = TimeSpan.Zero; // (probably some extra casting actually needed between object and TimeSpan, but this kind of idea ...)
        waitedSoFar = waitedSoFar + toWait;

        if (waitedSoFar > myWaitLimit)
            throw dce; // or use ExceptionDispatchInfo to preserve stack trace

        Context["WaitedSoFar"] = waitedSoFar; // (magic string "WaitedSoFar" only for readability; of course you can factor this out)
        return toWait;
    },
    onRetryAsync: async (res, timespan, retryCount, context) => {
});

Альтернативный подход может ограничить общее время выполнения (когда происходит 429 с) с использованием тайм-аута CancellationToken. Приведенный ниже подход не будет повторяться дальше после того, как CancellationToken был сигнализирован. Этот подход смоделирован так, чтобы быть близким к функциональности, запрошенной в вопросе, но время ожидания явно вступает в силу только в том случае, если возвращается ответ 429 и вызывается делегат sleepDurationProvider.

CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(/* my timeout */);

var policy = Policy.Handle<DocumentClientException>(ex => ex.StatusCode == (HttpStatusCode)429)
.WaitAndRetryAsync(
    retryCount: retries,
    sleepDurationProvider: (retryCount, exception, context) => {
        if (cts.IsCancellationRequested) throw exception; // or use ExceptionDispatchInfo to preserve stack trace

        DocumentClientException dce = exception as DocumentClientException;
        return dce.RetryAfter;
    },
    onRetryAsync: async (res, timespan, retryCount, context) => {
});

Если вы не хотите определять policy в той же области, в которой используете его, и закрывать переменную cts (как в приведенном выше примере), вы можете передать CancellationTokenSource, используя Polly Context как описано в этом блоге .


В качестве альтернативы Полли предоставляет TimeoutPolicy. Используя PolicyWrap, вы можете обернуть это вне политики повторных попыток. Тайм-аут может быть наложен на общее выполнение независимо от того, происходит 429 или нет.

Если стратегия предназначена для управления асинхронными вызовами Cosmos DB, которые по своей природе не принимают CancellationToken, вам нужно будет использовать TimeoutStrategy.Pessimistic, если вы хотите принудительно установить тайм-аут в этот промежуток времени. Тем не менее, обратите внимание на вики, как работает TimeoutStrategy.Pessimistic: он позволяет вызывающему потоку уйти от не подлежащего отмене вызова, но не отменяет в одностороннем порядке не подлежащий отмене вызов. Этот вызов может либо позднее вызвать ошибку, либо продолжить выполнение.


Очевидно, рассмотрите, что лучше из перечисленных выше вариантов, в соответствии с вашим контекстом.

...