Como capturar SqlException causado por deadlock?

92

De um aplicativo .NET 3.5 / C #, gostaria de capturar, SqlExceptionmas somente se for causado por deadlocks em uma instância do SQL Server 2008.

A mensagem de erro típica é Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Ainda assim, não parece ser um código de erro documentado para essa exceção.

Filtrar exceção contra a presença da palavra-chave de deadlock em sua mensagem parece uma maneira muito desagradável de atingir esse comportamento. Alguém conhece a maneira certa de fazer isso?

Joannes Vermorel
fonte
3
(Finalmente) encontrei a documentação para o código de erro: msdn.microsoft.com/en-us/library/aa337376.aspx . Você também pode encontrar isso por meio do próprio SQL Server:select * from master.dbo.sysmessages where error=1205
Martin McNulty

Respostas:

153

O código de erro específico do Microsft SQL Server para um deadlock é 1205, então você precisa lidar com o SqlException e verificar isso. Então, por exemplo, se para todos os outros tipos de SqlException você quiser que a exceção seja exibida:

catch (SqlException ex)
{
    if (ex.Number == 1205)
    {
        // Deadlock 
    }
    else
        throw;
}

Ou, usando a filtragem de exceção disponível em C # 6

catch (SqlException ex) when (ex.Number == 1205)
{
    // Deadlock 
}

Uma coisa útil a fazer para encontrar o código de erro SQL real para uma determinada mensagem é olhar em sys.messages no SQL Server.

por exemplo

SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033

Uma maneira alternativa de lidar com deadlocks (do SQL Server 2005 e superior) é fazer isso dentro de um procedimento armazenado usando o suporte 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

Há um exemplo completo aqui no MSDN de como implementar a lógica de nova tentativa de deadlock puramente no SQL.

AdaTheDev
fonte
2
Observe que os códigos de erro são específicos do fornecedor, então 1205 é um deadlock para o SQL Server, mas pode ser diferente para Oracle, MySQL, etc.
brianmearns
3
Dependendo da camada de dados, o SqlExceptionpode ser empacotado em outra. Portanto, podemos precisar capturar qualquer tipo de exceção e verificá-los, se eles não forem diretamente uma exceção de deadlock, verifique recursivamente os deles InnerException.
Frédéric
46

Como suponho que você queira detectar bloqueios, para poder tentar novamente a operação que falhou, gostaria de avisá-lo para um pequeno pegadinho. Espero que me desculpem por estar um pouco fora do assunto aqui.

Um deadlock detectado pelo banco de dados reverterá efetivamente a transação na qual você estava executando (se houver), enquanto a conexão é mantida aberta no .NET. Tentar novamente essa operação (na mesma conexão) significa que ela será executada em um contexto sem transação e isso pode levar à corrupção de dados.

É importante estar ciente disso. É melhor considerar a conexão completa condenada no caso de uma falha causada por SQL. A nova tentativa da operação só pode ser feita no nível em que a transação está definida (recriando essa transação e sua conexão).

Portanto, quando estiver tentando novamente uma operação com falha, certifique-se de abrir uma conexão completamente nova e iniciar uma nova transação.

Steven
fonte
4
Por que você precisa de uma conexão completamente nova? Eu postei uma pergunta sobre essa resposta aqui .
Sam,
3

Aqui está uma maneira C # 6 de detectar deadlocks.

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
}

Certifique-se de que este try..catch envolva toda a transação. De acordo com @Steven (veja sua resposta para detalhes), quando o comando sql falha devido ao deadlock, ele faz com que a transação seja revertida e, se você não recriar a transação, sua nova tentativa será executada fora do contexto de a transação e pode resultar em inconsistências de dados.

Brian
fonte