Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Suite à un interblocage d'une seule transaction entre les versions de SQL Server

L'un des blocages les moins courants est celui où il y a un seul utilisateur qui se bloque lui-même sur une ressource système. Un récent que j'ai rencontré consiste à créer un type d'alias, puis à déclarer une variable de ce type, dans la même transaction. Imaginez que vous essayez d'exécuter un test unitaire ou un test de pré-déploiement, de vérifier les échecs et de revenir en arrière dans tous les cas afin de ne laisser aucune trace de ce que vous avez fait. Le modèle pourrait ressembler à ceci :

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
DECLARE @x TABLE (e EmailAddress);
GO
ROLLBACK TRANSACTION;

Ou, plus probablement, un peu plus complexe :

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = N'whatever';
GO
ROLLBACK TRANSACTION;

Le premier endroit où j'ai essayé ce code était SQL Server 2012, et les deux exemples ont échoué avec l'erreur suivante :

Msg 1205, Niveau 13, État 55, Ligne 14
La transaction (ID de processus 57) a été bloquée sur les ressources de verrouillage avec un autre processus et a été choisie comme victime du blocage. Réexécutez la transaction.

Et il n'y a pas grand-chose à apprendre du graphique des blocages :

En revenant quelques années en arrière, je me souviens quand j'ai découvert les types d'alias pour la première fois, dans SQL Server 2000 (quand ils s'appelaient Types de données définis par l'utilisateur). À ce moment-là, ce blocage que j'ai rencontré plus récemment ne se produirait pas (mais c'est au moins en partie parce que vous ne pouviez pas déclarer une variable de table avec un type d'alias - voir ici et ici). J'ai exécuté le code suivant sur SQL Server 2000 RTM (8.0.194) et SQL Server 2000 SP4 (8.0.2039), et tout s'est bien passé :

BEGIN TRANSACTION;
GO
EXEC sp_addtype @typename = N'EmailAddress', @phystype = N'VARCHAR(320)';
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT @param;
END
GO
EXEC dbo.foo @param = N'whatever';
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = @x;
GO
ROLLBACK TRANSACTION;

Bien sûr, ce scénario n'était pas très répandu à l'époque car, après tout, peu de gens utilisaient des types d'alias en premier lieu. Bien qu'ils puissent rendre vos métadonnées plus auto-documentées et similaires à la définition des données, ils sont une véritable galère si jamais vous souhaitez les modifier, ce qui peut faire l'objet d'un autre article.

SQL Server 2005 est arrivé et a introduit une nouvelle syntaxe DDL pour créer des types d'alias :CREATE TYPE . Cela n'a pas vraiment résolu le problème de la modification des types, cela a juste rendu la syntaxe un peu plus propre. Dans RTM, tous les exemples de code ci-dessus fonctionnaient très bien sans blocages. Dans le SP4, cependant, ils seraient tous bloqués. Par conséquent, quelque part entre RTM et SP4, ils ont modifié le traitement interne des transactions impliquant des variables de table utilisant des types d'alias.

