Ошибка транзакции SQL: текущая транзакция не может быть зафиксирована и не может поддерживать операции записи в файл журнала - PullRequest
33 голосов
/ 20 сентября 2011

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

Ответ там содержит ссылки Использование TRY ... CATCH в Transact-SQL , к которому я вернусь через секунду ...

Мой код (унаследованный, конечно) имеет упрощенную форму:

SET NOCOUNT ON
SET XACT_ABORT ON

CREATE TABLE #tmp

SET @transaction = 'insert_backtest_results'
BEGIN TRANSACTION @transaction

BEGIN TRY

    --do some bulk insert stuff into #tmp

END TRY

BEGIN CATCH
    ROLLBACK TRANSACTION @transaction
    SET @errorMessage = 'bulk insert error importing results for backtest '
        + CAST(@backtest_id as VARCHAR) +
        '; check backtestfiles$ directory for error files ' + 
        ' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) + 
        ' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) +
        ' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) +
        ' error_state ' +  CAST(ERROR_STATE() AS VARCHAR) + 
        ' error_line: ' + CAST(ERROR_LINE() AS VARCHAR)
    RAISERROR(@errorMessage, 16, 1)
    RETURN -666
END CATCH

BEGIN TRY

    EXEC usp_other_stuff_1 @whatever

    EXEC usp_other_stuff_2 @whatever

    -- a LOT of "normal" logic here... inserts, updates, etc...

END TRY

BEGIN CATCH

    ROLLBACK TRANSACTION @transaction
    SET @errorMessage = 'error importing results for backtest '
        + CAST(@backtest_id as VARCHAR) +
        ' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) + 
        ' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) +
        ' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) +
        ' error_state ' +  CAST(ERROR_STATE() AS VARCHAR) + 
        ' error_line: ' + CAST(ERROR_LINE() AS VARCHAR)
    RAISERROR(@errorMessage, 16, 1)
    RETURN -777

END CATCH

RETURN 0

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

Эта хранимая процедура периодически выдает такие ошибки:

ошибка импорта результатов для бэк-теста 9649 error_number: 3930 error_message: текущая транзакция не может быть зафиксирована и не может поддерживать операции, которые записывают в файл журнала. Откат транзакции. error_severity: 16 error_state 1 error_line: 217

Итак, очевидно, что ошибка исходит из 2-го блока catch

Основываясь на том, что я прочитал в Используя TRY ... CATCH в Transact-SQL , я думаю, что при возникновении исключения используется XACT_ABORT вызывает «завершение и откат» транзакции ... и затем первая строка BEGIN CATCH пытается вслепую откатить снова.

Я не знаю, почему первоначальный разработчик включил XACT_ABORT, поэтому я думаю, что лучшим решением (чем его удаление) было бы использование XACT_STATE() для отката только при наличии транзакции (<>0 ). Это звучит разумно? Я что-то упустил?

Кроме того, упоминание о регистрации в сообщении об ошибке заставляет меня задуматься: есть ли другая проблема, возможно, с конфигурацией? Способствует ли использование RAISEERROR() в этом сценарии этой проблеме? Регистрируется ли это, в каком-то случае, когда регистрация невозможна, так как на это ссылается сообщение об ошибке?

Ответы [ 2 ]

37 голосов
/ 20 сентября 2011

Вы всегда должны проверять XACT_STATE(), независимо от настройки XACT_ABORT.У меня есть пример шаблона для хранимых процедур, которые должны обрабатывать транзакции в контексте TRY / CATCH на Обработка исключений и вложенные транзакции :

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(),
               @message = ERROR_MESSAGE(), 
               @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
15 голосов
/ 08 мая 2012

В приведенном выше обсуждении есть несколько недоразумений.

Во-первых, вы всегда можете откатить транзакцию ... независимо от состояния транзакции. Поэтому вам нужно только проверить XACT_STATE перед COMMIT, а не перед откатом.

Что касается ошибки в коде, вы захотите поместить транзакцию в TRY. Тогда в вашем CATCH первое, что вы должны сделать, это следующее:

 IF @@TRANCOUNT > 0
      ROLLBACK TRANSACTION @transaction

Затем, после вышеприведенного заявления, вы можете отправить электронное письмо или все, что вам нужно. (К вашему сведению: если вы отправите электронное письмо ПЕРЕД откатом, то вы обязательно получите ошибку «невозможно ... записать в файл журнала».)

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

Как правило ... ПОПЫТОК немедленно перейдет к ЗАПИСИ, если возникнет ошибка. Затем, когда вы находитесь в CATCH, вы можете использовать XACT_STATE, чтобы решить, можете ли вы зафиксировать. Но если вы всегда хотите выполнить ROLLBACK в улове, вам вообще не нужно проверять состояние.

...