Я открыл дело в службе технической поддержки Microsoft, и после немалого количества взад-вперед Pradeep M.M. (Технический руководитель службы поддержки SQL Server) с этим справился.
Общий процесс: чтение списка файлов в папке, и один за другим выполнить серию массовых вставок в эти файлы (сначала прочитать первую строку, которую мы анализируем по столбцам, а затем прочитать данные из вторая + строчки). Все массовые вставки используют опцию «ErrorFile», чтобы предоставить пользователям какую информацию мы можем, когда их данные неверно отформатированы. Процесс работал более 3 лет, но в последних условиях стресс-тестирования (до 8 одновременных запусков, выполняемых одним экземпляром SQL Server, со всеми форматированными файлами), мы получили ошибки, перечисленные выше.
Изначально, хотя были ошибки при генерации GUID, из-за этой «уже открытой» ошибки, но эта идея была в конечном итоге отброшена - если newid () не функционировал должным образом, гораздо больше людей имели бы гораздо больше серьезные проблемы.
В соответствии с Pradeep, вот пошаговый процесс работы Bulk Insert:
- BULK INSERT Команда отправлена и проанализирована на наличие синтаксических ошибок
- Затем команда BULK INSERT компилируется для генерации выполнения.
План для того же
- На этапе компиляции, если в запросе, если мы указали
ERRORFILE параметр, то мы создадим ErrorFile.log и
ErrorFile.Error.Txt в указанную папку (важно
что нужно понять, так это файл размером 0 КБ)
- Как только создание файла завершено, мы удаляем оба файла, используя
Вызовы API Windows
- Как только план выполнения будет готов, мы перейдем к этапу выполнения.
и попробуйте выполнить команду Bulk Insert, как часть этого мы будем
заново создайте ErrorFile.log и ErrorFile.Error.Txt в папке
указанное местоположение (согласно документации по книгам в Интернете ошибка
файлы не должны быть там в этом месте, иначе мы не сможем
исполнение http://msdn.microsoft.com/en-us/library/ms188365.aspx
- После завершения выполнения, если в
Массовая вставка соответствующих ошибок регистрируется в файлах ошибок
создано, если нет ошибок, эти 2 файла будут удалены.
Запуск ProcMon (Process Monitor) во время неудачных запусков показал, что ErrorFile был успешно создан и открыт на шаге 3, но НЕ был закрыт на шаге 4, в результате чего на шаге 5 возникла ошибка, которую мы видели. (Для успешных запусков файл был создан и закрыт, как и ожидалось.)
Дальнейший анализ ProcMon показал, что после процесса массовой вставки другой процесс, выполняющий CMD.EXE, выдавал над файлом операции «закрывать дескриптор». Мы используем подпрограмму, включающую xp_cmdshell, для получения списка файлов, которые должны быть обработаны, и это будет причиной процесса CMD.EXE. Вот кикер:
… существует некоторая бизнес-логика, которая запускает CMD.EXE внутри SQL Server, и, поскольку CMD.EXE является дочерним процессом, он наследует все дескрипторы, открытые родительским процессом (так что, вероятно, это какая-то проблема с синхронизацией в CMD. EXE содержит дескрипторы файлов, которые открываются при запуске, и все эти файлы, дескриптор которых наследуется CMD.EXE, не могут быть удалены и могут быть освобождены только после уничтожения CMD.EXE)
И это было все. Один запуск никогда не решает эту проблему, так как его вызов xp_cmdshell завершается до выпуска массовых вставок. Но при параллельных запусках, особенно при многих параллельных запусках (я столкнулся с проблемой только при 5 или более ходах), возникла проблема с синхронизацией:
- Один из пакетов служб SSIS выполняет и вызывает хранимую процедуру
который внутренне использует XP_CMDSHELL и запускает CMD.EXE для перечисления
Файлы
- То же соединение с сервером SQL завершает перечисление файлов
а затем запускает групповую операцию вставки
фаза для команды BULK INSERT
- В соответствии с дизайном Bulk Insert мы создаем ErrorFile
во время фазы компиляцииSE, а затем удалить его после компиляции
фаза выполнена
- В то же время запускается другой пакет служб SSIS и он вызывает
хранимая процедура, которая внутренне использует XP_CMDSHELL и запускает
CMD.EXE для перечисления всех файлов
- CMD.EXE - это дочерний процесс, запущенный под родительским
Обработайте SQLServr.exe, поэтому по умолчанию он наследует все дескрипторы.
созданный SQLServr.exe (так что этот процесс получает все дескрипторы для
ОШИБКА, созданная BULK INSERT в первом
Connection)
- Теперь в первом соединении фаза компиляции завершена и
следовательно мы пытаемся удалить файл, в течение которого мы должны закрыть
все ручки, мы видим, что CMD.EXE держит дескриптор
файл, и он все еще открыт, и, следовательно, мы не можем удалить файл. Так
без удаления файла мы переходим к этапу выполнения и в
На этапе выполнения мы пытаемся создать новый ОШИБКА с
то же имя, но так как файл уже существует, мы терпим неудачу с ошибкой
«Код ошибки операционной системы 80 (файл существует.).»
Мой краткосрочный обходной путь состоял в том, чтобы (1) реализовать цикл повторных попыток, сгенерировать новое имя ErrorFile и попробовать новую массовую вставку до трех раз, прежде чем отказаться, и (2) создать другую подпрограмму на наших ночных процессах для удаления все файлы, найденные в нашей папке «ErrorFile».
Долгосрочное исправление состоит в том, чтобы пересмотреть наш код, чтобы не выводить список файлов через xp_cmdshell. Это может показаться возможным, поскольку весь процесс ETL обернут и управляется пакетом служб SSIS; в качестве альтернативы, подпрограммы CLR могут быть встроены и проработаны. На данный момент, учитывая нашу ожидаемую рабочую нагрузку, обходной путь является достаточным (особенно учитывая все остальное, над чем мы сейчас работаем), поэтому может потребоваться немного времени, прежде чем мы реализуем финальную версию. исправить.
Добавлено для потомков, на случай, если это когда-нибудь случится с вами!