2011-09-20 16 views
26

Ho riscontrato un problema simile a The current transaction cannot be committed and cannot support operations that write to the log file, ma ho una domanda di follow-up.Errore di transazione SQL: impossibile eseguire il commit della transazione corrente e non supportare le operazioni che scrivono nel file di registro

La risposta non fa riferimento Using TRY...CATCH in Transact-SQL, che tornerò a in un secondo ...

Il mio codice (ereditata, naturalmente) ha la forma semplificata:

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 

penso io avere abbastanza informazioni per giocarci e capirlo da solo ... sfortunatamente riprodurre l'errore si sta dimostrando dannatamente vicino impossibile. Quindi spero che chiedere qui aiuterà a chiarire la mia comprensione del problema e della soluzione.

Questa stored procedure è, ad intermittenza, gettando errori come questo:

error importing results for backtest 9649 error_number: 3930 error_message: The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction. error_severity: 16 error_state 1 error_line: 217

Così, ovviamente, l'errore sta venendo dal 2 ° blocco catch

In base a quello che ho letto in Using TRY...CATCH in Transact-SQL, I pensate che quello che sta succedendo è che quando viene lanciata l'eccezione, l'uso di XACT_ABORT sta causando la "chiusura e il rollback" della transazione ... e quindi la prima riga di BEGIN CATCH tenta ciecamente di tornare indietro.

Non so il motivo per cui lo sviluppatore originale abilitato XACT_ABORT, così sto pensando la soluzione migliore (che rimuoverlo) sarebbe quella di utilizzare XACT_STATE() a rotolare indietro solo se v'è una transazione (<>0). Suona ragionevole? Mi sto perdendo qualcosa?

Inoltre, la menzione di registrazione nel messaggio di errore mi fa pensare: c'è un altro problema, potenzialmente con la configurazione? Il nostro uso di RAISEERROR() in questo scenario contribuisce al problema? Viene registrato, in alcuni casi in cui la registrazione non è possibile, come allude il messaggio di errore?

risposta

27

È sempre necessario verificare XACT_STATE(), irrilevante dell'impostazione XACT_ABORT. Ho un esempio di un modello per le stored procedure che hanno bisogno di gestire le transazioni nel contesto try/catch in Exception handling and nested transactions:

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 
+0

Il modello assume transazioni all'interno del blocco try; abbiamo più blocchi try all'interno di 1 transazione. –

+0

@Adam: riguarda il modo in cui gestisci "XACT_STATE" e le transazioni nel blocco CATCH. È possibile avere più blocchi try in una transazione utilizzando questo stesso modello. L'idea è di capire come interagiscono le transazioni e i blocchi catch e come bonus si ottiene anche la gestione delle transazioni nidificate e dei punti di salvataggio, che è molto utile nell'elaborazione batch, poiché dà la possibilità di riprendere il resto del batch anche se uno l'entrata era fallita. –

+0

Ho proseguito e ho completato il rollback in un 'if XACT_STATE() <> 0', ma solo il tempo ci dirà se questo lo risolve per noi. Suppongo che andrò avanti e accetto la tua risposta per ora. –

12

ci sono alcune incomprensioni nella discussione di cui sopra.

In primo luogo, è sempre possibile ROLLBACK una transazione ... indipendentemente dallo stato della transazione. Quindi devi solo controllare lo XACT_STATE prima di un COMMIT, non prima di un rollback.

Per quanto riguarda l'errore nel codice, si desidera inserire la transazione all'interno della PROVA. Poi, nel vostro fermo, la prima cosa da fare è la seguente:

IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION @transaction 

Poi, dopo la dichiarazione di cui sopra, allora si può inviare una e-mail o tutto ciò che è necessario. (FYI: Se invii l'e-mail PRIMA del rollback, otterrai sicuramente l'errore "impossibile ... scrivere su file di log")

Questo numero è stato pubblicato l'anno scorso, quindi spero che tu abbia risolto questo problema ora :-) Remus ti ha indicato la direzione giusta.

Come regola generale ... il TRY passerà immediatamente al CATCH quando si verifica un errore. Quindi, quando ti trovi nel CATCH, puoi utilizzare XACT_STATE per decidere se puoi eseguire il commit. Ma se vuoi sempre ROLLBACK nel catch, allora non è necessario controllare lo stato.

Problemi correlati