Tous les articles

L'horodatage des UUIDv7 n'est pas ce que vous croyez

Bases de données · 4 min de lecture ·

On présente souvent UUIDv7 avec trois promesses : des identifiants toujours croissants, un horodatage qui dit quand la ligne a été créée, et des identifiants impossibles à deviner. Dans un billet du 19 octobre 2026, Robert Alexander estime qu'une seule propriété est vraiment tenue, l'unicité, et que la spécification elle-même laisse plier le reste. Si vous utilisez HasUuids dans Laravel, vous utilisez UUIDv7 : autant savoir sur laquelle de ces promesses votre code s'appuie.

La spécification laisse dériver l'horodatage

La RFC 9562 dit que les implémentations peuvent modifier l'horodatage réel, ne garantit rien sur l'écart entre la valeur d'horloge et l'heure réelle, et autorise à l'avancer au-delà de l'heure effective. La documentation de Postgres pour uuid_extract_timestamp dit la même chose : la valeur extraite n'est pas forcément le moment où l'UUID a été généré.

La raison est un compromis entre garder les identifiants dans l'ordre et garder l'heure exacte. Quand l'horloge recule, un générateur qui refuse de revenir en arrière doit continuer avec un horodatage désormais situé dans le futur.

le temps réel passe l'horloge recule horodatage de l'ID en avance
L'ordre est préservé en figeant l'horodatage. Tant qu'il est figé, ce n'est pas l'heure de création.

Python montre jusqu'où cela peut aller

L'exemple d'Alexander utilise uuid.uuid7() de Python 3.14. Il recule l'horloge d'une heure pendant que le script génère des identifiants en boucle. On s'attendrait à un écart de 3 600 secondes entre l'horodatage embarqué et l'horloge, qui se réduirait ensuite. Au-delà d'un identifiant par milliseconde, il grandit au contraire : son exécution affiche environ 3 695, puis 3 795, 3 894 et 3 994 secondes. Le ticket Python correspondant considère cela conforme à la RFC et aux autres implémentations, et l'auteur dit observer le même comportement ailleurs.

Ce que fait le générateur de Laravel

J'ai lu le code présent dans le dossier vendor/ de ce site plutôt que de deviner. HasUuids crée les clés avec Str::uuid7(), qui appelle ramsey/uuid. Son UnixTimeGenerator prend l'heure en millisecondes et garde la dernière heure utilisée dans une propriété statique. Si la nouvelle heure est plus récente, il tire de nouveaux bits aléatoires. Sinon, même milliseconde ou horloge qui a reculé, il garde la dernière heure et incrémente la partie aléatoire. Si cet incrément déborde, il avance l'heure stockée d'une milliseconde.

Concrètement, d'après le code seul : dans un même processus PHP, l'horodatage ne recule jamais, et après un recul d'horloge les nouveaux identifiants portent l'heure précédente, plus tardive, jusqu'à ce que l'horloge rattrape. Cette dernière heure vit en mémoire, pas sur disque : un nouveau processus repart de zéro, et deux serveurs ignorent tout de l'horloge de l'autre. Rien n'y promet un ordre entre processus ou entre machines.

Deux réserves plus petites

L'horodatage fuit. Impossible de partager un UUIDv7 sans partager le moment de sa création, à la milliseconde. Alexander juge cela mineur pour la plupart des applications, mais une URL de profil qui révèle la date d'inscription du compte est un cas où cela compte.

L'ordre est approximatif. Les horloges de machines différentes ne se synchronisent pas à la milliseconde, et une implémentation sans compteur peut produire des identifiants dans le désordre au sein d'une seule boucle. Un ordre approximatif reste utile pour la localité de l'index. Ce n'est pas une séquence.

temps (6 octets, ms) version + aléatoire lisible par qui a l'ID colonne created_at la vraie trace
L'identifiant sert à identifier. L'heure va dans une colonne que vous maîtrisez.

Sur quoi compter dans une app Laravel

Comptez sur l'unicité, et sur un ordre d'insertion approximatif bon pour l'index. N'analysez pas l'identifiant pour savoir quand une ligne a été créée : gardez created_at, et triez dessus quand l'ordre compte pour une personne. N'utilisez pas l'identifiant comme preuve de quel événement est arrivé en premier entre serveurs. Et si l'heure de création est privée, ne mettez pas d'UUIDv7 dans une URL publique. Pour regarder la structure de l'un d'eux, le générateur d'UUID de ce site crée des identifiants v7 dans votre navigateur.

UUIDv7 garantit un identifiant unique. L'horodatage qu'il contient est une indication que le générateur a le droit de tordre : gardez created_at comme trace du moment.

Prochaine étape : cherchez dans votre code ce qui lit l'heure dans un UUID ou trie par identifiant là où une personne attend un ordre chronologique, et passez-le sur created_at. Source : Robert Alexander, UUIDv7 doesn't have the properties you think it does.

Ce que disent les lecteurs

  1. Pas encore de mot. Le premier est toujours le plus courageux.
Vous construisez quelque chose du genre ? Je peux vous aider. Écrivez-moi sur WhatsApp