Upsert et idempotence : ON CONFLICT
AvancéL'INSERT ... ON CONFLICT rend l'écriture atomique et rejouable : « insère, ou met à jour si elle existe déjà », sans course entre un SELECT et un INSERT.
INSERT INTO compteurs (cle, valeur)
VALUES ('visites', 1)
ON CONFLICT (cle)
DO UPDATE SET valeur = compteurs.valeur + EXCLUDED.valeur; -- EXCLUDED = la ligne proposée
INSERT INTO utilisateurs (email, nom)
VALUES ('alice@exemple.fr', 'Alice')
ON CONFLICT (email) DO NOTHING
RETURNING id; -- rien renvoyé si la ligne existait déjà
Clés d'idempotence#
Pour qu'un client puisse réessayer un paiement ou une création sans doublon, on enregistre une clé unique fournie par l'appelant :
CREATE TABLE paiements (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
cle_idempotence text NOT NULL UNIQUE,
montant numeric(12, 2) NOT NULL
);
INSERT INTO paiements (cle_idempotence, montant)
VALUES ($1, $2)
ON CONFLICT (cle_idempotence) DO NOTHING
RETURNING id;
Si aucune ligne n'est renvoyée, la requête a déjà été traitée : renvoyez le résultat précédent.
Piège :
SELECTpuisINSERTsi absent n'est pas atomique : deux requêtes concurrentes peuvent toutes deux ne rien trouver et insérer un doublon. Laissez la contrainteUNIQUEetON CONFLICTarbitrer.