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?
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
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.