TL; DR: La question ci-dessous se résume à: lors de l'insertion d'une ligne, existe-t-il une fenêtre d'opportunité entre la génération d'une nouvelle Identity
valeur et le verrouillage de la clé de ligne correspondante dans l'index clusterisé, où un observateur externe pourrait voir un plus récent Identity
valeur insérée par une transaction simultanée? (Dans SQL Server.)
Version détaillée
J'ai une table SQL Server avec une Identity
colonne appelée CheckpointSequence
, qui est la clé de l'index cluster de la table (qui possède également un certain nombre d'index non cluster supplémentaires). Les lignes sont insérées dans la table par plusieurs processus et threads simultanés (au niveau de l'isolement READ COMMITTED
et sans IDENTITY_INSERT
). En même temps, il existe des processus qui lisent périodiquement des lignes de l'index cluster, ordonnées par cette CheckpointSequence
colonne (également au niveau d'isolement READ COMMITTED
, l' READ COMMITTED SNAPSHOT
option étant désactivée).
Je compte actuellement sur le fait que les processus de lecture ne peuvent jamais "sauter" un point de contrôle. Ma question est: puis-je compter sur cette propriété? Et sinon, que pourrais-je faire pour que ce soit vrai?
Exemple: lorsque des lignes avec les valeurs d'identité 1, 2, 3, 4 et 5 sont insérées, le lecteur ne doit pas voir la ligne avec la valeur 5 avant de voir celle avec la valeur 4. Les tests montrent que la requête, qui contient une ORDER BY CheckpointSequence
clause ( et une WHERE CheckpointSequence > -1
clause), bloque de manière fiable chaque fois que la ligne 4 doit être lue, mais pas encore validée, même si la ligne 5 a déjà été validée.
Je crois qu'au moins en théorie, il peut y avoir une condition de race ici qui pourrait faire rompre cette hypothèse. Malheureusement, la documentation sur Identity
ne dit pas grand-chose sur le Identity
fonctionnement dans le contexte de plusieurs transactions simultanées, elle dit seulement "Chaque nouvelle valeur est générée en fonction de la valeur de départ et de l'incrément". et "Chaque nouvelle valeur pour une transaction particulière est différente des autres transactions simultanées sur la table." ( MSDN )
Mon raisonnement est, cela doit fonctionner en quelque sorte comme ceci:
- Une transaction est lancée (explicitement ou implicitement).
- Une valeur d'identité (X) est générée.
- Le verrou de ligne correspondant est pris sur l'index clusterisé en fonction de la valeur d'identité (sauf si l'escalade de verrous entre en jeu, auquel cas la table entière est verrouillée).
- La ligne est insérée.
- La transaction est validée (peut-être beaucoup de temps plus tard), donc le verrou est à nouveau retiré.
Je pense qu'entre les étapes 2 et 3, il y a une toute petite fenêtre où
- une session simultanée pourrait générer la prochaine valeur d'identité (X + 1) et exécuter toutes les étapes restantes,
- permettant ainsi à un lecteur venant exactement à ce moment de lire la valeur X + 1, sans la valeur de X.
Bien sûr, la probabilité de cela semble extrêmement faible; mais encore - cela pourrait arriver. Ou est-ce possible?
(Si vous êtes intéressé par le contexte: il s'agit de l'implémentation du moteur de persistance SQL de NEventStore. NEventStore implémente un magasin d'événements à ajouter uniquement où chaque événement obtient un nouveau numéro de séquence de point de contrôle croissant. Les clients lisent les événements du magasin d'événements classés par point de contrôle. afin d'effectuer des calculs de toutes sortes. Une fois qu'un événement avec le point de contrôle X a été traité, les clients ne prennent en compte que les événements "plus récents", c'est-à-dire les événements avec le point de contrôle X + 1 et plus. Par conséquent, il est essentiel que les événements ne puissent jamais être ignorés, car ils ne seraient plus jamais pris en compte. J'essaie actuellement de déterminer si l' Identity
implémentation de point de contrôle basée sur cette condition répond à cette exigence. Ce sont les instructions SQL exactes utilisées : schéma , requête du rédacteur ,Requête du lecteur .)
Si j'ai raison et que la situation décrite ci-dessus pourrait survenir, je ne vois que deux options pour y faire face, qui ne sont pas satisfaisantes:
- Lorsque vous voyez une valeur de séquence de point de contrôle X + 1 avant d'avoir vu X, ignorez X + 1 et réessayez plus tard. Cependant, parce que cela
Identity
peut bien sûr produire des lacunes (par exemple, lorsque la transaction est annulée), X pourrait ne jamais arriver. - Donc, même approche, mais acceptez l'écart après n millisecondes. Cependant, quelle valeur de n dois-je supposer?
De meilleures idées?