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 14La 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 | ||
déclarer dans la table var | ||||||||||
créer une procédure | ||||||||||
création et exécution de la procédure | ||||||||||
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 :
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.