O problema
Eu tenho um par de consultas que, sob isolamento serializável, causam um bloqueio RX-X. No entanto, quando uso Extended Events para assistir a aquisição de bloqueio, a aquisição de bloqueio RX-X nunca aparece, apenas é liberada. De onde isso vem?
A reprodução
Segue minha tabela:
CREATE TABLE dbo.LockTest (
ID int identity,
Junk char(4)
)
CREATE CLUSTERED INDEX CX_LockTest --not unique!
ON dbo.LockTest(ID)
--preload some rows
INSERT dbo.LockTest
VALUES ('data'),('data'),('data')
Aqui está o meu lote de problemas:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
INSERT dbo.LockTest
VALUES ('bleh')
SELECT *
FROM dbo.LockTest
WHERE ID = SCOPE_IDENTITY()
--ROLLBACK
Eu verifico os bloqueios mantidos por esta sessão e vejo RX-X:
SELECT resource_type, request_mode, request_status, resource_description
FROM sys.dm_tran_locks
WHERE request_session_id = 72 --change SPID!
Mas também tenho um Evento Estendido em lock_acquired
e lock_released
. Eu o filtro no associado_object_id apropriado... não há RX-X.
Após executar o rollback, vejo o RX-X (LAST_MODE) liberado, mesmo que nunca tenha sido adquirido.
O que eu tentei
Olhei para todos os bloqueios em Eventos Estendidos - sem filtragem. Nenhum bloqueio RX-X adquirido.
Eu também tentei Profiler: mesmos resultados (exceto, é claro, que obtém o nome certo ... sem "LAST_MODE").
Eu executei o XE para escalonamentos de bloqueio - não está lá.
Não há XE especificamente para conversões, mas pude confirmar que pelo menos a conversão de bloqueio de U para X é capturada por
lock_acquired
Também digno de nota é o RI-N que é adquirido, mas nunca lançado. Minha hipótese atual é que o RX-X é um bloqueio de conversão, conforme descrito aqui . Há bloqueios de intervalo de chaves sobrepostos no meu lote que parecem se qualificar para conversão, mas o bloqueio RX-X não está na tabela de conversão.
De onde vem esse bloqueio e por que não é captado pelos Eventos Estendidos?
A inserção de linha única adquire um
X
bloqueio (exclusivo) na nova linha.As
SELECT
tentativas de adquirir um bloqueio de chave compartilhada (RangeS-S
) compartilhado por intervalo.Essa solicitação é relatada pelo
lock_acquired
Extended Event como mode =RS_S
.É relatado pela classe de eventos Profiler
Lock:Acquired
como modo 13 (LCK_M_RS_S
).O modo solicitado é combinado com o modo de bloqueio exclusivo
Lock::CalculateGrantMode
existente emsqlmin.dll
. Não há modo combinado de intervalo compartilhado, exclusivo de chave (RangeS-X
), portanto, o resultado do cálculo é exclusivo de intervalo, exclusivo de chave (RangeX-X
), que é o modo 15.O cálculo do modo de concessão acima é executado antes que o evento estendido seja gerado pelo
lck_ProduceExtendedEvent<XeSqlPkg::lock_acquired>
. No entanto, tanto o Profiler quanto o Extended Events registram o modo solicitadoRangeS-S
, não o modo de bloqueio resultanteRangeX-X
. Isso é contrário à documentação limitada , que diz:A coluna de modo do evento estendido não possui documentação e a descrição nos metadados está em branco. Talvez a própria Microsoft nem tivesse certeza do comportamento.
Muitas vezes pensei que seria mais útil se os eventos de bloqueio relatassem os modos solicitado e resultante , mas não é isso que temos. O arranjo atual torna praticamente impossível rastrear e combinar a aquisição e liberação de bloqueio.
Pode haver uma boa razão para relatar bloqueios dessa maneira. Se não atender às suas necessidades, você pode abrir um caso de suporte com a Microsoft ou criar um item de Feedback do Azure.
LAST_MODE
O misterioso
LAST_MODE
é algo que Erik Darling comentou antes . É omap_key
valor mais alto na lista de modos de bloqueio expostos porsys.dm_xe_map_values
:A estrutura de memória acessada via DMV (usando
sqlmin!CMapValuesTable
) é armazenada a partir do endereçosqlmin!XeSqlPkg::g_lock_mode
. Cada entrada de 16 bytes na estrutura contém omap_key
e um ponteiro para a string retornadamap_value
pelo streaming TVF.As strings são armazenadas exatamente como mostrado na tabela acima (embora não nessa ordem). Parece ser um erro que a entrada 21 tenha um
map_value
"LAST_MODE" em vez do esperado "RX_X". Erik Darling relatou o problema no Azure Feedback .