Avance rapide de quelques années vers SQL Server 2008, où des paramètres table ont été ajoutés (voir un bon cas d'utilisation ici). Cela a rendu l'utilisation de ces types beaucoup plus répandue et a introduit un autre cas où une transaction essayant de créer et d'utiliser un tel type se bloquerait :

BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
DECLARE @r dbo.Items;
GO
ROLLBACK TRANSACTION;

J'ai vérifié Connect et trouvé plusieurs éléments connexes, l'un d'eux affirmant que ce problème a été résolu dans SQL Server 2008 SP2 et 2008 R2 SP1 :

Connect #365876 :un blocage se produit lors de la création d'un type de données défini par l'utilisateur et des objets qui l'utilisent

En fait, cela faisait référence au scénario suivant, dans lequel la simple création d'une procédure stockée faisant référence au type dans une variable de table entraînerait un blocage dans SQL Server 2008 RTM (10.0.1600) et SQL Server 2008 R2 RTM (10.50.1600) :

BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
ROLLBACK TRANSACTION;

Cependant, cela ne se bloque pas dans SQL Server 2008 SP3 (10.0.5846) ou 2008 R2 SP2 (10.50.4295). J'ai donc tendance à croire les commentaires sur l'élément Connect - que cette partie du bogue a été corrigée dans 2008 SP2 et 2008 R2 SP1, et n'a jamais été un problème dans les versions plus modernes.

Mais cela laisse toujours de côté la possibilité de soumettre réellement le type d'alias à toute sorte de véritable test. Ainsi, mes tests unitaires réussiraient tant que tout ce que je voulais faire était de tester que je pouvais créer la procédure - oubliez de déclarer le type en tant que variable locale ou en tant que colonne dans une variable de table locale.

La seule façon de résoudre ce problème consiste à créer le type de table avant de démarrer la transaction et à le supprimer explicitement par la suite (ou à le diviser en plusieurs transactions). Cela pourrait être extrêmement lourd, voire impossible, d'avoir des cadres et des harnais de test souvent automatisés qui modifient complètement leur mode de fonctionnement pour tenir compte de cette limitation.

J'ai donc décidé de passer par quelques tests dans les versions initiales et les plus récentes de toutes les versions majeures :SQL Server 2005 RTM, 2005 SP4, 2008 RTM, 2008 SP3, 2008 R2 RTM, 2008 R2 SP2, 2012 RTM, 2012 SP1, et 2014 CTP2 (et oui, je les ai tous installés). J'avais passé en revue plusieurs éléments de Connect et divers commentaires qui m'ont amené à me demander quels cas d'utilisation étaient pris en charge et où, et j'avais une étrange envie de savoir quels aspects de ce problème avaient réellement été résolus. J'ai testé divers scénarios de blocage potentiels impliquant des types d'alias, des variables de table et des paramètres de table par rapport à toutes ces versions ; le code est le suivant :

/* 
  alias type - declare in local table variable 
  always deadlocks on 2005 SP4 -> 2014, except in 2005 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320)
GO
DECLARE @r TABLE(e EmailAddress);
GO
ROLLBACK TRANSACTION;
 
 
/* 
  alias type - create procedure with param & table var 
  sometimes deadlocks - 2005 SP4, 2008 RTM & SP1, 2008 R2 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
ROLLBACK TRANSACTION;
 
 
/* 
  alias type - create procedure, declare & exec 
  always deadlocks on 2005 SP4 -> 2014, except on 2005 RTM
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE EmailAddress FROM VARCHAR(320);
GO
CREATE PROCEDURE dbo.foo 
  @param EmailAddress 
AS
BEGIN 
  SET NOCOUNT ON;
  DECLARE @x TABLE (e EmailAddress);
  INSERT @x SELECT @param;
END
GO
DECLARE @x EmailAddress;
SET @x = N'whatever';
EXEC dbo.foo @param = N'whatever';
GO
ROLLBACK TRANSACTION;
 
 
/* obviously did not run these on SQL Server 2005 builds */
 
/* 
  table type - create & declare local variable 
  always deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
DECLARE @r dbo.Items;
GO
ROLLBACK TRANSACTION;
 
 
/* 
  table type - create procedure with param and SELECT 
  never deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
CREATE PROCEDURE dbo.foo 
  @param dbo.Items READONLY
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT Item FROM @param;
END
GO
ROLLBACK TRANSACTION;
 
 
/* 
  table type - create procedure, declare & exec 
  always deadlocks on 2008 -> 2014
*/
 
BEGIN TRANSACTION;
GO
CREATE TYPE dbo.Items AS TABLE(Item INT);
GO
CREATE PROCEDURE dbo.foo 
  @param dbo.Items READONLY
AS
BEGIN 
  SET NOCOUNT ON;
  SELECT Item FROM @param;
END
GO
DECLARE @x dbo.Items;
EXEC dbo.foo @param = @x;
GO
ROLLBACK TRANSACTION;

Et les résultats reflètent mon histoire ci-dessus :SQL Server 2005 RTM n'a bloqué aucun des scénarios, mais au moment où le SP4 a été lancé, ils étaient tous bloqués. Cela a été corrigé pour le scénario "créer un type et créer une procédure", mais aucun des autres, dans 2008 SP2 et 2008 R2 SP1. Voici un tableau montrant tous les résultats :

Version de SQL Server / Build #
SQL 2005 SQL 2008 SQL 2008 R2 SQL 2012 SQL 2014
RTM
9.0.1399
SP4
9.0.5324
RTM
10.0.1600
SP3
10.0.5846
RTM
10.50.1600
SP2
10.50.4295
RTM
11.0.2100
SP1
11.0.3381
CTP2
12.0.1524
Type d'alias déclarer dans la table var
créer une procédure
création et exécution de la procédure
Type de tableau déclarer une variable locale N/A
créer une procédure
création et exécution de la procédure

Conclusion

Donc, la morale de l'histoire est qu'il n'y a toujours pas de solution pour le cas d'utilisation décrit ci-dessus, où vous voulez créer un type de table, créer une procédure ou une fonction qui utilise le type, déclarer un type, tester le module et rouler tout en arrière. Dans tous les cas, voici les autres éléments Connect que vous pouvez consulter ; nous espérons que vous pourrez voter pour eux et laisser des commentaires décrivant comment ce scénario de blocage affecte directement votre entreprise :

  • Connect #581193 :Créer un type de table et l'utiliser dans la même transaction provoque un blocage
  • Connect #800919 :Problème lors de la création d'une fonction avec le type de retour TableValue dans une transaction avec un type défini par l'utilisateur dans une table créée dans la même portée de transaction
  • Connect #804365 :un interblocage se produit lorsqu'un type de table défini par l'utilisateur est créé et utilisé dans une transaction

    Je m'attends à ce que des clarifications soient ajoutées à ces éléments Connect dans un proche avenir, bien que je ne sache pas exactement quand ils seront mis en place.