У меня несколько необычная потребность в обработке транзакций.
Я работаю над средой тестирования мутаций для SQL Server, для этого мне нужно запускать свои тесты внутри транзакции, чтобы база данных всегда находилась в том состоянии, в котором она была запущена, когда тесты заканчиваются.
Однако у меня есть проблема, заключающаяся в том, что пользователи могут кодировать внутри тестовых процедур и могут вызывать rollback transaction, которые могут быть или не быть внутри (вложенной) транзакции (точки сохранения).
высокий уровень выглядит так
start transaction
initialize test
run test with user code
may or may not contain:
- start tran
- start tran savename
- commit tran
- commit tran savename
- rollback tran
- rollback tran savename
output testresults
rollback transaction
Есть ли способ убедиться, что я всегда могу вернуться к исходному состоянию? Я должен принять во внимание, что пользователи могут вызывать хранимые процедуры/триггеры, которые могут быть вложенными, и все они могут содержать операторы транзакций. Со всеми моими решениями в тот момент, когда пользователь использует откат транзакций в своем тестовом коде, они избегают транзакции, и не все будет очищено.
Я хочу, чтобы, если пользователь вызывает откат, откатывается только его часть транзакции, а моя транзакция, которую я запускаю до инициализации теста, остается нетронутой.
Если это возможно, я хочу запретить своим пользователям использовать шаблон транзакции, который использует точки сохранения, когда транзакция уже существует.
Возможная альтернатива — использовать снимки базы данных для возврата базы данных в любое желаемое состояние независимо от того, что произошло в транзакциях. Начиная с SQL Server 2016 SP1, они доступны во всех выпусках; до этого они были доступны только в Enterprise и Developer.
насколько эффективны моментальные снимки по сравнению с транзакциями? Кажется, это может быть решением, если его можно будет сделать и восстановить автоматически.
Это будет сильно зависеть от того, что делают ваши транзакции; вам нужно проверить. Прошло некоторое время с тех пор, как я действительно тестировал эту настройку. Возврат к моментальному снимку, безусловно, быстрее, чем резервное копирование/восстановление (даже инкрементное), но не быстрее, чем очень тривиальные операторы SQL. Основное преимущество заключается в том, что вы не зависите от конкретных действий кода. Лично я ценю надежность тестов гораздо выше, чем скорость их выполнения. Вы всегда можете масштабировать запущенные тесты, бросив на них больше ресурсов (в данном случае несколько БД и параллелизм), но с надежностью не так повезло.
Как я сейчас читаю, у снимков возникают проблемы при попытке создать их автоматически, так как я не могу повлиять на место установки/прочитать путь установки для всех версий или найти доступную папку.


Я не думаю, что это возможно без каких-либо сообщений/правил для пользовательского кода. Несмотря на то, что вы делаете, если пользовательский код запустит столько COMMIT, сколько есть @@TRANCOUNT в это время, транзакция будет зафиксирована, и вы ничего не сможете с этим поделать.
Один из способов сделать это — проверить/принудить пользовательский код вместо использования COMMIT изменить его на if @@TRANCOUNT>=2 COMMIT. Это гарантирует, что ИСТИННАЯ фиксация данных может быть выполнена только ВАШЕЙ командой COMMIT. Конечно, бывает, что никогда особо не хочется коммита, так что просто rollback и все кончено.
Вы упоминаете:
What I want is that if a user calls rollback only their part of the transaction is rolled back
Обратите внимание, что вложенные транзакции — это своего рода миф. См. эта отличная статья. Короче говоря: «вложенные» BEGIN TRANSACTION и COMMIT на самом деле ничего не делают, кроме изменения значения системной переменной @@TRANCOUNT, чтобы можно было организовать некоторую организацию с помощью процедур.
Я искал способ имитировать вложенные транзакции, но если это невозможно, я подумаю о том, чтобы заставить своих пользователей использовать шаблон транзакции.
Я также рассмотрю ваше предложение изменить их код, чтобы добавить проверку транзакций.
Принуждение пользователей к использованию блока try/transaction/catch — это решение, которое может занять больше времени, но в целом является хорошей практикой. Одним из возможных вариантов может быть Erland Sommerskog: sommarskog.se/error_handling/Part1.html#jumpgeneralpattern . Имейте в виду, однако, что принудительное использование блока должно означать запрет на использование commit внутри раздела функционального кода, поэтому, похоже, вы не можете избежать этой части.
Я не думаю, что можно откатить часть транзакции и сохранить другую транзакцию нетронутой. В тот момент, когда мы откатываем транзакцию, откатывается вся транзакция.
Как указано другими серверами, вложенные транзакции не поддерживаются в SQL Server. Я решил свести к минимуму количество операторов изменения состояния после пользовательского кода и очистить эти операторы в начале тестовой партии. Таким образом, на самом деле не имеет значения, что я не могу откатить свою собственную транзакцию, поскольку тяжелая работа будет отменена либо мной, либо пользователем.
Я также решил провалить тест, когда начальный @@TRANCOUNT и конечный @@TRANCOUNT не совпадают, чтобы ни один тест не мог пройти, когда с транзакциями пользователей что-то не так.
Однако текущая система по-прежнему будет бороться с пользователем, выполняющим COMMIT TRAN. Это проблема в другой раз.
Я считаю, что на самом деле необходимо написать тестовые примеры для T-SQL. Если это так, то я чувствую, что вам не нужно изобретать велосипед и начинать использовать среду тестирования с открытым исходным кодом T-SQLT https://tsqlt.org/.
Я знаю о tsqlt, и я черпал из него вдохновение, проблема в том, что он не поддерживает проверку шпионажа, надлежащее покрытие кода или тестирование мутаций.
rollback tranвсегда откатывайте всю транзакцию. Если вашим пользователям разрешено вызывать это, им разрешено откатывать вашу транзакцию. Вы ничего не можете с этим поделать. Код, который делает это, является PITA для использования. Их код нужно писать так, чтобы не откатывать не начатые им транзакции.