PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 15.10 » Internes » Écrire un wrapper de données distantes » Le verrouillage de ligne dans les wrappers de données distantes

59.5. Le verrouillage de ligne dans les wrappers de données distantes

Si le mécanisme de stockage sous-jacent à un FDW a un concept de verrouillage individuel des lignes pour prévenir des mises à jour concurrentes de ces lignes, il est généralement intéressant pour le FDW d'effectuer des verrouillages de niveau ligne avec une approximation aussi proche que possible de la sémantique utilisée pour les tables ordinaires de PostgreSQL. Ceci implique de multiples considérations.

Une décision clef à prendre est si il vaut mieux effectuer un verrouillage précoce ou un verrouillage tardif. Dans le verrouillage précoce, une ligne est verrouillée lorsqu'elle est récupérée pour la première fois à partir du stockage sous-jacent, alors qu'avec le verrouillage tardif, la ligne est verrouillée seulement lorsque le besoin est connu et nécessaire. (La différence survient parce que certaines lignes peuvent être abandonnées par des restrictions vérifiées localement ou des conditions de jointure.) Le verrouillage précoce est beaucoup plus simple et évite des allers-retours supplémentaires vers le stockage distant, mais il peut entraîner des verrouillages de lignes qui n'auraient pas eu besoin de l'être, résultant en une réduction de la concurrence voire même des deadlocks inattendus. De plus, le verrouillage tardif n'est possible seulement que si la ligne à verrouiller peut être identifiée de manière unique à nouveau plus tard. Idéalement, l'identifiant de ligne devrait identifier une version spécifique de la ligne, comme les TID de PostgreSQL le font.

Par défaut, PostgreSQL ignore les considérations de verrouillage lorsqu'il s'interface avec les FDW, mais un FDW peut effectuer un verrouillage précoce sans un support explicite du code du serveur principal. Les fonctions de l'API décrites dans le Section 59.2.6, qui ont été ajoutées dans la version 9.5 de PostgreSQL, autorise un FDW à utiliser un verrouillage tardif si il le désire.

Une considération supplémentaire est que dans le niveau d'isolation READ COMMITTED, PostgreSQL peut avoir besoin de vérifier à nouveau les restrictions et conditions de jointures avec une version mise à jour de certaines lignes. Vérifier à nouveau des conditions de jointure requiert d'obtenir à nouveau des copies des lignes non ciblées qui étaient auparavant jointes à la ligne cible. En travaillant avec des tables standards PostgreSQL, ceci est effectué en incluant les TID des tables non ciblées dans la liste des colonnes projetées via la jointure, puis en récupérant à nouveau les lignes non ciblées si nécessaire. Cette approche maintient l'ensemble des données jointes compact, mais il demande une capacité peu coûteuse de récupération à nouveau, ainsi qu'un TID qui peut identifier de manière unique la version de la ligne à récupérer à nouveau. Par défaut, donc, l'approche utilisée avec les tables distantes est d'inclure une copie de la ligne entière récupérée dans la liste de colonnes projetée via la jointure. Ceci n'impose rien au FDW mais peut entraîner des performances réduites des jointures par fusion ou hachage. Un FDW qui remplit les conditions pour récupérer à nouveau peut choisir de le faire.

Pour une commande UPDATE ou DELETE sur une table distante, il est recommandé que l'opération de ForeignScan sur la table cible effectue un verrouillage précoce sur les lignes qu'elle récupère, peut-être via un équivalent de la commande SELECT FOR UPDATE. Un FDW peut détecter si une table est la cible d'une commande UPDATE/DELETE lors de la planification en comparant son relid à root->parse->resultRelation, ou lors de l'exécution en utilisant la fonction ExecRelationIsTargetRelation(). Une possibilité alternative est d'effectuer un verrouillage tardif à l'intérieur des fonctions callback ExecForeignUpdate ou ExecForeignDelete, mais aucun support spécial n'est fourni pour cela.

Pour les tables distantes qui sont verrouillées par une commande SELECT FOR UPDATE/SHARE, l'opération ForeignScan peut encore effectuer un verrouillage précoce en récupérant des lignes avec l'équivalent de la commande SELECT FOR UPDATE/SHARE. Pour effectuer à la place un verrouillage tardif, fournissez les fonctions callback définies à Section 59.2.6. Dans GetForeignRowMarkType, sélectionner l'option rowmark ROW_MARK_EXCLUSIVE, ROW_MARK_NOKEYEXCLUSIVE, ROW_MARK_SHARE ou ROW_MARK_KEYSHARE en fonction de la force du verrouillage demandé. (Le code du serveur principal agira de la même manière indépendamment de l'option choisie parmi ces quatre options.) Ailleurs, vous pouvez détecter si une table distante a été verrouillée par ce type de commandes en utilisant la fonction get_plan_rowmark lors de la planification ou la fonction ExecFindRowMark lors de l'exécution ; vous devez vérifier non seulement si une structure rowmark non nulle est renvoyée, mais également que son champ strength n'est pas égal à LCS_NONE.

Enfin, pour les tables distantes qui sont utilisées dans une commande UPDATE, DELETE ou SELECT FOR UPDATE/SHARE sans demande de verrouillage de ligne, vous pouvez passer outre le choix par défaut de copier les lignes entières dans la fonction GetForeignRowMarkType en sélectionnant l'option ROW_MARK_REFERENCE lorsqu'elle voit comme valeur de puissance de verrouillage LCS_NONE. Ceci aura pour conséquence d'appeler RefetchForeignRow avec cette valeur pour le champ markType ; elle devrait alors récupérer à nouveau la ligne sans prendre aucun nouveau verrouillage. (Si vous avez une fonction GetForeignRowMarkType mais ne souhaitez pas récupérer à nouveau des lignes non verrouillées, sélectionnez l'option ROW_MARK_COPY pour LCS_NONE.)

Voir les commentaires dans src/include/nodes/lockoptions.h, pour RowMarkType et dans src/include/nodes/plannodes.h pour PlanRowMark, et les commentaires pour ExecRowMark dans src/include/nodes/execnodes.h pour des informations complémentaires.