瞬态故障重试逻辑最佳实践

时间:2018-06-02 12:48:07

标签: c# transient-failure

朋友们,我有一个关于在执行SQL命令时实现简单重试策略的问题。

我的问题是:重试循环是否应该封装连接和事务的构造,还是应该存在于连接内部。

例如:

  $sql = $this->db->query("SELECT * FROM country
  LEFT JOIN competition ON country_id = competition.country_id
  LEFT JOIN competition_seasons ON competition_id = competition.id
  LEFT JOIN competition_rounds ON competition_rounds.season_id = competition_seasons.id
  LEFT JOIN `match` ON match.round_id = competition_rounds.id
  WHERE match.datetime = " . $args["date"] . "");

我在这里所做的是否恰当和可接受,或者重试逻辑是否存在于SqlConnection构造的外部?

1 个答案:

答案 0 :(得分:3)

将我的评论正式化为答案。

  

重试逻辑应该存在于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));

这种方法将使您的代码更加SOLID,从而保持独立的重试逻辑。