Для некоторой части моей системы мне нужно добавить логику повторных попыток для чтения из базы данных.У меня есть несколько репозиториев с асинхронным и синхронизированным методами чтения, которые я не могу изменить.Я нашел простое решение - перехват всех методов чтения с помощью AsyncInterceptor и добавление политики повторного чтения с помощью Polly при обнаружении исключения базы данных.Polly повторяет чтение с некоторыми интервалами.
Код перехватчика:
public class RetriableReadAsyncInterceptor : IAsyncInterceptor
{
public void InterceptSynchronous(IInvocation invocation)
{
invocation.ReturnValue = InternalInterceptSync(invocation);
}
public void InterceptAsynchronous(IInvocation invocation)
{
throw new NotImplementedException();
}
public void InterceptAsynchronous<TResult>(IInvocation invocation)
{
invocation.ReturnValue = InternalInterceptAsync<TResult>(invocation);
}
private IEnumerable<TimeSpan> RetryIntervals =>
new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(15)
};
private object InternalInterceptSync(IInvocation invocation)
{
return Policy
.Handle<DatabaseException>()
.WaitAndRetry(RetryIntervals, (exception, timeSpan) =>
{
Console.WriteLine($"Exception {timeSpan}");
})
.Execute(() =>
{
invocation.Proceed();
return invocation.ReturnValue;
});
}
private async Task<TResult> InternalInterceptAsync<TResult>(IInvocation invocation)
{
return await Policy
.Handle<DatabaseException>()
.WaitAndRetryAsync(RetryIntervals, (exception, timeSpan) =>
{
Console.WriteLine($"Exception {timeSpan}");
})
.ExecuteAsync(async () =>
{
invocation.Proceed();
var task = (Task<TResult>)invocation.ReturnValue;
return await task;
});
}
}
Код репозитория:
public class Repository : IRepository
{
private int _exceptionsCoutner;
public Entity GetById(int id)
{
if (_exceptionsCoutner <= 2)
{
_exceptionsCoutner++;
throw new DatabaseException();
}
//read from db
return new Entity {Id = id};
}
public async Task<Entity> GetByIdAsync(int id)
{
if (_exceptionsCoutner <= 2)
{
_exceptionsCoutner++;
throw new DatabaseException();
}
//read from db
return await Task.FromResult(new Entity { Id = id });
}
}
Синхронизированная версия GetById работает должным образом (повторяется с интервалами):
Exception 00:00:01
Exception 00:00:05
Exception 00:00:10
Асинхронная версия GetById повторяет попытку, но не ожидает истечения временного интервала:
Exception 00:00:01
Exception 00:00:01
Exception 00:00:01
Я не могу понять, в чем проблема.Если есть мысли - поделитесь пожалуйста.Полный пример можно найти здесь .