.net - tratamiento - Cómo atrapar SqlException causado por un interbloqueo?
que es un deadlock en base de datos (3)
Desde una aplicación .NET 3.5 / C #, me gustaría capturar SqlException
pero solo si es causada por interbloqueos en una instancia de SQL Server 2008.
El mensaje de error típico es Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Sin embargo, no parece ser un código de error documentado para esta excepción.
Filtrado de excepción contra la presencia de la palabra clave de interbloqueo en su mensaje parece una forma muy fea de lograr este comportamiento. ¿Alguien sabe la forma correcta de hacer esto?
Aquí hay una forma C # 6 de detectar interbloqueos.
try
{
//todo: Execute SQL.
//IMPORTANT, if you used Connection.BeginTransaction(), this try..catch must surround that code. You must rollback the original transaction, then recreate it and re-run all the code.
}
catch (SqlException ex) when (ex.Number == 1205)
{
//todo: Retry SQL
}
Asegúrese de que try..catch rodee toda su transacción. Según @Steven (ver su respuesta para más detalles), cuando falla el comando sql debido al interbloqueo, hace que la transacción se retrotraiga y, si no recrea la transacción, su reintento se ejecutará fuera del contexto de la transacción y puede provocar inconsistencias en los datos.
Como supongo que posiblemente desee detectar interbloqueos, para poder volver a intentar la operación fallida, me gustaría advertirle un poco. Espero que me disculpes por estar un poco fuera de tema aquí.
Un punto muerto detectado por la base de datos revertirá efectivamente la transacción en la que se estaba ejecutando (si corresponde), mientras que la conexión se mantiene abierta en .NET. Volver a intentar esa operación (en esa misma conexión) significa que se ejecutará en un contexto sin transacción y esto podría provocar daños en los datos.
Es importante ser consciente de esto. Lo mejor es considerar la conexión completa condenada en caso de una falla causada por SQL. Volver a intentar la operación solo puede hacerse en el nivel donde se define la transacción (recreando esa transacción y su conexión).
Por lo tanto, cuando vuelva a intentar una operación fallida, asegúrese de abrir una conexión completamente nueva y comenzar una nueva transacción.
El código de error específico de Microsft SQL Server para un interbloqueo es 1205, por lo que necesitaría manejar la SqlException y verificarlo. Entonces, por ejemplo, si para todos los demás tipos de SqlException desea que la burbuja sea la excepción:
catch (SqlException ex)
{
if (ex.Number == 1205)
{
// Deadlock
}
else
throw;
}
O bien, utilizando el filtrado de excepciones disponible en C # 6
catch (SqlException ex) when (ex.Number == 1205)
{
// Deadlock
}
Una forma práctica de encontrar el código de error de SQL real para un mensaje determinado es buscar los mensajes de sys en SQL Server.
p.ej
SELECT * FROM sys.messages WHERE text LIKE ''%deadlock%'' AND language_id=1033
Una forma alternativa de manejar deadlocks (desde SQL Server 2005 y superior) es hacerlo dentro de un procedimiento almacenado usando el soporte TRY ... CATCH:
BEGIN TRY
-- some sql statements
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() = 1205)
-- is a deadlock
ELSE
-- is not a deadlock
END CATCH
Hay un ejemplo completo here en MSDN de cómo implementar lógica de reintentos de interbloqueo puramente dentro de SQL.