Une transaction regroupe plusieurs commandes SQL en une seule unité logique. Si toutes réussissent, COMMIT rend leurs effets durables. Si une étape échoue ou si une règle métier n’est plus satisfaite, ROLLBACK annule l’ensemble.
Pourquoi regrouper les opérations ?
Imaginez un transfert entre deux comptes. Débiter le premier sans créditer le second laisserait la base dans un état incohérent. Les deux modifications doivent réussir ou échouer ensemble.
BEGIN;
UPDATE accounts
SET balance = balance - 100
WHERE id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE id = 2;
COMMIT;Entre BEGIN et COMMIT, les commandes appartiennent à la même transaction. En cas de problème détecté par l’application, exécutez ROLLBACK au lieu de COMMIT.
Chaque commande est déjà transactionnelle
PostgreSQL exécute aussi une commande isolée dans une transaction implicite. Un bloc explicite devient nécessaire lorsque plusieurs commandes doivent partager le même résultat atomique ou la même vision cohérente des données.
Après une erreur SQL dans une transaction, PostgreSQL place généralement le bloc dans un état d’échec. Il faut alors l’annuler avant de poursuivre. Une application doit donc toujours prévoir le chemin de rollback et libérer correctement la connexion.
Revenir à un point intermédiaire
Un savepoint permet d’annuler une partie du travail sans abandonner tout le bloc :
BEGIN;
INSERT INTO orders (customer_id, status)
VALUES (42, 'pending');
SAVEPOINT before_optional_item;
INSERT INTO order_items (order_id, product_id, quantity)
VALUES (currval('orders_id_seq'), 99, 1);
ROLLBACK TO before_optional_item;
COMMIT;Cet exemple illustre la syntaxe, mais une application devrait récupérer l’identifiant créé avec RETURNING plutôt que dépendre du nom d’une séquence.
Comprendre l’isolation
Plusieurs transactions peuvent s’exécuter en même temps. Leur niveau d’isolation détermine quelles modifications concurrentes elles peuvent observer et quels conflits elles doivent gérer.
READ COMMITTED est le niveau par défaut de PostgreSQL. Chaque commande voit un instantané des données prises au début de cette commande. Deux lectures successives dans la même transaction peuvent donc observer des résultats différents si une autre transaction valide une modification entre les deux.
Des niveaux plus forts, comme REPEATABLE READ et SERIALIZABLE, offrent davantage de garanties mais peuvent provoquer des erreurs de sérialisation. L’application doit pouvoir relancer la transaction complète lorsque PostgreSQL signale ce type de conflit.
Bonnes pratiques
- gardez les transactions aussi courtes que possible ;
- n’attendez pas une saisie utilisateur ou un appel réseau lent au milieu du bloc ;
- verrouillez uniquement ce qui est nécessaire ;
- traitez explicitement les erreurs et exécutez un rollback ;
- concevez les opérations relançables lorsque l’isolation peut produire un conflit ;
- placez les invariants importants dans des contraintes de base de données.
Une transaction longue conserve des ressources et peut retarder d’autres opérations. Elle ne remplace ni les contraintes UNIQUE, CHECK ou de clé étrangère, ni une stratégie correcte de concurrence.
Questions fréquentes
COMMIT peut-il échouer ?
Oui. Des contraintes différées ou des conflits liés à l’isolation peuvent encore provoquer un échec. L’application ne doit annoncer le succès qu’après confirmation du commit.
Une transaction protège-t-elle plusieurs services ?
Une transaction PostgreSQL couvre les opérations effectuées dans la base concernée. Elle ne rend pas automatiquement atomiques un paiement externe, un email ou une écriture dans un autre système.
Faut-il toujours utiliser SERIALIZABLE ?
Non. Choisissez le niveau à partir des anomalies que le métier doit empêcher, puis gérez les éventuels conflits. Un niveau plus strict n’élimine pas le besoin de concevoir et tester les reprises.