Estamos executando o SQL Server 2019 CU12 para um de nossos clientes. Algum tempo atrás, começamos a obter deadlocks com fio em que um único processo se bloqueia no acesso a uma variável de tabela.
Exemplo de relatório de impasse
<deadlock>
<victim-list>
<victimProcess id="process2ae9f9f7468" />
</victim-list>
<process-list>
<process id="process2ae9f9f7468" taskpriority="0" logused="0" waitresource="OBJECT: 2:-1194094756:0 " waittime="110" ownerId="6978622122" transactionname="GetInitializedIMA" lasttranstarted="2021-09-15T21:46:44.243" XDES="0x2b4d9477be8" lockMode="Sch-S" schedulerid="3" kpid="10868" status="suspended" spid="102" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2021-09-15T21:46:44.113" lastbatchcompleted="2021-09-15T21:46:44.113" lastattention="2021-09-15T21:46:15.777" clientapp=".Net SqlClient Data Provider" hostname="removed" hostpid="15900" loginname="removed" isolationlevel="read committed (2)" xactid="6978622078" currentdb="15" currentdbname="MigrationSubjects" lockTimeout="4294967295" clientoption1="673187936" clientoption2="128056">
<executionStack>
<frame procname="unknown" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">unknown</frame>
</executionStack>
<inputbuf>
(@Ids [SubjectRegistry.Consolidation.IdTable] READONLY)
DELETE [reg].[HistoricalCompanyInfo] FROM [reg].[HistoricalCompanyInfo] t
INNER JOIN @Ids ids ON ids.Id = t.HistoricalCompanyInfoId
</inputbuf>
</process>
</process-list>
<resource-list>
<objectlock lockPartition="0" objid="-1194094756" subresource="FULL" dbid="2" objectname="tempdb.dbo.#B8D38F5C" id="lock2ac0942fa00" mode="Sch-M" associatedObjectId="-1194094756">
<owner-list>
<owner id="process2ae9f9f7468" mode="Sch-M" />
<owner id="process2ae9f9f7468" mode="Sch-S" requestType="wait" />
</owner-list>
<waiter-list>
<waiter id="process2ae9f9f7468" mode="Sch-S" requestType="wait" />
</waiter-list>
</objectlock>
</resource-list>
</deadlock>
A definição do tipo de tabela é a seguinte:
CREATE TYPE [dbo].[SubjectRegistry.Consolidation.IdTable] AS TABLE(
[Id] [bigint] NOT NULL, PRIMARY KEY CLUSTERED ([Id] ASC) WITH (IGNORE_DUP_KEY = OFF)
)
GO
Alguma idéia do que poderia causar esses bloqueios estranhos e como ignorá-los?
Finalmente, acabamos de fazer um avanço com o nosso problema. Obrigado Erik Darling pela dica.
Primeiro, tentarei esclarecer um pouco todo o processo de inscrição para dar o contexto. Existe um código de aplicativo bastante complexo sendo executado como uma única transação de banco de dados. Ele usa vários tipos de tabela definidos pelo usuário como variáveis de tabela. Alguns dos tipos são muito simples com apenas uma coluna (como a mostrada na pergunta), alguns deles contêm mais de 10 colunas de vários tipos.
O aplicativo usa essas variáveis para passar esses dados como parâmetro do sp_executesql. Vários comandos como esse são processados um a um dentro de uma transação pela aplicação (.Net)…
Um exemplo de um código de aplicativo ligeiramente simplificado usado no aplicativo:
E o deadlock mencionado na pergunta ocorreu em um desses comandos em algum lugar no meio da transação…
Tentamos diminuir o nível de compatibilidade de 150 para 140 e a situação mudou. Em vez de deadlock começamos a receber uma mensagem de erro mais conveniente de “ String ou dados binários seriam truncados na tabela… ”, mas de um comando completamente diferente muito mais próximo do final da transação. Quando voltamos para 150, começamos a receber um erro de auto-deadlock e um comando anterior novamente.
Também tentamos manter o nível de compatibilidade em 150 e apenas desligar a compilação adiada por
O impasse também foi retornado. A única alteração do nível de compatibilidade alterou a mensagem de erro retornada.
O problema de truncamento mencionado acima foi que o aplicativo tentou inserir uma string nvarchar mais longa do que a definição de coluna do tipo de tabela permitida. Quando atualizamos a definição do tipo de tabela para acomodar strings mais longas, todos os comandos na transação foram processados sem problemas, independentemente do nível de compatibilidade definido.
Tentei simular esse comportamento em um exemplo artificial fora do aplicativo .Net, mas sem sucesso. Portanto, não consigo passar nenhuma etapa de replicação exata para esse comportamento. Cada tentativa que fiz resultou no erro “conveniente” “ String ou dados binários seriam truncados… ”. No entanto, a mecânica geral do problema parece ser como sugerido acima.
Apenas uma rápida recapitulação:
Espero que ajude alguém que encontre o mesmo problema.