Лучшие практики логики Transient Fault Retry - PullRequest
0 голосов
/ 02 июня 2018

Друзья, у меня есть вопрос о реализации простой политики повтора для выполнения команды SQL.

Мой вопрос: должен ли цикл повтора инкапсулировать конструкцию соединения и транзакции или он должен житьвнутри соединения.

Например:

private void RetryLogSave(DynamicParameters parameters, int retries = 3)
{    
    int tries = 0;

    using (var connection = new SqlConnection(_connectionString))
    {
        connection.Open();

        using (var transaction = connection.BeginTransaction())
        {
            var logItemCommand = new CommandDefinition(commandText: Constants.InsertLogItem,
                parameters: parameters, transaction: transaction, commandType: System.Data.CommandType.Text);

            do
            {
                try
                {
                    tries++;
                    connection.Execute(logItemCommand);
                    transaction.Commit();
                    break;
                }
                catch (Exception exc)
                {
                    if (tries == retries)
                    {
                        transaction.Rollback();
                        throw exc;
                    }
                    Task.Delay(100 * tries).Wait();
                }
            }
            while (true);
        }
}
}

Является ли то, что я здесь сделал, уместным и приемлемым, или логика повторения должна существовать вне конструкции SqlConnection?

1 Ответ

0 голосов
/ 02 июня 2018

Формализация моих комментариев в качестве ответа.

должна ли логика повторения существовать снаружи конструкции SqlConnection?

Да.Если вы выполняете логику повторных попыток с открытым соединением, вы тратите ресурсы впустую.Кто-то другой может использовать его, пока вы ждете N секунд для повторной попытки.Открытие / закрытие соединений обычно (для большинства драйверов ODBC) осуществляется поверх механизма пула соединений.Вы на самом деле не закрываете его - вы позволяете соединению вернуться в пул для повторного использования кем-либо еще.Сохранение соединений, открытых во время повторной попытки, заставит систему создавать все новые и новые физические соединения (поскольку они не возвращаются в пул), и в конечном итоге ваш SQL Server будет исчерпан.

Что касается механизма повторной попытки - чтобы не изобретать велосипед, я обычно использую библиотеку Polly .

Вы можете определить где-нибудь статический класс со списком ваших политик:

public static class MyPolices
{
    // Retry, waiting a specified duration between each retry
    public static Policy RetryPolicy = Policy
       .Handle<Exception>() // can be more specific like SqlException
       .WaitAndRetry(new[]
       {
          TimeSpan.FromSeconds(1),
          TimeSpan.FromSeconds(2),
          TimeSpan.FromSeconds(3)
       });
}

Затем упростите свой метод до

private void LogSave(DynamicParameters parameters)
{    
    using (var connection = new SqlConnection(_connectionString))
    {
        connection.Open();

        using (var transaction = connection.BeginTransaction())
        {
            // make sure to not forget to dispose your command
            var logItemCommand = new CommandDefinition(commandText: Constants.InsertLogItem,
                parameters: parameters, transaction: transaction, commandType: System.Data.CommandType.Text);

            try
            {
                // not sure if conn.Execute is your extension method?
                connection.Execute(logItemCommand);
                transaction.Commit();
            }
            catch (Exception exc)
            {
                transaction.Rollback();
                throw;
            }
        }
    }
}

и назовите его следующим образом

MyPolices.RetryPolicy.Execute(() => LogSave(parameters));

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

...