web-dev-qa-db-fra.com

Erreur de transaction SQL: la transaction en cours ne peut pas être validée et ne peut pas prendre en charge les opérations qui écrivent dans le fichier journal

Je rencontre un problème similaire à La transaction actuelle ne peut pas être validée et ne peut pas prendre en charge les opérations qui écrivent dans le fichier journal , mais j'ai une question complémentaire.

La réponse y fait référence Utilisation de TRY ... CATCH dans Transact-SQL , sur laquelle je reviendrai dans une seconde ...

Mon code (hérité, bien sûr) a la forme simplifiée:

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

Je pense que j'ai suffisamment d'informations pour jouer avec et le découvrir moi-même ... malheureusement, reproduire l'erreur se révèle sacrément impossible. J'espère donc que demander ici aidera à clarifier ma compréhension du problème et de la solution.

Cette procédure stockée génère, par intermittence, des erreurs comme celle-ci:

erreur lors de l'importation des résultats du backtest 9649 numéro_erreur: 3930 message_erreur: la transaction en cours ne peut pas être validée et ne peut pas prendre en charge les opérations qui écrivent dans le fichier journal. Annulez la transaction. error_severity: 16 error_state 1 error_line: 217

Donc, évidemment, l'erreur vient du 2e bloc de capture

Sur la base de ce que j'ai lu dans Utilisation de TRY ... CATCH dans Transact-SQL , je pense que ce qui se passe est que lorsque l'exception est levée, l'utilisation de XACT_ABORT provoque la "fin et l'annulation" de la transaction ... puis la première ligne du BEGIN CATCH tente aveuglément de revenir en arrière.

Je ne sais pas pourquoi le développeur d'origine a activé XACT_ABORT, Donc je pense que la meilleure solution (que de le supprimer) serait d'utiliser XACT_STATE() pour annuler uniquement s'il y a une transaction (<>0). Cela vous semble-t-il raisonnable? Suis-je en train de manquer quelque chose?

De plus, la mention de la connexion dans le message d'erreur me fait me demander: y a-t-il un autre problème, potentiellement avec la configuration? Notre utilisation de RAISEERROR() dans ce scénario contribue-t-elle au problème? Est-ce que cela est enregistré, dans une sorte de cas où la journalisation n'est pas possible, comme le fait allusion au message d'erreur?

34
Adam Tuttle

Vous devez toujours vérifier XACT_STATE(), sans rapport avec XACT_ABORT réglage. J'ai un exemple de modèle pour les procédures stockées qui doivent gérer les transactions dans le contexte TRY/CATCH à Gestion des exceptions et transactions imbriquées :

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
38
Remus Rusanu

Il y a quelques malentendus dans la discussion ci-dessus.

Tout d'abord, vous pouvez toujours ROLLBACK une transaction ... quel que soit l'état de la transaction. Il vous suffit donc de vérifier le XACT_STATE avant un COMMIT, pas avant un rollback.

En ce qui concerne l'erreur dans le code, vous souhaiterez mettre la transaction dans le TRY. Ensuite, dans votre CATCH, la première chose à faire est la suivante:

 IF @@TRANCOUNT > 0
      ROLLBACK TRANSACTION @transaction

Ensuite, après la déclaration ci-dessus, vous pouvez envoyer un e-mail ou tout ce qui est nécessaire. (Pour info: si vous envoyez l'e-mail AVANT la restauration, vous obtiendrez définitivement l'erreur "impossible ... d'écrire dans le fichier journal".)

Ce problème venait de l'année dernière, donc j'espère que vous avez résolu ce problème maintenant :-) Remus vous a indiqué la bonne direction.

En règle générale ... le TRY sautera immédiatement au CATCH en cas d'erreur. Ensuite, lorsque vous êtes dans CATCH, vous pouvez utiliser le XACT_STATE pour décider si vous pouvez valider. Mais si vous voulez toujours ROLLBACK dans la capture, vous n'avez pas du tout besoin de vérifier l'état.

15
Donna_123_a