12.4. Vérification de cohérence des données au niveau de
l'application
Comme les lecteurs ne verrouillent pas les données avec PostgreSQL™, quelque soit le niveau
d'isolation, les données lues par une transaction peuvent être
surchargées par une autre transaction concurrente. En d'autres
termes, si une ligne est renvoyée par
SELECT
, cela ne signifie pas que la
ligne est toujours courante au moment où elle est renvoyée
(c'est-à-dire, quelque temps après que la requête courante n'ait
commencé). La ligne pourrait avoir été modifiée ou supprimée par une
transaction validée après le lancement de cette requête. Même si la
ligne est toujours valide « maintenant », elle pourrait être modifiée ou
supprimée avant que la transaction courante ne soit validée ou
annulée.
Une autre façon de penser à ceci est que chaque transaction voit une
image du contenu de la base de données et les transactions
concurrentes en cours d'exécution pourraient très bien voir des
images différentes. Donc, le concept entier de « maintenant » est quelque peu mal défini. Ceci
n'est habituellement pas un gros problème si les applications
clientes sont isolées les unes des autres, mais si les clients
peuvent communiquer via des canaux externes à la base de données, de
sérieuses confusions pourraient survenir.
Pour s'assurer de la validité actuelle d'une ligne et pour la
protéger contre les modifications concurrentes, vous devez utiliser
SELECT FOR UPDATE
,
SELECT FOR SHARE
ou une
instruction
LOCK TABLE
appropriée (
SELECT FOR
UPDATE
et
SELECT FOR
SHARE
verrouillent uniquement les lignes renvoyées
contre les mises à jours concurrentes alors que
LOCK TABLE
verrouille la table
entière). Ceci doit être pris en compte lors du portage
d'applications vers PostgreSQL™ depuis d'autres
environnements.
Des vérifications globales de validité requièrent une réflexion
supplémentaire avec MVCC. Par
exemple, une application bancaire pourrait souhaiter vérifier que la
somme de tous les crédits d'une table est équivalente à la somme des
débits d'une autre table lorsque les deux tables sont activement
mises à jour. Comparer les résultats de deux commandes SELECT sum(...) successives ne fonctionnera pas
correctement avec le mode de lecture validée car la deuxième requête
a des chances d'inclure les résultats de transactions non
comptabilisées dans la première. Faire les deux sommes dans une seule
transaction sérialisée donnera une image plus précise des effets des
transactions validées avant le début de la transaction sérialisable
-- mais vous pourriez vous demander si la réponse est toujours juste
au moment où elle est fournie. Si la transaction sérialisable a
elle-même appliqué des modifications avant de tenter la vérification
de cohérence, l'utilité de la vérification devient bien plus
discutable car, maintenant, elle inclut des modifications apparues
après le début de la transaction mais pas toutes. Dans de tels cas,
une personne attentionnée pourrait souhaiter verrouiller toutes les
tables nécessaires à la vérification pour obtenir une image
indiscutable de la réalité. Un verrou de mode SHARE (ou plus important) garantit qu'il n'y a aucune
modification non validée dans la table verrouillée, autres que celles
de la transaction.
Il est à noter que si vous voulez vous reposer sur les verrous
explicites pour éviter les changements concurrents, vous devriez
utiliser le mode Read Commited, ou alors, dans le mode sérialisable,
être attentif à l'obtention des verrous avant d'effectuer des
requêtes. Un verrou obtenu par une transaction sérialisable garantie
qu'aucune autre transaction modifiant la table n'est en cours
d'exécution mais si l'image vue par la transaction est antérieure à
l'obtention du verrou, elle pourrait être antérieure aux quelques
modifications maintenant validées dans la table. Une image de la
transaction sérialisable est généralement gelée au début de la
première requête ou de la première commande de modification de
données (SELECT, INSERT, UPDATE ou DELETE). Du coup, il est possible d'obtenir des
verrous de façon explicite avant que l'image ne soit gelée.