Почему не работает EF Core retry при сбое - PullRequest
0 голосов
/ 14 июля 2020

Я настроил свой контекст db для повторной попытки при сбое следующим образом:

optionsBuilder.UseSqlServer(connectionString, sqlServerOptionsAction: sqlOptions => {
                sqlOptions.EnableRetryOnFailure(
                maxRetryCount: 10,
                maxRetryDelay: TimeSpan.FromSeconds(30),
                errorNumbersToAdd: null);
            });

Также я настроил следующий перехватчик:

public class DbCommandFailureInterceptor : DbCommandInterceptor
    {
        private int retryCount = 2;

        public override async Task<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
        {
            Throw(command);
            return result;
        }

        public override async Task<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
        {
            Throw(command);
            return result;
        }

        private void Throw(DbCommand command)
        {
            if (!(command.CommandText.Contains("serverproperty")
        || command.CommandText.Contains("_MigrationHistory"))
        && retryCount > 0) {
                --retryCount;
                throw SqlExceptionFaker.Error10053;
            }
        }
    }

Так что в основном я ожидаю, когда сущности из базы данных, чтобы поразить метод Throw 3 раза, 2 раза с исключением исключения и последний раз без его выброса. Но на самом деле повторная попытка при сбое не выполняется, а выполнение первого исключения останавливается. Что я делаю не так?

У меня есть такой помощник для отказоустойчивых транзакций:

public class ResilientTransaction
    {
        private readonly DbContext _context;
        private ResilientTransaction(DbContext context) =>
            _context = context ?? throw new ArgumentNullException(nameof(context));

        public async Task ExecuteAsync(Func<Task> action)
        {
            var strategy = _context.Database.CreateExecutionStrategy();
            await strategy.ExecuteAsync(async () => {
                using (var transaction = await _context.Database.BeginTransactionAsync()) {
                    await action();
                    await transaction.CommitAsync();
                }
            });
        }

        public static ResilientTransaction New(DbContext context) =>
            new ResilientTransaction(context);
    }

А вот как я его выполняю:

await ResilientTransaction.New(_dbContext).ExecuteAsync(async () => {
                    _dbContext.Tenants.Add(new Tenant());
                    await _dbContext.SaveChangesAsync();
                });
...