2011-09-20 85 views
26

Tengo un problema similar al The current transaction cannot be committed and cannot support operations that write to the log file, pero tengo una pregunta de seguimiento.Error de transacción SQL: la transacción actual no se puede confirmar y no puede admitir operaciones que escriben en el archivo de registro

La respuesta no hace referencia a Using TRY...CATCH in Transact-SQL, que voy a volver en un segundo ...

Mi código (heredado, por supuesto) tiene la forma simplificada:

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 

creo que tengo suficiente información para jugar con ella y resolverla yo mismo ... desafortunadamente reproducir el error está resultando casi imposible. Así que espero que preguntar aquí ayude a aclarar mi comprensión del problema y la solución.

Este procedimiento almacenado es, de forma intermitente, arrojando errores como éste:

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

Así que, obviamente, el error viene del segundo bloque catch

Sobre la base de lo que he leído en Using TRY...CATCH in Transact-SQL, me piense que lo que sucede es que cuando se lanza la excepción, el uso de XACT_ABORT está causando que la transacción "finalice y se retrotraiga" ... y entonces la primera línea del BEGIN CATCH intenta ciegamente volver atrás.

No sé por qué el desarrollador original habilitado XACT_ABORT, así que estoy pensando que la mejor solución (de eliminarlo) sería utilizar XACT_STATE() sólo para hacer retroceder si hay una transacción (<>0). ¿Eso suena razonable? ¿Me estoy perdiendo de algo?

Además, la mención de iniciar sesión en el mensaje de error me hace preguntarme: ¿Hay otro problema, potencialmente con la configuración? ¿Nuestro uso de RAISEERROR() en este escenario contribuye al problema? ¿Eso se registra, en algún tipo de caso donde el registro no es posible, como se alude al mensaje de error?

Respuesta

27

Siempre debe verificar XACT_STATE(), irrelevante de la configuración XACT_ABORT. Tengo un ejemplo de una plantilla para procedimientos almacenados que necesitan para manejar las transacciones en el contexto TRY/CATCH en 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

Su plantilla supone transacciones dentro del bloque try; tenemos múltiples bloques de prueba dentro de 1 transacción. –

+0

@ Adam: Se trata de cómo manejas el 'XACT_STATE' y las transacciones en el bloque CATCH. Puedes tener múltiples bloques de prueba en una transacción usando esta misma plantilla. La idea es comprender cómo interactúan las transacciones y los bloques catch, y como bonificación también se manejan las transacciones anidadas y los puntos de rescate, lo cual es muy útil en el procesamiento por lotes, ya que permite reanudar el resto del lote incluso si la entrada había fallado –

+0

He seguido adelante y envolví la declaración de reversión en un 'si XACT_STATE() <> 0', pero solo el tiempo dirá si eso lo resuelve por nosotros. Supongo que seguiré adelante y acepto tu respuesta por el momento. –

12

Hay algunos malentendidos en el debate anterior.

En primer lugar, siempre puede ROLLBACK una transacción ... sin importar el estado de la transacción. Por lo tanto, solo tiene que verificar el XACT_STATE antes de un COMMIT, no antes de un rollback.

En cuanto al error en el código, querrá poner la transacción dentro del TRY. Luego, en su captura, la primera cosa que debe hacer es la siguiente:

IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION @transaction 

Entonces, después de la declaración anterior, entonces usted puede enviar un correo electrónico o lo que sea necesario. (Para su información: si envía el correo electrónico ANTES de la reversión, definitivamente obtendrá el error "No se puede ... escribir en el archivo de registro")

Este problema fue del año pasado, así que espero que lo haya resuelto ahora :-) Remus te indicó la dirección correcta.

Como regla general ... el TRY saltará inmediatamente a la CATCH cuando haya un error. Luego, cuando esté en el CATCH, puede usar XACT_STATE para decidir si puede comprometerse. Pero si siempre quieres ROLLBACK en la captura, entonces no necesitas verificar el estado en absoluto.

Cuestiones relacionadas