SqlBulkCopy.WriteToServer 不可靠地遵守 BulkCopyTimeout

发布于 2024-08-20 09:19:16 字数 1409 浏览 3 评论 0原文

我需要计算 SqlBulkCopy 的连续超时异常。为了测试这一点,我使用外部应用程序来启动交易并进行交易。锁定目标表。

仅在第一次调用时,SqlBulkCopy 才会在预期时抛出超时异常。我们尝试过使用外部连接&事务,以及使用连接字符串和内部事务。具有外部连接&事务中,无限等待永远不会发生在打开连接或开始或提交事务时,而是始终在 .WriteToServer() 处。

是否有某种方法可以使 SqlBulkCopy.WriteToServer() 在达到其 .BulkCopyTimeout 限制时可靠地抛出超时异常?

public void BulkCopy(string connectionString, DataTable table, int bulkTimeout)
{
    using (SqlBulkCopy bulkCopy = new SqlBulkCopy(
        connectionString, 
        SqlBulkCopyOptions.UseInternalTransaction))
    {
    bulkCopy.BulkCopyTimeout = bulkTimeout;//e.g. 120 sec.
    //... fill with data, map columns...
    bulkCopy.WriteToServer(table);
    // ^^^^ waits indefinitely, doesn't throw until *after*
    //      the lock is released.
    }
}

我更喜欢让异常冒泡,而不是在 using 块的范围内处理它们,但我总是可以重新抛出。非常感谢您的任何见解。

更新1:

仍然没有解决方案。但发现了有趣的行为——正常的 SqlCommand 在同一锁定期间将按预期抛出 TimeoutException,这会导致 SqlBulkCopy.WriteToServer 方法无限期挂起。

以下是我们尝试过的方法(但都失败了),以使 SqlBulkCopy.WriteToServer 在预期时始终抛出超时:

  • MARS(多个活动结果集)开/关
  • TableLock 开与关
  • 目标作为堆表与索引表
  • 更长/更短的 BulkTimeout 值(10 秒到 5 分钟)
  • 内部事务与外部事务

目前,作为一种解决方法,我交替使用 a) 将 WriteToServer 调用放入异步包装器中,以便我可以自己计时,b) 仅调用 WriteToServer 一次;超时后,等待常规 SqlCommand成功,然后再次尝试 WriteToServer。使用这些方法,我至少能够保持对执行流程的控制。

I need to count sequential timeout exceptions from SqlBulkCopy. To test this, I use an external app to start a transaction & lock up the target table.

Only on the first call does SqlBulkCopy throw a timeout exception when expected. We've tried using an external connection & transaction, as well as using a connection string and internal transaction. With the external connection & transaction, the infinite wait was never in opening the connection or beginning or committing the transaction, but always at .WriteToServer().

Is there some approach to this whereby SqlBulkCopy.WriteToServer() will reliably throw a timeout exception when it has reached its .BulkCopyTimeout limit?

public void BulkCopy(string connectionString, DataTable table, int bulkTimeout)
{
    using (SqlBulkCopy bulkCopy = new SqlBulkCopy(
        connectionString, 
        SqlBulkCopyOptions.UseInternalTransaction))
    {
    bulkCopy.BulkCopyTimeout = bulkTimeout;//e.g. 120 sec.
    //... fill with data, map columns...
    bulkCopy.WriteToServer(table);
    // ^^^^ waits indefinitely, doesn't throw until *after*
    //      the lock is released.
    }
}

I prefer to let exceptions bubble up rather than handle them in the scope of the using block, but I can always rethrow. Thanks much for any insight.

Update 1:

Still no resolution. Interesting behavior discovered though -- a normal SqlCommand will throw a TimeoutException as expected during the same lock that makes the SqlBulkCopy.WriteToServer method hang indefinitely.

Here are approaches that we've tried -- and that have failed -- to get SqlBulkCopy.WriteToServer to consistently throw timeouts when expected:

  • MARS (Multiple Active Result Sets) on/off
  • TableLock on vs. off
  • Destination as heap table vs. indexed table
  • Longer/shorter BulkTimeout values (10 seconds to 5 minutes)
  • Internal vs external transaction

For now, as a workaround, I'm alternating between a) putting the WriteToServer call in an asynchronous wrapper so I can time it myself, and b) only calling WriteToServer once; after timeouts, wait until a regular SqlCommand succeeds before trying WriteToServer again. Using these approaches, I'm at least able to stay in control of the execution flow.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

霊感 2024-08-27 09:19:16

您是否尝试过将 SqlBulkOptions.TableLock 选项传递给 SqlBulkCopy?该选项(引号)意味着它将:

获取批量更新锁
批量复制操作的持续时间。

因此,如果有另一个进程锁定表,它将阻止获得锁定,并且理论上会可靠地超时。

更新:
我设置了自己的测试工具,但无法重现。为了锁定表,我在 SSMS 中启动了一个事务,执行 SELECT * FROM TargetTable WITH (HOLDLOCK)。我使用了问题中包含的相同 BulkCopy 方法,使用内部事务,批量加载超时为 30 秒。每次尝试进行批量复制都会在 30 秒后按预期超时。当我回滚 SSMS 事务时,它会成功。

我使用的是 SQL Server 2008 Express、.NET 3.5。

这不是像第一次尝试后,批量加载超时没有正确传入吗?即它没有以某种方式设置为“无限期”。

更新2:
还在连接字符串中打开了多个活动结果集支持,但每次仍然持续超时。

Have you tried passing in the SqlBulkOptions.TableLock option to SqlBulkCopy? That option (quote) means it will:

Obtain a bulk update lock for the
duration of the bulk copy operation.

So, if there is another processing locking the table, it would prevent the lock being gained and in theory, reliably timeout.

Update:
I set up my own test harness and can't reproduce. To lock the table, I started a transaction in SSMS doing a SELECT * FROM TargetTable WITH (HOLDLOCK). I used the same BulkCopy method you included in the question, using internal transactions, with a bulk load timeout of 30 seconds. Each attempt to do the bulk copy times out as expected after 30 seconds. It then succeeds when I rollback the SSMS transaction.

I was using SQL Server 2008 Express, .NET 3.5.

It's not something like after the first attempt, the bulk load timeout is not being passed in correctly? i.e. it's not somehow being set to "indefinite".

Update 2:
Also switched on Multiple Active Result Sets support in the connection string, still consistently times out for me each time.

墨小墨 2024-08-27 09:19:16

我后来遇到了这个问题,为了解决这个问题,将 BulkCopyTimeout 设置为零。

bulkCopy.BulkCopyTimeout = 0;

I had this problem latter and for resolving this problem set BulkCopyTimeout to zero.

bulkCopy.BulkCopyTimeout = 0;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文