SqlBulkCopy.WriteToServer не надежно подчиняется BulkCopyTimeout - PullRequest
8 голосов
/ 05 февраля 2010

Мне нужно сосчитать исключения из последовательного тайм-аута из 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 минут)
  • Внутренняя и внешняя транзакция

На данный момент, в качестве обходного пути, я чередую между: а) помещением вызова WriteToServer в асинхронную оболочку, чтобы я мог сам рассчитать время, и б) только вызовом WriteToServer только один раз; после истечения времени ожидания дождитесь, пока обычная команда SqlCommand завершится успешно , прежде чем снова пытаться выполнить WriteToServer. Используя эти подходы, я по крайней мере могу контролировать поток выполнения.

Ответы [ 2 ]

4 голосов
/ 05 февраля 2010

Вы пытались передать SqlBulkCopy параметр SqlBulkOptions.TableLock? Этот параметр (цитата) означает, что он будет:

Получение блокировки массового обновления для продолжительность операции массового копирования.

Таким образом, если есть другая обработка, блокирующая таблицу, это предотвратит получение блокировки и теоретически надежно истечет.

Обновление:
Я установил свой собственный тестовый комплект и не могу воспроизвести. Чтобы заблокировать таблицу, я запустил транзакцию в SSMS, выполнив SELECT * FROM TargetTable WITH (HOLDLOCK). Я использовал тот же метод BulkCopy, который вы включили в вопрос, с использованием внутренних транзакций с тайм-аутом массовой загрузки 30 секунд. Каждая попытка выполнить массовое копирование завершается, как и ожидалось, через 30 секунд. Затем он завершается успешно, когда я выполняю откат транзакции SSMS.

Я использовал SQL Server 2008 Express, .NET 3.5.

Это не похоже на то, что после первой попытки тайм-аут массовой загрузки не был правильно передан? то есть это не так, как-то установлено "неопределенным".

Обновление 2:
Также включена поддержка нескольких активных наборов результатов в строке подключения, но каждый раз для меня постоянно истекает время ожидания.

1 голос
/ 07 февраля 2010

У меня была эта проблема позже, и для решения этой проблемы установите BulkCopyTimeout в ноль.

bulkCopy.BulkCopyTimeout = 0;
...