Como IDENTITY_INSERT afeta a simultaneidade?

11

Estou tentando ajudar um cliente com um complemento SAP de terceiros que tenha uma falha de publicação e ficou sem suporte.

Sob certas circunstâncias, ele arquiva e lança mensagens incompletas da tabela da fila de lançamentos na tabela de arquivamento de lançamentos. Preciso mover esses resultados arquivados de volta para a fila.

O ID da fila é uma coluna de identidade e eu gostaria de manter o mesmo.

A questão é: se eu inserir a identidade em / inserir / inserir a identidade, o que posso esperar em relação à simultaneidade com processos que criam as entradas da fila e esperamos que a coluna de identidade seja gerada automaticamente?

Quaisquer indicações sobre a melhor maneira de demonstrar esse comportamento também serão muito apreciadas.

Metáfora
fonte

Respostas:

8

A configuração IDENTITY_INSERT ONpor si só não elimina a simultaneidade - isso não coloca nenhum bloqueio exclusivo na tabela, apenas um bloqueio breve de estabilidade do esquema (Sch-S).

Então, o que poderia teoricamente acontecer, sob o comportamento padrão, é que você poderia fazer isso na sessão 1:

BEGIN TRANSACTION;

-- 1
SET IDENTITY_INSERT dbo.tablename ON;

-- 2
INSERT dbo.tablename(id, etc) VALUES(100, 'foo'); -- next identity is now 101

-- 3
INSERT dbo.tablename(id, etc) VALUES(101, 'foo'); -- next identity is now 102

-- 4
SET IDENTITY_INSERT dbo.tablename OFF;

COMMIT TRANSACTION;

Em outra sessão, você pode inserir linhas na tabela nos pontos 1, 2, 3 ou 4. Isso pode parecer uma coisa boa, exceto o que acontece com qualquer inserção que ocorre entre 2 e 3, é que o valor gerado automaticamente disparou em outra sessão é baseada nos resultados da instrução 2 - portanto, ela gerará uma 101 e, em seguida, a instrução 3 falhará com uma violação de chave primária. Isso é bastante simples de configurar e testar você mesmo com alguns WAITFORs:

-- session 1
-- DROP TABLE dbo.what;
CREATE TABLE dbo.what(id INT IDENTITY PRIMARY KEY);
GO
BEGIN TRANSACTION;

SET IDENTITY_INSERT dbo.what ON;

INSERT dbo.what(id) VALUES(32);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(33);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(34);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(35);
WAITFOR DELAY '00:00:05';
INSERT dbo.what(id) VALUES(36);

SET IDENTITY_INSERT dbo.what OFF;

COMMIT TRANSACTION;

Depois que o lote iniciar, inicie-o em outra janela:

-- session 2
INSERT dbo.what DEFAULT VALUES;
WAITFOR DELAY '00:00:01';
GO 20

A sessão 2 só deve inserir valores de 1 a 20, certo? Exceto que, como a identidade subjacente foi atualizada pelo seu manual insere a sessão 1, em algum momento a sessão 2 continuará onde a sessão 1 parou e inserirá 32, 33 ou 34 etc. Isso será permitido, mas a sessão 1 falhará na próxima inserção com uma violação de PK (qual deles vence pode ser apenas uma questão de tempo).

Uma maneira de solucionar isso é invocar um TABLOCKna primeira inserção:

INSERT dbo.what WITH (TABLOCK) (id) VALUES(32);

Isso bloqueará qualquer outro usuário que tentar inserir (ou fazer alguma coisa, realmente) nesta tabela até que você termine de mover essas linhas arquivadas de volta. Isso limita a simultaneidade, com certeza, mas é dessa maneira que você deseja que o bloqueio funcione. E espero que isso não ocorra a um ritmo tão frequente em que você bloqueia outras pessoas o tempo todo.

Algumas outras soluções alternativas:

  • pare de se preocupar com o IDENTITYvalor gerado. Quem se importa? Use a UNIQUEIDENTIFIER(talvez gerado em uma tabela separada com IDENTITYum substituto) se o valor original for muito importante.
  • altere o processo de arquivamento para usar uma "exclusão reversa" onde algo é marcado como arquivado inicialmente e o arquivamento não é tornado permanente até uma data posterior. Qualquer processo que esteja tentando movê-los de volta pode simplesmente executar uma atualização direta e corrigir o sinalizador de exclusão suave.
Aaron Bertrand
fonte
Obrigado pela sua explicação. Estou escrevendo um utilitário independente para ajudar com um produto sem suporte que cria esses campos de identidade. Não tenho controle sobre como funciona.
Metaphor