Что происходит с блокировками транзакции при откате к SAVEPOINT, если они были захвачены до и после этой то...

Что происходит с блокировками транзакции при откате к SAVEPOINT, если они были захвачены до и после этой точки?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Откат к SAVEPOINT отменяет изменения данных после этой точки, но не завершает транзакцию. Блокировки, полученные до SAVEPOINT, обычно сохраняются до фиксации или полного отката транзакции; блокировки, полученные после него, во многих СУБД могут быть освобождены при откате к SAVEPOINT.

Точное поведение зависит от СУБД и типа блокировки, поэтому полагаться на автоматическое освобождение всех блокировок нельзя.

Исторический контекст

SAVEPOINT появился как средство частичного отката внутри длинной транзакции. Без него ошибка на одном шаге заставляла бы отменять всю транзакцию, даже если предыдущие операции были корректны и их хотелось сохранить.

Для конкурентного доступа это создало дополнительный вопрос: частичный откат должен отменять данные, но не должен нарушать защиту изменений, которые транзакция собирается сохранить. Поэтому граница отката и граница времени удержания блокировок — не одно и то же.

Постановка проблемы

Транзакция может сначала заблокировать важную строку, затем создать SAVEPOINT и выполнить дополнительную операцию. Если дополнительная операция завершается ошибкой, транзакция откатывается к SAVEPOINT, но продолжает работать.

Если бы при таком откате освобождались все блокировки, другая транзакция могла бы изменить уже проверенную или ранее изменённую строку. Это нарушило бы изоляцию и могло бы привести к потере обновлений или нарушению бизнес-инварианта.

Подробное решение

Блокировка, полученная до SAVEPOINT, защищает работу, выполненную до этой точки, поэтому она обычно остаётся у транзакции. Откат к SAVEPOINT отменяет изменения данных и состояния после точки сохранения, но не превращает транзакцию в новую независимую транзакцию.

Во многих СУБД блокировки, приобретённые после SAVEPOINT, освобождаются при откате к нему. Это позволяет повторить неудачный фрагмент и уменьшить время ожидания других транзакций. Однако поведение зависит от реализации: разные типы блокировок, блокировки на уровне таблиц, блокировки ограничений и внутренние служебные блокировки могут обрабатываться по-разному.

BEGIN; SELECT * FROM inventory WHERE id = 1 FOR UPDATE; SAVEPOINT before_optional_step; SELECT * FROM inventory WHERE id = 2 FOR UPDATE; -- Ошибка в дополнительной операции ROLLBACK TO SAVEPOINT before_optional_step; -- Блокировка строки id = 1 обычно сохраняется; -- блокировка строки id = 2 во многих СУБД освобождается. COMMIT;

Главное следствие: откат к SAVEPOINT не гарантирует, что транзакция перестанет блокировать ранее затронутые строки. Если нужно полностью освободить блокировки, требуется полный ROLLBACK, а не частичный откат.

SAVEPOINT также не создаёт вложенную независимую транзакцию. Внешняя транзакция всё ещё может быть отменена целиком, а её фиксация остаётся единственным моментом, когда все сохранённые изменения становятся окончательными.

Ситуация из практики

Сервис резервирования сначала блокирует строку товара и уменьшает доступный остаток, затем пытается записать необязательную запись аудита. Аудит не записался из-за ошибки формата данных.

Вариант с полным откатом прост и безопасен, но отменяет резервирование и требует повторить всю бизнес-операцию. Вариант без SAVEPOINT может оставить транзакцию в ошибочном состоянии или вынудить приложение вручную компенсировать уже сделанные изменения.

Выбранный вариант — установить SAVEPOINT перед записью аудита, откатиться к нему при ошибке и продолжить транзакцию. При этом блокировка строки товара, полученная раньше, сохраняется, поэтому другая транзакция не изменит остаток до завершения резервирования. Компромисс — транзакция всё ещё удерживает раннюю блокировку и может увеличить конкуренцию за товар.

Что кандидаты часто упускают

1. Освобождает ли ROLLBACK TO SAVEPOINT все блокировки транзакции?

Нет. Он откатывает только часть работы после указанной точки. Ранее полученные блокировки обычно сохраняются, иначе другая транзакция могла бы вмешаться в состояние, которое текущая транзакция уже проверила или изменила.

2. Можно ли считать SAVEPOINT вложенной транзакцией?

Нет. SAVEPOINT задаёт точку частичного отката, но не создаёт отдельную транзакцию с независимыми блокировками, фиксацией и жизненным циклом. Внешняя транзакция по-прежнему отвечает за общий результат и может быть полностью отменена.

3. Гарантирует ли откат к SAVEPOINT, что следующий повтор операции будет безопасным?

Нет. Откат возвращает данные и часть состояния к прежней точке, но транзакция может продолжать удерживать ранние блокировки, а внешние побочные эффекты уже могли произойти. Поэтому повторяемый фрагмент должен быть идемпотентным или защищённым от дублирования, а операции вне базы данных нельзя считать отменёнными SAVEPOINT.