0

我们正在将 SQL 迁移到 Azure。我们的 DAL 是基于 Entity Framework 4.x 的。我们希望使用瞬态故障处理块为 SQL Azure 添加重试逻辑。

总的来说,我们正在寻找最好的 80/20 规则(或者可能更多的是 95/5,但你明白了)——我们不打算花费数周重构/重写代码(有很多)。我很好地重新实现了我们的 DAL 框架,但不是所有针对它编写和生成的代码都不再是我们必须做的了,因为这已经在这里只是为了解决少数情况。缓解 >>> 为我们消除这种极端情况。

查看MSDN 此处解释的可能选项,似乎案例 #3有“最快”的实现方式,但只是乍一看。稍微考虑一下这个解决方案后,我突然想到我们可能在连接管理方面遇到问题,因为这个规避了实体框架用于管理连接的内置过程(即总是关闭它们)。在我看来,“解决方案”是确保我们实例化的 100% 的上下文使用使用块,但是对于我们的架构,这将是困难的。

所以我的问题是:使用该链接中的案例#3,挂起连接是一个问题,还是在某个地方发生了一些我不知道的魔法?

4

2 回答 2

1

我做了一些实验,结果证明这让我们回到了我们过去习惯的旧的“管理连接”情况,只是这一次连接从我们身上抽象了一点,我们现在必须“管理上下文”类似。

假设我们有以下OnContextCreated实现:

private void OnContextCreated()
{
    const int maxRetries = 4;
    const int initialDelayInMilliseconds = 100;
    const int maxDelayInMilliseconds = 5000;
    const int deltaBackoffInMilliseconds = initialDelayInMilliseconds;

    var policy = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>(maxRetries,
                                                                            TimeSpan.FromMilliseconds(initialDelayInMilliseconds),
                                                                            TimeSpan.FromMilliseconds(maxDelayInMilliseconds),
                                                                            TimeSpan.FromMilliseconds(deltaBackoffInMilliseconds));
    policy.ExecuteAction(() =>
            {
                try
                {
                    Connection.Open();
                    var storeConnection = (SqlConnection) ((EntityConnection) Connection).StoreConnection;
                    new SqlCommand("declare @i int", storeConnection).ExecuteNonQuery();
                    //Connection.Close();
                    // throw new ApplicationException("Test only");
                }
                catch (Exception e)
                {
                    Connection.Close();

                    Trace.TraceWarning("Attempted to open connection but failed: " + e.Message);

                    throw;
                }
            }
        );
}

在这种情况下,我们强制打开连接(这是这里的目标)。因此,上下文在许多调用中保持打开状态。因此,我们必须告诉 Context 何时关闭连接。我们这样做的主要机制是在 Context 上调用 Dispose 方法。因此,如果我们只允许垃圾收集来清理我们的上下文,那么我们就允许连接保持打开状态。

Connection.Close()我通过切换块中的注释try并针对我们的数据库运行一堆单元测试来测试这一点。在没有调用的情况Close下,我们跳到了 ~275-300 个活动连接(从 SQL Server 的角度来看)。通过调用Close,该数字徘徊在〜12。然后,我用少量的单元测试进行了复制,包括和不包括usingContext 块,并复制了相同的结果(不同的数字 - 我忘记了它们是什么)。

我正在使用以下查询来计算我的连接数:

SELECT s.session_id, s.login_name, e.connection_id,
      s.last_request_end_time, s.cpu_time, 
      e.connect_time
FROM sys.dm_exec_sessions AS s
INNER JOIN sys.dm_exec_connections AS e
ON s.session_id = e.session_id
WHERE login_name='myuser'
ORDER BY s.login_name

结论:如果您Connection.Open()使用此解决方法调用以启用瞬态故障处理块,那么您必须为您使用的所有using上下文使用块,否则您将遇到问题(使用 SQL Azure,将导致您的数据库被“限制”并最终离线数小时!)。

于 2013-01-18T14:26:48.340 回答
1

这种方法的问题是它只处理连接重试而不是命令重试。

如果您使用 Entity Framework 6(目前处于 alpha 阶段),那么有一些新的内置支持使用 Azure SQL 数据库(带有一点配置)进行瞬时重试:http ://entityframework.codeplex.com/wikipage?title=连接%20Resiliency%20Spec

我创建了一个库,它允许您配置 Entity Framework 以使用故障处理块重试,而无需更改每个数据库调用 - 通常您只需要更改配置文件和可能的一两行代码。

这允许您将它用于 Entity Framework 或 Linq To Sql。

https://github.com/robdmoore/ReliableDbProvider

于 2013-06-17T16:37:36.550 回答