¿Cómo afecta IDENTITY_INSERT a la concurrencia?

11

Estoy tratando de ayudar a un cliente con un complemento de SAP de terceros que tiene un error de publicación y se ha quedado sin soporte.

En determinadas circunstancias, archiva y publica de forma incompleta desde la tabla de la cola de publicación a la tabla de archivo de publicación. Necesito mover estos resultados archivados nuevamente a la cola.

La ID de la cola es una columna de identidad y me gustaría mantenerla igual.

La pregunta es, si hago identidad_insertar / insertar / identidad_insertar desactivado, ¿qué puedo esperar con respecto a la concurrencia con los procesos que crean las entradas de la cola y esperan que la columna de identidad se genere automáticamente?

Cualquier sugerencia sobre la mejor manera de demostrar tal comportamiento también sería muy apreciada.

Metáfora
fuente

Respuestas:

8

La configuración IDENTITY_INSERT ONpor sí sola no elimina la concurrencia: esto no coloca ningún bloqueo exclusivo en la mesa, solo un breve bloqueo de estabilidad del esquema (Sch-S).

Entonces, lo que teóricamente podría suceder, bajo el comportamiento predeterminado, es que podría hacer esto en la sesión 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;

En otra sesión, puede insertar filas en la tabla en los puntos 1, 2, 3 o 4. Esto puede parecer algo bueno, excepto que lo que sucede para cualquier inserción que ocurre entre 2 y 3 es que el valor generado automáticamente se activó otra sesión se basa en los resultados de la declaración 2, por lo que generará un 101, y luego la declaración 3 fallará con una violación de clave principal. Esto es bastante simple de configurar y probarse con algunos 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;

Una vez que ese lote ha comenzado, comience este lote en otra ventana:

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

La sesión 2 solo debe insertar valores del 1 al 20, ¿verdad? Excepto que debido a que la identidad subyacente ha sido actualizada por su sesión 1 de inserciones manuales, en algún momento la sesión 2 retomará donde dejó la sesión 1 e insertará 32, 33 o 34, etc. Se permitirá hacer esto, pero entonces la sesión 1 fallará en la siguiente inserción con una infracción de PK (la que gane puede ser solo cuestión de tiempo).

Una forma de solucionar esto es invocar a TABLOCKen la primera inserción:

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

Esto bloqueará a cualquier otro usuario que intente insertar (o hacer algo, realmente) con esta tabla hasta que haya terminado de mover esas filas archivadas hacia atrás. Esto acelera la concurrencia, claro, pero esta es la forma en que desea que funcione el bloqueo. Y con suerte esto no es algo que esté sucediendo a un ritmo tan frecuente en el que estás bloqueando a otras personas todo el tiempo.

Un par de otras soluciones:

  • deja de preocuparte por el IDENTITYvalor generado. ¿A quien le importa? Utilice un UNIQUEIDENTIFIER(tal vez generado en una tabla separada con un IDENTITYcomo sustituto) si el valor original es muy importante.
  • cambie el proceso de archivo para usar una "eliminación suave" donde algo se marca como archivado inicialmente y el archivo no se hace permanente hasta una fecha posterior. Entonces, sea cual sea el proceso que intente moverlos hacia atrás, simplemente puede realizar una actualización directa y corregir el indicador de eliminación suave.
Aaron Bertrand
fuente
Gracias por su explicación. Estoy escribiendo una utilidad independiente para ayudar con un producto fuera de soporte que crea estos campos de identidad. No tengo control sobre cómo funciona.
Metáfora