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