Прервать политику ожидания AndRetryAsync?

Я хотел бы использовать 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 ответ

Решение

Этот первый пример ниже ограничивает общее время ожидания между повторными попытками до общего промежутка времени. myWaitLimit, но не учитывает, сколько времени тратят звонки на CosmosDB перед возвратом DocumentClientException, Потому что Полли 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 вокруг с помощью Полли Context как описано в этом сообщении в блоге.


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

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


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

Другие вопросы по тегам