🔧 AI Nachrichten Major AI platforms go down in unprecedented simultaneous outage(03.09.2026 um 17:34 Uhr)
🔧 AI Nachrichten ChatGPT, Claude, and Grok Down? Users Report Widespread Outages(03.09.2026 um 19:14 Uhr)
🔧 AI Nachrichten OpenAI Launches GPT-6 Astra, Says We May Have Entered the AGI Era(03.09.2026 um 22:08 Uhr)
🔧 AI Nachrichten Claude Comes to CarPlay as Fifth Major AI Chatbot App(05.09.2026 um 05:31 Uhr)
🔧 AI Nachrichten OpenAI’s GPT-6 Astra Is AGI, Says NVIDIA CEO Jensen Huang(07.09.2026 um 06:31 Uhr)
🔧 AI Nachrichten Blame AI companies for Mac mini and Mac Studio shortage(31.08.2026 um 10:32 Uhr)
🔧 AI Nachrichten Major AI platforms go down in unprecedented simultaneous outage(03.09.2026 um 17:34 Uhr)
🔧 AI Nachrichten ChatGPT, Claude, and Grok Down? Users Report Widespread Outages(03.09.2026 um 19:14 Uhr)
🔧 AI Nachrichten OpenAI Launches GPT-6 Astra, Says We May Have Entered the AGI Era(03.09.2026 um 22:08 Uhr)
🔧 AI Nachrichten Claude Comes to CarPlay as Fifth Major AI Chatbot App(05.09.2026 um 05:31 Uhr)
🔧 AI Nachrichten OpenAI’s GPT-6 Astra Is AGI, Says NVIDIA CEO Jensen Huang(07.09.2026 um 06:31 Uhr)
🔧 AI Nachrichten Blame AI companies for Mac mini and Mac Studio shortage(31.08.2026 um 10:32 Uhr)

🔧 Programmierung 🕛 kürzlich 5 Min Lesezeit
0

Implementing UUID v7 by hand: time-sortable primary keys (and the same-millisecond trap)

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Random UUID v4 makes a poor primary key: values land all over the index, so B-tree inserts scatter across pages and page splits go up. UUID v7 puts a millisecond timestamp in the leading bits (standardized in RFC 9562), so generation order equals sort order and you get locality back.



I needed a UUID generator that runs entirely in the browser, and while building it I implemented v7 by hand instead of pulling in a library. Here is the layout, the implementation, the trap I hit, and when each version is actually the right choice.






The v7 layout



The 128 bits break down like this:




CODE
| 48bit unix_ms | 4bit ver(=7) | 12bit rand_a | 2bit variant | 62bit rand_b |






The first 48 bits are the Unix timestamp in milliseconds, big-endian. Then the version (7) and variant bits, and everything else is random. The random part must come from a cryptographic source (crypto.getRandomValues) — not Math.random().






Implementation: putting the bits in the right place



The whole job is "pack the timestamp into the first six bytes, most significant byte first, then overwrite the version and variant nibbles."




CODE
function rnd(n){ const a = new Uint8Array(n); crypto.getRandomValues(a); return a; }
function hex(b){ let s=''; for(const x of b) s += ('0'+x.toString(16)).slice(-2); return s; }

function uuidV7() {
const ts = Date.now(); // 48-bit millisecond timestamp
const b = rnd(16);
// 48-bit big-endian millisecond timestamp (top six bytes)
b[0] = (ts / 0x10000000000) & 0xff; // ts >> 40
b[1] = (ts / 0x100000000) & 0xff; // ts >> 32
b[2] = (ts / 0x1000000) & 0xff; // ts >> 24
b[3] = (ts / 0x10000) & 0xff; // ts >> 16
b[4] = (ts / 0x100) & 0xff; // ts >> 8
b[5] = ts & 0xff;
b[6] = (b[6] & 0x0f) | 0x70; // version 7
b[8] = (b[8] & 0x3f) | 0x80; // variant (10xx)
const h = hex(b);
return `${h.slice(0,8)}-${h.slice(8,12)}-${h.slice(12,16)}-${h.slice(16,20)}-${h.slice(20)}`;
}






