朋友们,我有一个关于在执行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构造的外部?
答案 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,从而保持独立的重试逻辑。