Как избежать использования повторяющихся имен точек сохранения во вложенных транзакциях во вложенных хранимых процессах? - PullRequest
4 голосов
/ 09 апреля 2010

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

BEGIN TRANSACTION
SAVE TRANSACTION TX

-- Stuff

IF @error <> 0
    ROLLBACK TRANSACTION TX

COMMIT TRANSACTION

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

Довольно часто у меня будет хранимая процедура, вызывающая другие хранимые процедуры, которые используют один и тот же шаблон. Я обнаружил (к моей стоимости), что, поскольку я везде использую одно и то же имя точки сохранения, я могу попасть в ситуацию, когда моя внешняя транзакция частично зафиксирована - в точности противоположность атомарности, которую я пытаюсь достичь .

Я собрал пример, который показывает проблему. Это один пакет (без вложенных хранимых процедур), и это выглядит немного странно, так как вы, вероятно, не использовали бы одно и то же имя точки сохранения дважды в одном пакете, но мой реальный сценарий будет слишком запутанным для публикации.

CREATE TABLE Test (test INTEGER NOT NULL)

BEGIN TRAN 
SAVE TRAN TX

    BEGIN TRAN
    SAVE TRAN TX
        INSERT INTO Test(test) VALUES (1)
    COMMIT TRAN TX

    BEGIN TRAN
    SAVE TRAN TX
        INSERT INTO Test(test) VALUES (2)
    COMMIT TRAN TX

    DELETE FROM Test

ROLLBACK TRAN TX
COMMIT TRAN TX

SELECT * FROM Test

DROP TABLE Test

Когда я выполняю это, он перечисляет одну запись со значением "1". Другими словами, , хотя я откатил свою внешнюю транзакцию , запись была добавлена ​​в таблицу.

Что происходит, так это то, что ROLLBACK TRANSACTION TX на внешнем уровне откатывается до последнего SAVE TRANSACTION TX на внутреннем уровне. Теперь, когда я все это записываю, я вижу логику: сервер просматривает файл журнала, рассматривая его как линейный поток транзакций; он не понимает вложенность / иерархию, подразумеваемую вложенностью транзакций (или, в моем реальном сценарии, вызовами других хранимых процедур).

Итак, ясно, что мне нужно начать использовать уникальные имена точек сохранения, а не вслепую везде использовать «TX». Но - и вот где я наконец дошел до сути - есть ли способ сделать это с возможностью копирования, чтобы я мог все еще использовать один и тот же код везде? Можно ли как-нибудь автоматически сгенерировать имя точки сохранения на лету? Есть ли соглашение или лучшие практики для подобных вещей?

Не сложно придумать уникальное имя каждый раз, когда вы начинаете транзакцию (может основываться на имени SP или что-то в этом роде), но я действительно волнуюсь, что в конечном итоге возникнет конфликт - и вы не станете знать об этом, потому что вместо того, чтобы вызвать ошибку, он просто молча уничтожает ваши данные ...: - (

Ответы [ 2 ]

2 голосов
/ 09 апреля 2010

Согласен с решением КМ.

Я предпочитаю использовать GUID для создания уникальных имен точек сохранения.

DECLARE @savepoint AS VARCHAR(36)
SET @savepoint = CONVERT(VARCHAR(36), NEWID())

BEGIN TRANSACTION
SAVE TRANSACTION @savepoint
...

ROLLBACK TRANSACTION @savepoint
COMMIT TRANSACTION
1 голос
/ 09 апреля 2010

посмотрите на документы: СОХРАНИТЬ СДЕЛКУ (Transact-SQL)

SAVE { TRAN | TRANSACTION } { savepoint_name | @savepoint_variable }
[ ; ]

похоже, что вы можете назвать его на основе переменной, поэтому попробуйте создать свой шаблон:

DECALRE @savepoint_variable varchar(1000)
SET @savepoint_variable=OBJECT_NAME(@@PROCID)+'|'+CONVERT(char(23),GETDATE(),121)

BEGIN TRANSACTION
SAVE TRANSACTION @savepoint_variable

-- Stuff

IF @error <> 0
BEGIN
    ROLLBACK TRANSACTION @savepoint_variable
END

COMMIT TRANSACTION

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...