Un déploiement PostgreSQL en production a planté après qu'un développeur a ajouté un argument optionnel à une fonction existante avec CREATE OR REPLACE FUNCTION. Le changement a laissé deux fonctions portant le même nom, ce qui a provoqué l'erreur « function is not unique » de la base de données et une erreur 400 de l'API. Cet incident montre comment une seule erreur de migration peut corrompre silencieusement un schéma en production et pourquoi les vérifications au niveau du code ne suffisent pas.
Ce qui s'est mal passé
L'équipe devait étendre une procédure stockée avec un paramètre supplémentaire optionnel. Ils ont exécuté CREATE OR REPLACE FUNCTION …, supposant que cela écraserait l'ancienne définition. PostgreSQL ne remplace une fonction que lorsque la liste complète des arguments correspond exactement. Modifier la signature crée une toute nouvelle entrée de fonction tout en laissant l'originale intacte.
Comme le nouvel argument avait une valeur par défaut, les appelants qui fournissaient l'ancien nombre d'arguments pouvaient correspondre à l'une ou l'autre des définitions. PostgreSQL ne pouvait pas décider laquelle invoquer et a renvoyé l'erreur « function is not unique », qui s'est manifestée par une réponse 400 de l'API.
Le dépôt de code ne montrait qu'une seule définition, et un script personnalisé qui scannait l'arborescence source ne signalait aucun doublon. Le doublon n'existait que dans la base de données, introduit lorsqu'un ancien fichier de migration a été réexécuté sur le serveur.
Pourquoi la migration est passée entre les mailles du filet
La migration qui a ajouté le paramètre optionnel a simplement exécuté CREATE OR REPLACE FUNCTION. Lorsque la migration s'est exécutée une seconde fois — peut-être après un rollback ou lors d'un déploiement répété — la base de données a traité la commande comme un « ajout d'une nouvelle surcharge » plutôt que comme un « remplacement de l'existante ». La migration n'a pas vérifié l'état résultant, de sorte que le doublon a persisté sans être remarqué.
Le script qui vérifiait le dépôt examinait les fichiers sources, pas le schéma en direct. Il regardait par la porte d'entrée pendant que le bug entrait par la porte de derrière.
Les enjeux
Une seule fonction ambiguë peut paralyser n'importe quel service qui en dépend. La correction a consisté à effectuer un rollback de la transaction si le nombre de fonctions était incorrect et à notifier le cache du schéma pour qu'il se recharge.
Comment sécuriser les migrations
L'équipe a reconstruit la migration avec des vérifications explicites, la transformant en une opération auto-vérifiée :
- Démarrer une transaction pour que tout échec annule l'ensemble du changement.
- Supprimer explicitement l'ancienne fonction avant de créer la nouvelle version, garantissant qu'une seule définition existe.
- Créer la nouvelle fonction avec la signature souhaitée.
- Compter les fonctions portant le nom donné dans
pg_cataloget vérifier que le compte est exactement de un. - Effectuer un rollback de la transaction si le compte diffère, empêchant la persistance du doublon.
- Notifier le cache du schéma pour qu'il se recharge, garantissant que les requêtes suivantes voient la définition mise à jour.
En demandant à la base de données « quelles fonctions sont présentes ? » plutôt qu'en supposant que le code est correct, la migration devient fiable face aux exécutions répétées, aux déploiements partiels ou aux modifications manuelles.
Contre-argument : commodité vs sécurité
CREATE OR REPLACE FUNCTION est attrayant car il permet aux développeurs d'itérer rapidement sans écrire d'instructions DROP distinctes. Dans les environnements où les migrations ne sont exécutées qu'une seule fois, ce raccourci fonctionne parfaitement. Le risque apparaît lorsque les migrations sont rejouées — que ce soit à cause de pipelines CI qui réinitialisent les bases de données de test, de rollbacks automatisés ou de réapplications manuelles en production.
À retenir
Modifier la signature d'une fonction avec CREATE OR REPLACE ne garantit pas son remplacement — PostgreSQL créera silencieusement une surcharge si la liste des arguments diffère. Les environnements de production qui s'appuient sur des migrations doivent vérifier le schéma résultant, et pas seulement le code source. L'intégration de suppressions explicites, de vérifications transactionnelles et d'assertions post-migration transforme un raccourci pratique en un processus fiable et reproductible.