The detail worth remembering: shift the digits down with division, not with >>. ts >> 40 does not work, because JavaScript's bitwise operators coerce their operands to 32 bits and a 48-bit value gets mangled. Division followed by & 0xff is safe.



For v4, by the way, don't hand-roll anything if the platform gives you the standard API — it is both faster and safer.




CODE
function uuidV4() {
if (crypto.randomUUID) return crypto.randomUUID(); // one call where it's supported
const b = rnd(16); b[6] = (b[6]&0x0f)|0x40; b[8] = (b[8]&0x3f)|0x80;
const h = hex(b);
return `${h.slice(0,8)}-${h.slice(8,12)}-${h.slice(12,16)}-${h.slice(16,20)}-${h.slice(20)}`;
}









The trap: ordering is not guaranteed within a millisecond



If two IDs are generated in the same millisecond, their leading 48 bits are identical and everything after that is random — so their relative order is arbitrary. If you assume "generation order == strictly ascending", a burst of IDs will quietly break that assumption.



When you do need strict monotonicity, use the 12-bit rand_a field as a counter: increment it within the same millisecond, and reseed it when the millisecond changes.




CODE
let lastMs = 0, seq = 0;
function uuidV7Monotonic() {
const ts = Date.now();
if (ts === lastMs) seq = (seq + 1) & 0x0fff; // same ms: +1 (12 bits)
else { lastMs = ts; seq = rnd(2)[0] & 0x0fff; } // new ms: start from random
// ...store seq in the low 12 bits of b[6..7], overwriting version = 7...
}






If "roughly time-ordered" is good enough for your use case, plain random is fine. The design decision to make up front is simply whether you need strict monotonicity — not which implementation looks cleverer.






Checking it instead of trusting the spec



I measured the following in both Node and the browser rather than assuming the spec held:




  1. Are the version and variant bits actually set? (u[14] === '7', and u[19] is one of 8, 9, a, b.)

  2. Do two IDs generated across a millisecond boundary always sort ascending?

  3. Generate 1000 IDs inside a single millisecond — does the plain implementation break ascending order, and does the counter version hold?



Reproducing the failure yourself, on purpose, is what makes the edge of the trap visible. "The spec says it should" is not the same as having seen it.






v4 vs v7 vs ULID




































Time-sortable Locality Standard In one line
UUID v4 No Low RFC 9562 Fully random. Spreads well, hard on indexes
UUID v7 Yes (ms granularity) High RFC 9562 Good primary key. Drops into an existing UUID column
ULID Yes High De facto 26-char Base32. Worth a look if you don't need UUID compatibility


If you are using UUIDs as primary keys, v7 is worth considering first. In practice the big win is that it fits your existing uuid / UUID column type unchanged — no migration of the column, just of the generator.






The result



I published the generator as a browser tool: it does v4 and v7, with options for count, hyphens, and uppercase. Generation happens in the page and nothing is sent to a server: https://hashitosystem.com/tools/uuidgen/






Wrap-up



v7 is the right fit for "I want roughly time-ordered keys with good locality." The implementation is just splitting the timestamp into bytes with division and overwriting the version/variant nibbles. The one caveat to internalize: ordering within a single millisecond is not guaranteed, so pair it with a counter if you need strict monotonicity. Get that one point right and it is safe to use.







This article is about my own side project. It was written with AI assistance.


Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
3 Quellen
GPT-6 Astra Release Today? OpenAI’s Next Major AI Model Is Almost Here
1 Quelle
Apple accuses OpenAI of destroying evidence as trade-secrets fight intensifies
1 Quelle
Major AI platforms go down in unprecedented simultaneous outage
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Implementing UUID v7 by hand: time-sortable primary keys (and the same-millisecond trap)

Thematisch verwandte Begriffe: Implementing, UUID, hand, timesortable · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...