#sql-server #liquibase
#sql-сервер #liquibase
Вопрос:
У нас есть несколько сценариев настройки, которые немного сложны. Эти сценарии используют ряд доступных хранимых процедур в нашей базе данных для вставки данных конфигурации. Если какой-либо скрипт попытается вставить недопустимые данные, хранимые процедуры вызовут
RAISERROR @msg, 16, 1
И тогда SP вернется. SP запускают свои собственные именованные транзакции, и они будут откатывать / фиксировать именованную транзакцию, однако LB не обнаруживает возникшую ошибку и принимает выполнение как успешное выполнение. Но мы не хотим продолжать, если это произойдет.
failOnError в наборах изменений имеет значение true, но LB все равно продолжается. Даже в DATABASECHANGELOG этот неудачный набор изменений помечен как выполненный, как если бы он был успешным.
Мы также пытались удалить вложенные транзакции (именованные транзакции), но безуспешно.
Мы удалили имя из транзакции и просто использовали BEGIN TRAN, выполнение LB останавливается при неправильном сценарии, но проблема в том, что LB не может зафиксировать свою собственную транзакцию и не может снять блокировку, поэтому она остается ЗАБЛОКИРОВАННОЙ.
Есть ли способ сообщить LB, что произошла ошибка, и остановить ее?
Мы используем Liquibase 3.5.0. и Microsoft SQL Server
=== РЕДАКТИРОВАТЬ
Итак, после отладки Liquibase мы обнаружили две вещи:
-
При подключении к MS SQL Server, если возникает RAISERROR и в скрипте также есть результирующие наборы, он не выдаст исключение, если мы не вызовем statement.getMoreResults() . То же самое происходит с Sybase (мы тестировали это и с Sybase). Поэтому мы подумали, что, возможно, в LB после выполнения инструкции нам нужно вызвать getMoreResults(), пока он не выдаст исключение или не вернет false, что означает, что ошибки не произошло.
-
Скрипт выполняет вызов хранимой процедуры. Хранимая процедура имеет «BEGIN TRAN», и в конце она либо ФИКСИРУЕТСЯ, либо ОТКАТЫВАЕТСЯ. Если происходит откат, он также выполняет RAISERROR. Обратите внимание, что наши скрипты не выполняют никаких обновлений / вставок, они только предоставляют данные во временной таблице, поэтому мы не выполняем обработку транзакций в наших скриптах. В этом сценарии, учтите, что мы добавили код для вызова getMoreResults() , исключение выдается правильно, но затем в LB исполнитель пытается database.rollback(), а затем снова в StandardLockService, прежде чем снять блокировку, он пытается database.rollback(), который заканчивается исключениемпотому что наш SP уже откатил транзакцию. Этот последний откат в LB приводит к тому, что ошибка, вызванная JDBC, проглатывается, и в результате мы не только видим ошибку, которая ее вызвала, но и блокировку, которая осталась неизданной, и это вызывает наибольшую озабоченность, потому что, даже если мы повторно запустим скрипт и исправим его, блокировка не была снятаи нам нужно сделать это вручную.
Можно возразить, что наша обработка транзакций неверна, но все, что я пытаюсь сказать, это то, что снятие блокировки не должно влиять, если наш сценарий неверен. LB должен снимать блокировку и вызывать исключение или продолжить, если сценарий / набор изменений не выполняется успешно.
Комментарии:
1. Я также отметил это в очень простых скриптах, которые выполняют всего несколько вставок. Решением для меня было разделить каждую вставку разделителем пакетов (GO). Я использую sql в формате Liquibase.
2. Насколько мне известно, Liquibase использует соединение JDBC с БД. Есть ли у вас под рукой инструмент для работы с БД, который также использует драйвер JDBC? Вы можете вручную запустить инструкции SQL, которые будет выполнять liquibase, и посмотреть, что произойдет. Может быть, ошибка не передается обратно в инструкцию sql, которая вызвала SP? И, следовательно, не сообщается через JDBC вызывающему коду? Я ничего не знаю о SQL-Server dbs. Это просто подсказка о том, как вы могли бы сузить причину этого…
3. @PeterHenell Наш скрипт не отформатирован в формате LB. Многие из них перемещаются из устаревшей системы. Они выполняют несколько действий, например, вставляют записи, проводят дюжину проверок зависимостей, чтобы убедиться, что не вставлены недопустимые данные и т.д.
4. @Jens Я не запускал их с помощью инструмента JDBC, но если вы запустите тот же скрипт в Salford server studio, он распечатает журнал красным цветом, поскольку произошла ошибка. Я попробую использовать JDBC, когда вернусь к работе
5. Если вы используете SQL Server 2012 или новее, вы можете попробовать
THROW
команду, которая прерывает пакетную обработку, в то времяRAISERROR
как это не так. В противном случае вам может потребоваться перехватить и проверить сообщения, возвращающиеся с SQL Server. Обычно это просто операторы печати, но в зависимости от настроек драйвера / подключения ошибки могут обрабатываться как сообщения. В этом случае вам необходимо перехватывать сообщения и проверять их «состояние» / «серьезность» / «уровень», чтобы определить, являются ли они ошибочными или информационными.
Ответ №1:
Если кто-нибудь тоже сталкивается с этим: в моем случае у меня был очень сложный SQL-скрипт только для MS SQL Server. Это также не смогло остановить выполнение изменений LB, если в сценарии SQL произошла ошибка, в любом случае, если я использую RAISERROR или THROW. Что мне нужно сделать, чтобы заставить его работать:
- удалите (или прокомментируйте) все места, где были созданы результирующие наборы (ВЫБЕРИТЕ)
- запустите SQL-скрипт с «SET NOCOUNT ON;», чтобы избежать результатов вставки или обновления (… затронутых строк)
- завершите SQL-скрипт с помощью «SET NOCOUNT OFF;», чтобы LB мог работать должным образом сразу после выполнения SQL-скрипта (set EXECTYPE)
Ответ №2:
Используйте предварительное условие https://docs .liquibase.com/concepts/advanced/preconditions.html
создайте другой набор изменений и проверьте результат выполнения, прежде чем перейти к следующему.