Ask HN: Me pasó una colisión real de UUID v4...
(news.ycombinator.com)- La base de datos detectó hoy un UUID v4 duplicado, y el valor existente era exactamente igual a
b6133fd6-70fe-4fe3-bed6-8ca8fc9386cdde un registro agregado en 2025 - El paquete en uso es uuid de npm, y dice que lo genera con
import { v4 as uuidv4 } from "uuid";y luegoconst document_id = uuidv4();para insertarlo en la base de datos - La base de datos solo tiene alrededor de 15,000 registros, así que parece estadísticamente imposible, y pregunta si alguien más ha pasado por lo mismo
1 comentarios
Opiniones de Hacker News
jandrewrogers: Esto es sorprendentemente común. La seguridad de UUIDv4 depende de la suposición de que existe una fuente de entropía de alta calidad, y esa suposición se rompe con facilidad por fallas de hardware, bugs de software bastante comunes o porque los desarrolladores no entienden bien la entropía.
Detectar que una fuente de entropía está rota es bastante costoso, así que casi nadie lo hace, y al final uno se entera solo después de que ocurre una colisión. Por eso, en muchos sistemas de alta confiabilidad y alta garantía, UUIDv4 está explícitamente prohibido.
Cuantas más fuentes de entropía haya, mejor, y una buena parte de ellas debería ser no determinista. Incluso en juegos pequeños, si mezclas en la semilla inicial valores como las coordenadas del mouse, el intervalo entre pulsaciones de botones o la cantidad de frames antes de presionar iniciar, se vuelve mucho más difícil de predecir aunque internamente uses un generador seudoaleatorio. Si CloudFlare usara menos de 100 fuentes de entropía, me decepcionaría.
Eso pasa si no verificas valores de retorno del estilo “pediste N bytes pero solo se devolvieron 3, así que hay que volver a pedir los N-3 restantes”, como ocurría antes en el ecosistema de Go. Como en la mayoría del hardware o sistemas operativos casi nunca falla, la gente no lo revisa, y un día aparece en producción convertido en decenas de miles de colisiones.
throwaway_19sz: Suena a historia increíblemente absurda, pero es real. Hace 10 años, un amigo entró como CTO a una startup de alto crecimiento; la empresa tenía como 200 desarrolladores y en su primera semana descubrió que existía un microservicio dedicado exclusivamente a generar UUID.
Había 3 ingenieros asignados a un solo endpoint, e incluso una persona de base de datos. Si algún equipo necesitaba un UUID nuevo y “seguro”, tenía que llamar a ese servicio; el servicio generaba el UUID, revisaba en su propia base si ya existía uno igual emitido antes, y si no, lo insertaba y lo devolvía. No sé si era por tranquilidad mental, pero ese equipo hasta tenía su propio tablero kanban y sus sprints.
Después entré a otra startup donde cada vez que a alguien se le ocurría una nueva preocupación, nacían un nuevo microservicio y un nuevo equipo. Uno de los objetivos trimestrales era explícitamente aumentar el tamaño del equipo de ingeniería, y los equipos de 3 o 4 personas se inventaban trabajo dentro de sus propios sprints y reuniones de planeación. Propuse mover gente de proyectos estables a tareas urgentes, pero me lo bloquearon porque chocaba con un KPI que exigía aumentar el número de ingenieros hasta cierta cifra.
Para alta disponibilidad y despliegue global, también se puede shardear asignando rangos de ID dedicados a cada instancia. Reservas algunos bits altos para el ID del datacenter y unos cuantos bits para la instancia generadora dentro de ese datacenter. Espera, esto me suena conocido… Me pregunto si Twitter todavía usa este enfoque o si al final lo cambiaron.
Cada día recibían un dump de la base de datos para validarlo al generar IDs “temporales”, y recién quedaban “confirmados” después de enviarlos correctamente al CMDB. También había guardrails para que los IDs temporales no terminaran usándose en producción, y existía un proceso para reciclar IDs confirmados que no se habían usado. La última vez que supe, llevaban 18 meses en un proyecto de 6 meses para mover la caché local de la base de datos a Zookeeper.
CodesInChaos: Normalmente esto pasa por un generador seudoaleatorio con mala semilla. Importa si el UUID se generó en el backend o en el frontend.
El frontend es inherentemente difícil de confiar por muchas razones, incluidas colisiones intencionales, así que hay que manejar colisiones. El backend sí puede hacerse confiable. Antes esto pasaba con VM, pero hoy en día ya debería estar resuelto, aunque todavía puede ocurrir si un proceso muy aislado en sandbox usa una ruta alternativa de aleatoriedad no segura. También un fork de proceso o de VM puede producir colisiones al copiar el estado.
kst: Esto me recordó un pasaje de “Pro Git”. <https://git-scm.com/book/en/v2>
El ejemplo decía que, incluso si los 6,500 millones de personas del planeta produjeran cada segundo una cantidad de código equivalente a toda la historia del kernel de Linux y la hicieran push a un único repositorio gigante de Git, tardarías aproximadamente 2 años en llegar a una probabilidad del 50% de colisión de objetos SHA-1. Así que me gustaba la frase de que una colisión natural de SHA-1 es menos probable que que todos los miembros de tu equipo mueran la misma noche en ataques de lobos no relacionados entre sí. SHA-1 no es un número aleatorio y tiene 160 bits, así que es distinto de UUIDv4, pero me encanta la comparación de ataques de lobos no relacionados.
Es esa analogía de dar una vuelta a la Tierra cada mil millones de años caminando por el ecuador, quitar una gota de agua del Pacífico en cada vuelta, y cuando el océano se vacíe poner una hoja de papel; incluso repitiendo eso hasta llegar al Sol, los tres primeros dígitos del temporizador de 52! segundos no cambiarían.
e12e: Hay una discusión relacionada aquí: https://github.com/uuidjs/uuid/issues/546
Por ejemplo, mencionan que al probar
crypto.getRandomValues()con googlebot, este resultó ser determinista.adyavanapalli: Lo que están describiendo es tan raro que, en este mismo instante, sería más probable que la Tierra entera fuera destruida por un asteroide.
Tengo entendido que sí hubo una mujer que fue alcanzada por un meteorito y sobrevivió con una lesión en la pierna. Si hubo una colisión de UUID, es abrumadoramente más probable que haya sido por un bug de software o una anomalía en la computadora, aunque también podría ser radiación cósmica. Los rayos cósmicos afectan memoria o CPU más seguido de lo que uno cree.
juancn: ¿No será un problema raro de inicialización del generador aleatorio o falta de entropía? Si no lo personalizaron, entonces usan
crypto.getRandomValues(rnds8), ygetRandomValuesno especifica una cantidad mínima de entropía.Geee: Según la interpretación de muchos mundos de la mecánica cuántica, debe existir alguna rama del universo donde todos los UUID sean iguales. Me pregunto qué pensarán las personas de ese mundo.
mittermayr: Estoy totalmente de acuerdo en que no tiene sentido. Aun así, si tuviera que adivinar, la diferencia sería que antes generaban UUIDv4 en el teléfono del usuario y los enviaban a la base de datos, mientras que el UUID que colisionó esta mañana se generó en un servidor Ubuntu.
No sé exactamente cómo se genera UUIDv4 ni si las características de la máquina generadora entran en el algoritmo, pero el único cambio que se me ocurre es que antes se generaban en el dispositivo y desde hace unos meses se generan en el servidor.
Pero en el servidor, especialmente en 2026, eso no debería pasar. Antes hubo problemas con semillas de aleatoriedad en VM, pero ahora debería ser menos común. Incluso si uno de los UUID se generó mal, la probabilidad de que un UUID realmente aleatorio colisione con ese sigue siendo bajísima, así que tendrían que estar mal ambos generadores.
dweez: Es hora de volver a ver este artículo divertido: https://jasonfantl.com/posts/Universal-Unique-IDs/
Si convirtiéramos todo el universo en una computadora gigantesca para generar UUID hasta la muerte térmica, ¿cuántos bits necesitaría el espacio de IDs?
beejiu: Me pregunto si el UUID se genera del lado del cliente o del servidor. Si es del lado del cliente, podría deberse a bots de rastreo. Por ejemplo, Googlebot ejecuta JavaScript con una “aleatoriedad” determinista.
Explicaciones de este tipo son varios órdenes de magnitud más plausibles que una colisión realmente aleatoria.
merlindru: Probablemente es un problema de semilla. Si puedes demostrar que no lo es, tal vez te vuelvas un poco famoso.
erlkonig: Siempre le he dicho a mi equipo que, con suficiente volumen de datos, los valores aleatorios pueden colisionar tarde o temprano, y ahí es cuando ves qué tan robusto es tu software.
Aun así, hay muchos desarrolladores con experiencia, líderes de equipo y CIO que creen que eso es imposible y no escriben nada de código para manejar ese caso. Entonces un mal generador aleatorio puede romper el sistema mucho antes de lo esperado, e incluso provocar corrupción simultánea sin detección ni regeneración. Me suena a la misma clase de gente que no verifica si
malloc()tuvo éxito. A veces les pregunto: “si de verdad es imposible, ¿no estaremos usando demasiados bits?”.leni536: No fue una coincidencia; hay un bug en alguna parte. Viendo por encima, el paquete parece llamar a
crypto.randomUUID()del runtime de JS, y eso siempre debería estar correctamente inicializado.La posibilidad de un bug en el runtime parece bajísima, pero quién sabe. Me da curiosidad qué runtime de JS usan.
jbverschoor: La causa más plausible es que el paquete de generación aleatoria del que depende
uuidhaya sido comprometido recientemente para volver predecibles los números “aleatorios”. Como resultado, muchos proyectos relacionados con criptografía, SSL o monedas podrían haber quedado en riesgo por un ataque a la cadena de suministro.uuid/src/rng.tsy el arreglo aleatorio pasó a serconst. Eso hace que todas las llamadas compartan el mismo arreglo aleatorio.Luego las llamadas posteriores van actualizando el código aleatorio anterior, así que si generaste algo importante, buena suerte. Antes el código hacía una copia nueva con
slice(). Puede haber sido un cambio accidental, pero no entiendo cómo pasó si ni siquiera parece algo que superaría una prueba de generar dos números aleatorios y verificar que sean distintos.pif: Incluso con una fuente de entropía de alta calidad, no puedes convertir un “probablemente” en un “garantizado”. Si necesitas un valor difícil de adivinar, busca criptografía; pero si necesitas unicidad garantizada, tienes que construirla tú mismo.
athrowaway3z: Una regla práctica simple es pensar si puedes meter un timestamp además del valor aleatorio en el ID. Casi siempre la respuesta es sí, y entonces UUIDv7 suele bastar.
Si revisaste el problema tan a fondo que puedes escribir una demostración de que la filtración de información no es aceptable, felicidades: probablemente tu sistema es lo bastante complejo y lento como para usar un hash criptográfico fuerte o, si no quieres complicarte, UUIDv5.
darqis: PostgreSQL 18 soporta uuidv7 de forma nativa, y la configuración por defecto puede ser
uniqueconuuid7().tumdum_: Es un generador seudoaleatorio con mala semilla.
serf: Sería algo así como 1 entre 4.72 × 10²⁸, o sea 1 entre 47.3 octillones. Si de verdad pasó, antes que comprar un boleto de lotería sospecharía de una condición de carrera o de algún otro error simple.
evnix: Incluso dejando de lado toda la matemática de probabilidades, la realidad en la que vivimos es que, aun usando el mejor generador aleatorio por hardware, en la práctica puede haber menos aleatoriedad de la que uno cree.
Donde la seguridad no sea crucial, yo migraría a algo como TSID, o a uuidv7, para que esto en la práctica no ocurra. Me parece mejor que sobrediseñar el código con reintentos.
jordiburgos: Ojalá no usen
b6133fd6-70fe-4fe3-bed6-8ca8fc9386cd. Revisé mi base de datos y ya lo estaba usando.16b55183-1697-496e-bc8a-854eb9aae0f3, y probablemente tenga más. ¿No podríamos verificar duplicados si todos publican aquí su lista?pyuser583: Me da curiosidad cuál es el UUID preferido hoy en día.
smokel: Muchas veces he pasado por culpar al compilador, a rayos cósmicos, a efectos cuánticos o al menos a algún bug oscuro del kernel, para al final darme cuenta de que el bug era mío.
Una colisión en 15,000 registros es demasiado improbable, así que yo sospecharía primero de otras causas: manejo de duplicados, solicitudes reenviadas, objetos reutilizados, logs engañosos, reutilización de identificadores en otra ruta de código, etc. Si compartes un poco más del código alrededor, seguro se puede revisar entre todos.
wazoox: Aún no me ha pasado, pero hace dos días encontré esto en lo profundo de una base de código PHP en producción: una función
createUUID()que recortaba y acomodaba un valor creado conmd5(uniqid('', true))para que pareciera UUID.No sé cómo este horror todavía no nos ha mordido una arteria.
sedatk:
uuidjs/uuidtiene una advertencia de que puede generar UUID duplicados en clientes con generadores aleatorios deterministas como Googlebot.Dice que esto puede ser un problema para aplicaciones que esperan que los UUID generados del lado del cliente siempre sean únicos, así que recomiendan verificar duplicados y fallar con elegancia, o deshabilitar operaciones de escritura para clientes tipo Googlebot: https://github.com/uuidjs/uuid/commit/91805f665c38b691ac2cbd...
xyzzy123: Una vez una prueba de carga larga falló por UUID duplicados en un sistema distribuido basado en Linux.
Después de investigar mucho, resultó ser un bug del kernel, concretamente una condición de carrera. En sistemas multiprocesador, si dos procesos leen
/dev/randomal mismo tiempo, en casos muy raros, del orden de una vez entre un millón, podían recibir los mismos bytes. Yo empezaría revisando la inicialización del generador aleatorio.baq: Parece que la VM en ejecución virtualizó toda la entropía hasta hacerla desaparecer.
glaslong: Hay que comprar unas lámparas de lava.
0xfffafaCrash: Me da curiosidad si el UUID se generó en el frontend o en el backend. Si fue en el frontend, antes que un problema de entropía apostaría a que se manipuló el código del cliente o la solicitud para inyectar un UUID ya conocido.
latentframe: Una de las frases más peligrosas en ingeniería es estadísticamente imposible. A suficiente escala, los casos extremos dejan de ser teoría y se vuelven eventos operativos.
8organicbits: El año pasado escribí sobre una colisión real, incluyendo la biblioteca implicada: https://alexsci.com/blog/uuid-oops/
Para que un UUID tenga resistencia a colisiones, hay muchas restricciones que deben respetarse con rigor, y aquí parece muy probable que el problema esté en el generador aleatorio.
nu11ptr: Al final es un problema de fuente de entropía. Por eso yo siempre genero e inserto dentro de un loop. Si hay colisión, se puede manejar con elegancia.
sbuttgereit: No es “técnicamente imposible”. Es muy técnicamente posible. Con buena aleatoriedad es extremadamente, extremadamente raro, pero no hay nada en UUIDv4 que impida técnicamente producir un valor duplicado.
beardyw: Puede que sea una pregunta tonta, pero ¿no se podría agregar la fecha, aunque sea en segundos hexadecimales? Siento que con apenas unos bytes extra, algo que hoy está bien quedaría garantizado también para el futuro.
mdavid626: También puede haber otras explicaciones. Por ejemplo, que alguien haya manipulado manualmente una solicitud o la base de datos.
radial_symmetry: A mí me pasó una vez algo así y pensé que me estaba volviendo loco, pero leer estos comentarios me tranquiliza.
NKosmatos: No es “técnicamente imposible”. No es imposible; es simplemente extremadamente, extremadamente improbable. Creo que deberías comprar un boleto de lotería.
Cada vez que uso la palabra “improbable”, me acuerdo de https://hitchhikers.fandom.com/wiki/Infinite_Improbability_D...
sudb: Por primera vez siento que elegir CUID2 en uno de mis proyectos sí fue una buena idea de verdad: https://github.com/paralleldrive/cuid2