Ask HN : j’ai réellement subi une collision d’UUID v4...
(news.ycombinator.com)- La base de données a détecté aujourd’hui un UUID v4 en double, et la valeur existante était exactement identique à
b6133fd6-70fe-4fe3-bed6-8ca8fc9386cd, déjà attribuée à un enregistrement ajouté en 2025 - Le paquet utilisé est uuid sur npm, avec une génération effectuée via
import { v4 as uuidv4 } from "uuid";puisconst document_id = uuidv4();avant insertion dans la base de données - La base ne contient qu’environ 15 000 enregistrements, ce qui semble statistiquement impossible, et l’auteur demande si quelqu’un a déjà vécu la même chose
1 commentaires
Avis Hacker News
jandrewrogers : C’est étonnamment courant. La sécurité de l’UUIDv4 repose sur l’hypothèse qu’on dispose d’une source d’entropie de haute qualité, mais cette hypothèse est facilement invalidée par des défauts matériels, des bugs logiciels ordinaires ou une mauvaise compréhension de l’entropie par les développeurs
Détecter qu’une source d’entropie est défaillante coûte assez cher, donc presque personne ne le fait, et au final on ne s’en rend compte qu’après une collision. C’est pourquoi l’UUIDv4 est explicitement interdit dans beaucoup de systèmes à haute fiabilité ou à fortes garanties
Plus on a de sources d’entropie, mieux c’est, et une bonne partie d’entre elles devrait être non déterministe. Même dans un petit jeu, si on mélange dans la seed initiale des valeurs comme les coordonnées de la souris, l’intervalle entre les clics ou le nombre d’images avant d’appuyer sur le bouton de démarrage, il devient beaucoup plus difficile de prédire le résultat, même si on utilise ensuite un générateur pseudo-aléatoire en interne. Si CloudFlare utilisait moins de 100 sources d’entropie, je serais déçu
Ça arrive quand on ne vérifie pas la valeur de retour du type « j’ai demandé N octets mais seulement 3 ont été renvoyés, donc il faut redemander les N-3 octets », comme dans l’ancien écosystème Go. Sur la plupart des machines ou systèmes d’exploitation, ça ne se produit pas, donc les gens ne vérifient pas, puis un jour ça explose en production avec des dizaines de milliers de collisions
throwaway_19sz : C’est difficile à croire et assez drôle, mais c’est vrai. Il y a dix ans, un ami a rejoint une startup en hypercroissance comme CTO, une boîte avec environ 200 développeurs, et dès sa première semaine il a découvert qu’il existait un microservice dédié à la génération d’UUID
Il y avait trois ingénieurs affectés à cet unique endpoint, plus même un DBA. Chaque équipe devait appeler ce service quand elle avait besoin d’un nouvel UUID « sûr » ; le service générait un UUID, vérifiait dans sa propre base s’il avait déjà été émis, l’insérait si ce n’était pas le cas, puis le renvoyait. Peut-être que c’était pour se rassurer, mais l’équipe avait aussi son propre tableau Kanban et ses propres sprints
Dans une startup rejointe plus tard, chaque nouvelle inquiétude imaginée par quelqu’un donnait naissance à un nouveau microservice et à une nouvelle équipe. L’objectif trimestriel consistait explicitement à augmenter la taille des équipes d’ingénierie, et des équipes de 3 ou 4 personnes se fabriquaient elles-mêmes du travail via leurs sprints et réunions de planification. J’ai proposé de déplacer des effectifs de projets stables vers des urgences, mais on m’a bloqué en disant que cela entrait en conflit avec des KPI imposant d’atteindre un certain nombre d’ingénieurs
Pour la haute disponibilité et le déploiement mondial, on peut aussi sharder en donnant à chaque instance une plage d’ID dédiée. Il suffit de réserver quelques bits de poids fort pour l’ID du datacenter, puis quelques autres pour l’instance du générateur dans ce datacenter. Attends, j’ai l’impression d’avoir déjà vu ça quelque part… Je me demande si Twitter utilise toujours cette approche, ou s’ils ont fini par en changer
Chaque jour, on récupérait un dump de base pour vérifier les ID « temporaires », et ils ne devenaient « définitifs » qu’après soumission correcte au CMDB. Il y avait aussi des garde-fous pour empêcher qu’un ID temporaire passe en prod, ainsi qu’une procédure de recyclage des ID définitifs non utilisés. La dernière fois que j’en ai entendu parler, ils en étaient à leur 18e mois sur un projet prévu pour 6 mois visant à déplacer le cache de la base locale vers Zookeeper
CodesInChaos : En général, ça vient d’un générateur pseudo-aléatoire mal seedé. Savoir si l’UUID a été créé côté back-end ou côté front-end est important
Le front-end est fondamentalement difficile à considérer comme fiable pour plusieurs raisons, y compris les collisions intentionnelles, donc il faut gérer les collisions. Le back-end peut, lui, être fiable. Par le passé, on voyait ça dans les VM, mais aujourd’hui ça devrait être réglé ; en revanche, si un processus fortement sandboxé utilise une voie de repli non sûre pour l’aléatoire, ça peut encore arriver. Un fork de processus ou de VM peut aussi produire des collisions par duplication d’état
kst : Ça me rappelle un passage de « Pro Git ». <https://git-scm.com/book/en/v2>
L’exemple disait que si les 6,5 milliards d’humains sur Terre produisaient tous, chaque seconde, un volume de code équivalent à l’historique complet du noyau Linux et le poussaient dans un unique dépôt Git géant, il faudrait environ 2 ans pour atteindre 50 % de probabilité de collision d’objets SHA-1. J’aimais bien la formule disant qu’une collision naturelle SHA-1 était moins probable que le fait que tous les membres de l’équipe meurent la même nuit dans des attaques de loups sans lien entre elles. Les hashes SHA-1 ne sont pas des nombres aléatoires et ils font 160 bits, donc ce n’est pas la même chose qu’un UUIDv4, mais j’aime bien cette image des attaques de loups sans lien entre elles
L’analogie dit en gros que si on faisait le tour de la Terre à l’équateur à raison d’un pas tous les milliards d’années, qu’à chaque tour on retirait une goutte d’eau du Pacifique, puis qu’une fois l’océan vide on empilait une feuille de papier, et qu’on répétait jusqu’à atteindre le Soleil, les trois premiers chiffres du minuteur de 52! secondes n’auraient toujours pas changé
e12e : Il y a une discussion connexe ici : https://github.com/uuidjs/uuid/issues/546
On y lit par exemple que
crypto.getRandomValues()testé dans googlebot était déterministeadyavanapalli : Ce dont on parle est tellement rare qu’à cet instant précis, il est plus probable que toute la Terre soit détruite par un astéroïde
J’ai entendu parler d’une femme réellement frappée par une météorite mais qui a survécu avec une blessure à la jambe. Si une collision UUID s’est produite, il est de loin plus probable qu’elle vienne d’un bug logiciel ou d’un comportement anormal de l’ordinateur, voire d’un rayon cosmique. Les rayons cosmiques perturbent la mémoire ou le CPU plus souvent qu’on ne l’imagine
juancn : Le générateur aléatoire n’est-il pas mal initialisé, ou manque-t-il d’entropie ? Si rien n’a été personnalisé, il utilise
crypto.getRandomValues(rnds8), etgetRandomValuesne précise pas de quantité minimale d’entropieGeee : Selon l’interprétation des mondes multiples de la mécanique quantique, il doit exister quelque part une branche de l’univers où tous les UUID sont identiques. J’imagine ce que doivent penser les gens là-bas
mittermayr : Je suis tout à fait d’accord : ça n’a pas de sens. Mais si je devais deviner, je dirais qu’avant les UUIDv4 étaient générés sur le téléphone de l’utilisateur puis envoyés à la base, alors que celui qui est entré en collision ce matin a été généré sur un serveur Ubuntu
Je ne sais pas exactement comment un UUIDv4 est généré ni si les caractéristiques de la machine génératrice influent sur l’algorithme, mais le seul changement qui me vienne à l’esprit est qu’avant ils étaient créés sur l’appareil et que depuis quelques mois ils le sont sur le serveur
Mais côté serveur, surtout en 2026, ça ne devrait pas arriver. Autrefois, le seed aléatoire des VM posait problème, mais aujourd’hui ça devrait être moins le cas. Même si un seul des UUID a été mal généré, la probabilité qu’un UUID vraiment aléatoire tombe dessus reste extrêmement faible ; il faut donc probablement un problème des deux côtés
dweez : Il est temps de relire cet article amusant : https://jasonfantl.com/posts/Universal-Unique-IDs/
Si on transformait tout l’univers en immense ordinateur dédié à générer des UUID jusqu’à la mort thermique, de combien de bits l’espace d’identifiants devrait-il disposer ?
beejiu : Je me demande si les UUID sont générés côté client ou côté serveur. Si c’est côté client, ça peut venir de bots de crawl. Par exemple, Googlebot exécute le JavaScript avec un « aléatoire » déterministe
Ce type d’explication est de plusieurs ordres de grandeur plus plausible qu’une vraie collision purement aléatoire
merlindru : Il y a de fortes chances que ce soit un problème de seed. Si vous pouvez prouver que ce n’est pas le cas, vous deviendrez peut-être un peu célèbre
erlkonig : Je répète toujours à mon équipe qu’avec suffisamment de données, des valeurs aléatoires peuvent finir par entrer en collision, et qu’à ce moment-là on découvre à quel point le logiciel est robuste
Pourtant, beaucoup de développeurs expérimentés, de leads et même de CIO croient encore que c’est impossible et n’écrivent aucun code pour gérer ce cas. Un mauvais générateur aléatoire peut alors casser le système bien plus tôt que prévu, avec même une corruption concurrente sans détection ni régénération. Ça me rappelle les gens qui ne vérifient pas si
malloc()a réussi. Je leur demande souvent : « si c’est impossible, n’est-ce pas qu’on utilise trop de bits ? »leni536 : Ce n’est pas arrivé par hasard ; il y a un bug quelque part. À première vue, le package semble appeler
crypto.randomUUID()du runtime JS, qui est censé être correctement seedé en permanenceUn bug du runtime semble extrêmement improbable, mais on ne sait jamais. Je suis curieux de savoir quel runtime JS est utilisé
jbverschoor : La cause la plus plausible serait que le package aléatoire dont dépend
uuida été compromis récemment pour rendre les nombres « aléatoires » prévisibles. Dans ce cas, une attaque supply chain pourrait mettre en danger de nombreux projets liés à la cryptographie, au SSL ou à la téléphonieuuid/src/rng.ts, le tableau aléatoire est devenuconst. Tous les appels partagent désormais le même tableau aléatoireLes appels suivants écrasent donc l’aléatoire généré précédemment ; si vous avez créé quelque chose d’important, bonne chance. Avant, le code utilisait
slice()pour faire une nouvelle copie. C’est peut-être involontaire, mais je ne vois pas comment ça a pu passer alors qu’un simple test générant deux nombres aléatoires et vérifiant qu’ils diffèrent n’aurait probablement même pas réussipif : Même avec une source d’entropie de haute qualité, on ne peut pas transformer un « probablement » en « nécessairement ». Si vous avez besoin d’une valeur difficile à deviner, regardez du côté de la cryptographie ; mais si vous avez besoin d’une unicité garantie, il faut la construire vous-même
athrowaway3z : Une règle empirique simple est de se demander si l’on peut mettre un timestamp dans l’ID en plus de l’aléatoire. Dans la plupart des cas, la réponse est oui, et UUIDv7 suffit
Si vous avez étudié le problème assez en profondeur pour pouvoir démontrer que la fuite d’information n’est pas acceptable, félicitations : votre système est probablement assez complexe et assez lent pour utiliser un hash cryptographique fort, ou simplement UUIDv5 si vous n’avez pas envie de vous compliquer la vie
darqis : PostgreSQL 18 prend en charge uuidv7 nativement, et on peut simplement mettre
uniqueetuuid7()comme valeur par défauttumdum_ : C’est un générateur pseudo-aléatoire mal seedé
serf : On parle d’environ 1 sur 4,72 × 10²⁸, soit 1 sur 47,3 octillions. Si c’est réel, avant d’acheter un billet de loterie, je soupçonnerais d’abord une condition de course ou une autre erreur simple
evnix : Même sans parler des maths de la probabilité, la réalité dans laquelle on vit fait que même les meilleurs générateurs matériels donnent parfois quelque chose de moins aléatoire qu’on ne l’imagine
Là où la sécurité n’est pas cruciale, je passerais à quelque chose comme TSID, ou à uuidv7, afin d’éliminer pratiquement ce genre de problème en pratique. Ça me paraît préférable à surconcevoir le code autour des retries
jordiburgos : J’aimerais qu’on évite d’utiliser
b6133fd6-70fe-4fe3-bed6-8ca8fc9386cd. J’ai vérifié ma base et il était déjà pris16b55183-1697-496e-bc8a-854eb9aae0f3, et probablement d’autres aussi. Si tout le monde poste sa liste ici, on devrait pouvoir vérifier les doublons, non ?pyuser583 : Je me demande quel UUID est aujourd’hui le plus recommandé
smokel : Il m’est arrivé plusieurs fois d’accuser le compilateur, les rayons cosmiques, les effets quantiques ou au moins un obscure bug du noyau, avant de finir par comprendre que le bug venait de moi
Une collision sur 15 000 enregistrements, c’est bien trop improbable ; je suspecterais d’abord d’autres causes : gestion des doublons, requêtes retransmises, objets réutilisés, logs trompeurs, réutilisation d’identifiants dans un autre chemin de code, etc. Si vous partagez un peu plus du code autour, on peut regarder ensemble
wazoox : Ça ne m’est pas encore arrivé, mais il y a deux jours j’ai trouvé ça au fin fond d’une base de code PHP en production : une fonction
createUUID()qui tronquaitmd5(uniqid('', true))et le recollait pour lui donner la forme d’un UUIDJe ne sais pas comment cette horreur n’a pas encore fini par nous mordre là où ça fait mal
sedatk :
uuidjs/uuidavertit qu’il peut générer des UUID dupliqués sur des clients à générateur aléatoire déterministe comme GooglebotLe message dit que cela peut poser problème aux applications qui attendent que des UUID générés côté client soient toujours uniques ; il faut donc vérifier les doublons et échouer proprement, ou désactiver les écritures pour les clients Googlebot : https://github.com/uuidjs/uuid/commit/91805f665c38b691ac2cbd...
xyzzy123 : Sur un système distribué sous Linux, un test de charge de longue durée a déjà échoué chez nous à cause d’UUID dupliqués
Après une longue enquête, c’était un bug du noyau, plus précisément une condition de course. Sur un système multiprocesseur, si deux processus lisaient
/dev/randomexactement en même temps, ils pouvaient très rarement — environ une fois sur un million — recevoir les mêmes octets. Je commencerais par regarder l’initialisation du générateur aléatoirebaq : On dirait qu’une VM en cours d’exécution a virtualisé toute l’entropie jusqu’à la faire disparaître
glaslong : Il faut acheter des lampes à lave
0xfffafaCrash : Je suis curieux de savoir si l’UUID a été généré côté front-end ou côté back-end. Si c’est côté front-end, je miserais moins sur un problème d’entropie que sur la possibilité que du code client ou une requête ait été manipulé pour injecter un UUID déjà connu
latentframe : L’une des phrases les plus dangereuses en ingénierie est statistiquement impossible. À suffisamment grande échelle, les cas extrêmes cessent d’être théoriques et deviennent des incidents de production
8organicbits : J’ai écrit l’an dernier sur une vraie collision, avec la bibliothèque concernée : https://alexsci.com/blog/uuid-oops/
Les UUID n’ont une résistance aux collisions que si l’on respecte strictement de nombreuses contraintes, et dans ce cas-ci il semble très probable que le problème vienne du générateur aléatoire
nu11ptr : Au fond, c’est un problème de source d’entropie. C’est pour ça que je génère et j’insère toujours dans une boucle. S’il y a collision, on peut la gérer proprement
sbuttgereit : Ce n’est pas « techniquement impossible ». C’est techniquement possible. Avec une bonne source d’aléatoire, c’est simplement très, très improbable, mais rien dans UUIDv4 n’empêche techniquement la génération de doublons
beardyw : Question peut-être bête, mais pourquoi ne pas ajouter la date, ne serait-ce qu’en secondes hexadécimales ? Avec juste quelques octets de plus, on aurait l’impression de garantir que ce qui est acceptable aujourd’hui le restera à l’avenir
mdavid626 : Il peut aussi y avoir d’autres explications. Par exemple, quelqu’un a pu bricoler la requête à la main ou intervenir dans la base
radial_symmetry : Ça m’est arrivé une fois et j’ai cru devenir fou, mais lire ces commentaires me rassure
NKosmatos : Ce n’est pas « techniquement impossible ». Ce n’est pas impossible ; c’est juste très, très improbable. Il faudrait peut-être acheter un billet de loterie
Chaque fois que je vois le mot « improbable », je pense à https://hitchhikers.fandom.com/wiki/Infinite_Improbability_D...
sudb : Pour l’un de mes projets, j’ai pour la première fois l’impression que choisir CUID2 était vraiment une bonne idée : https://github.com/paralleldrive/cuid2