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